[일일코딩 #32] Two Sum

[일일코딩 #32] Two Sum

Question

링크
Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example:
Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

My Answer

var twoSum = function(nums, target) {
  for (var i = 0; i <nums.length; i++) {
    var lastIdx = nums.lastIndexOf(target - nums[i]);
    if(lastIdx > 0) return new Array(i, lastIdx);
  }
};

lastIndexOf로 풀었다. 처음엔 Array 초기화 할 때 var answerArr = []로 해서 answerArr.push(a, b)로 넣었는데 코드 줄인다고 위처럼 바꿨더니 성능이 조금 더 줄었다. 변수 할당보다 new Array 로 하는게 더 오래걸리나보다.

Other’s answer

3가지 방법을 제시함.
1. 브루트 포스: 포문 2번 돌면서 뺀 값이 있나 찾기
2. Two pass hash table: 잘 이해 안감 왜 굳이?
3. One-pass hash table: 내가 푼 방법. 뺀 값이 map에 있나 확인

Advertisements

[일일코딩 #31] 팩토리얼의 0꼬리 구하기

Daily Codewars #31 Factorial Tail

Question

자연수 a의 팩토리얼 수를 b진수로 고쳤을 때 뒤에 0이 얼마나 붙는지 구하는 질문이다. 4kyu짜리 문제.

codewars link

How many zeroes are at the end of the factorial of 10? 10! = 3628800, i.e. there are 2 zeroes. 16! in hexadecimal would be 0x130777758000, which has 3 zeroes.

Unfortunately, machine integer numbers has not enough precision for larger values. Floating point numbers drop the tail we need. We can fall back to arbitrary-precision ones – built-ins or from a library, but calculating the full product isn’t an efficient way to find just the tail of a factorial. Calculating 100’000! in compiled language takes around 10 seconds. 1’000’000! would be around 10 minutes, even using efficient Karatsuba algorithm

is to write a function, which will find the number of zeroes at the end of (number) factorial in arbitrary radix = base for larger numbers.

  • base is an integer from 2 to 256
  • number is an integer from 1 to 1’000’000

비하인드 스토리

점심 먹고 잠도 깰 겸 코드워즈를 틀었다. 처음엔 별 고민 없이 숫자.toString(몇진수)가 큰 수에서 overflow나길래 toString 없이 N진수로 고치는 코드를 짰다. 근데 그런 문제가 아니었음. 50 팩토리얼만 해도 콤퓨타에서 저장할 수 없으니… (팩토리얼로 변환가능해야 하는 범위는 100만까지다).
회사 옆자리 동료분께 “50!를 2진수로 바꿨을 때 뒤에 0이 얼마나 붙어요?” 여쭤보니 그건 수학 문제에 더 가깝고, ACM(대학생 프로그래밍 경시대회) 1번 문제 난이도라고 하였다(다른 분께선 그건 아니고 예선문제 정도 레베루라고…).

내가 계속 실마리를 못 잡으니 힌트를 주셨다.

h: 0의 의미를 생각해보세요
나: …
h: 50!를 10진수로 바꿨을 때 뒤에 0이 붙는 경우를 생각해보세요

My Answer

정말 실마리는 0의 의미에 있었다.
팩토리얼을 풀어해쳤을 때, 8!8*7*6*5*4*3*2*1 이다. 10진수가 0으로 끝나려면 2A * 5B10C가 저 안에 있으면 되지 않을까?
그럼 n진수 뒤의 0은 number(팩토리얼 당하는 수)를 풀어해친 수 속에 얼마나 base(진수)가 들어있는지 찾으면 되는구나.

처음엔 number를 무작정 base로 나눠갔다. 당연히 또 overflow가 났다. 나머지를 계속 곱하면서 저장하니 당연히…

function zeroes (base, number) {
  var answer = 0;
  var remain = 1;
  for (var i = number; i > 0; i--) {
    var newI = i;
    newI *= remain; // 나누고 난 나머지는 계속 곱해둠 -> overflow 원흉
    if(newI%base === 0) { // 나눠 떨어지면 answer를 증가시킴
      answer++;
      newI = Math.floor(newI/base);
    }
    remain = newI;
  }
  return answer;
}

그 다음은 basenumber 모두 소인수분해를 해서 이를 비교해보았다. 소인수분해 함수를 만들어서 돌려 썼다.

function zeroes (base, number) {
  function makePrime(num, obj) { // 소인수분해 하는 함수
    for (var i = 2; i <= num; i++) {
      var quantity = 0;
      while(num%i===0) {
        quantity++;
        num = Math.floor(num/i);
      }
      if(quantity>0){
        var originVal = obj[i];
        obj[i] = originVal? originVal + quantity : quantity;
      }
    }
    return obj;
  }

  var basePrime = makePrime(base, {});
  var numberPrime = {};
  for (var i = number; i >= 0; i--) { // 팩토리얼 for문 돌며 공용 object에 인수를 집어넣는다
    makePrime(i, numberPrime);
  }

  var answer = 0;
  var answerList = [];
  var baseLength = Object.keys(basePrime).length;
  for (var i = 0; i < baseLength; i++) { // 소인수분해한 두 object 비교
    var oName = Object.keys(basePrime)[i];
    if(!numberPrime[oName]) {
      answer = 0;
      break;
    }
    var result = Math.floor(numberPrime[oName] / basePrime[oName])
    answerList.push(result)
  }
  return Math.min.apply(null, answerList);
}

이 함수의 문제점은 zeroes(2, 524288)정도로 팩토리얼 할 수가 커지면 연산이 터지는 것이었다.
zeroes(2, 100000)크기의 연산도 4.8s나 걸린다.
자잘한 성능 최적화로는 안되고 연산 자체를 토막내야 되겠네…

더 생각해보니 number의 소인수를 굳이 모두 구할 필요 없이 base의 소인수로만 연산하면 되는거였다. base는 범위도 256까지밖에 안 된다. (e.g. base 소인수가 {2:2, 3:5}라면 number에서 2와 3의 갯수만 구하기)

function zeroes (base, number) {
  function makePrime(num, obj) { // base용 소인수분해
    for (var i = 2; i <= num; i++) {
      var quantity = 0;
      while(num%i===0) {
        quantity++;
        num = Math.floor(num/i);
      }
      if(quantity>0){
        var originVal = obj[i];
        obj[i] = quantity;
      }
    }
    return obj;
  }

  function makePrimeArray(num, obj, arr) { // number용 소인수분해: base의 키값만 돌며 인수를 구한다
    var length = arr.length;
    for (var i = 0; i < length; i++) {
      var arrKey = arr[i], quantity = 0;
      while(num>0 && num%arrKey===0) {
        quantity++;
        num = Math.floor(num/arrKey);
      }
      if(quantity>0){
        var originVal = obj[arrKey];
        obj[arrKey] = originVal? originVal + quantity : quantity;
      }
    }
    return obj;
  }

  var basePrime = makePrime(base, {}),
      numberPrime = {},
      basePrimeArr = Object.keys(basePrime),
      answer = Infinity,
      answerList = [],
      baseLength = Object.keys(basePrime).length;

  for (var i = number; i >= 0; i--) {
    makePrimeArray(i, numberPrime, basePrimeArr);
  }
  for (var prop in basePrime) {
    answer = Math.min(answer, Math.floor(numberPrime[prop] / basePrime[prop])) | 0;
  }
  return answer
}

그래서 base용, number용 소인수 구하는 함수를 나눴다.
굳이 하면 합칠 수 있겠는데… 이 때는 쫄보라서 최대한 속도 높이려고 함수 분리했음 흐훅

이렇게 바꿨더니 기존에 연산 죽던 524288이 0.2s만에 계산되더라. 오예!

pompeu2004’s Solution

이럴 줄 알았어… 남들은 엄청 짧은 코드로 풀 줄…

function zeroes (base, number) {
  var factors = {}, i = 1;
  while(++i <= base) while(base%i == 0) {
    base /= i; 
    factors[i] = (factors[i]||0) + 1;
  }
  return Math.min(...Object.keys(factors).map(factor => {
    var count = 0, i = 1;
    while((i *= factor) <= number) count += number/i>>0;
    return count/factors[factor]>>0;
  }));
}

결론

멍충 인증

초심자를 위한 Github 협업 튜토리얼 (with 토끼와 거북이)

대상

본 자료는 간단한 git-flow로 Github에서 협업을 하는 과정을 설명한 글입니다.
초심자를 대상으로 하며, SourceTree를 사용합니다.

익힐 수 있는 개념

commit, push, branch, merge, rebase, pull request, release, fork


PART 1. 단일 저장소에서 협업하기

토끼와 거북이- image.003

개발자 토끼와 거북이가 달리기 대회 사이트 ‘달사’를 만들려고 합니다
토끼는 메인 페이지를, 거북이는 대회 신청 페이지를 만들기로 했습니다.

그런데 각자 코딩을 하면, 이를 어떻게 합쳐서 하나의 ‘달사’로 만들까요?
토끼와 거북이는 이에 대한 수단으로 git을 사용하기로 했습니다.

토끼와 거북이- image.004

토끼는 https://github.com/ 에 가입해서  ‘dalsa’라는 이름의 새로운 저장소를 만들었습니다.
Public으로 만들어 누구나 이 프로젝트에 참여할 수 있게 할 거예요.
* Organization 을 만들어 단체의 이름으로 저장소를 만들 수도 있습니다

토끼와 거북이- image.005

원격(Github 사이트)에 dalsa 저장소가 생겼습니다.
이젠 여기에 코딩을 하기 위해 로컬(내 컴퓨터)에 저장소를 다운받아야 합니다.

토끼와 거북이- image.006

토끼는 먼저 git을 설치하고, (https://git-scm.com/book/ko/v2/시작하기-Git-설치) SourceTree의 ‘+New Repository > Clone from URL’을 사용해서 저장소를 받아옵니다.
SourceTree, Github Desktop같은 GUI 프로그램은 시각적으로 상태를 확인할 수 있다는 장점이 있지만 CLI의 모든 기능을 지원하지 않습니다.
개인이 이해하기 쉬운 것 먼저 배우셔도 되지만, CLI로 하는 법은 처음이든 나중이든 필수로 익히셔야 합니다.
CLI로 git을 설치하고 사용하는 방법은 git 간편 안내서 를 참고해주세요.

토끼와 거북이- image.007

토끼는 제일 먼저 저장소에 대한 설명인 README.md 파일을 만들었어요.
이를 원격에 올릴거예요.

깃에 커밋을 하는 올릴 파일은 3가지 상태가 있습니다.
이를 ‘포장하는 과정’이라고 생각해 볼게요,

  1. 포장 전인 파일들: Unstaged Files
  2. 포장하기로 한 파일들: Staged Files
  3. 포장 완료 파일 묶음: Commit

토끼와 거북이- image.008

README파일을 체크해서 ‘이 파일을 포장할거다’라는 Staged단계로 올립니다.
지금은 한 파일밖에 없지만, 나중에 변경 내역이 여러 파일에 있으면 내가 원하는 것들만 선택해서 staged로 올릴 수 있겠죠?
이는 ‘README 파일 추가’란 이름을 붙여서(commit message) 선물로 포장할거예요(commit).

토끼와 거북이- image.009토끼와 거북이- image.010

이렇게 코드를 의미있는 단위로 쪼개서 포장하는 것이 Commit 입니다.
각 커밋별로 변경된 이력을 편하게 볼 수 있고, 예전에 올렸던 커밋으로 코드를 돌려 시간여행을 할 수 있습니다.
오프라인 상태에서도 가능해서 비행기나 산 꼭대기에서도 커밋할 수 있죠(?)

토끼와 거북이- image.011토끼와 거북이- image.012

이렇게 커밋만 하면 내 컴퓨터에만 저장되어 있고 Github사이트엔 아직 올라가지 않은 상태입니다.
이 커밋들을 진짜 올리기 위해 Push를 합니다.
Push를 누르면 git이 우리 컴퓨터 폴더에 숨겨져 있는 .git파일을 참조해 그 곳에 저장된 달사 저장소 url에 코드를 올려줍니다.

토끼와 거북이- image.013

Github 저장소에 가 보면 잘 올라와 있는 것을 확인할 수 있습니다.

이렇게 바로 올린 코드는 Master 브랜치(기본적으로 생김)에 올라갔습니다.
브랜치는 단어 그대로 가지를 뻗어 나가는 것입니다.
토끼와 거북이- image.015

토끼와 거북이는 Master 브랜치엔 완벽한 코드만(언제든지 배포할 수 있는) 두자는 약속을 했습니다.
그래서 ‘개발 중’인 코드를 올리기 위한 dev브랜치를 새로 만들었습니다.

토끼와 거북이- image.014

특정한 코드 상태에서 브랜치를 따면 새로운 브랜치에서 독립적으로 작업하고 나중에 합칠 수 있습니다.
위의 예시에서 브랜치 없이 둘이 한 종이에 작업했다면 서로 같은 곳을 수정했을 때 (잡은 손 고치기) 에러가 났겠죠.
브랜치로 작업하면 추후에 합칠 때 원하는 손 그림을 택하면 됩니다.

토끼와 거북이- image.016

둘은 짝 코딩으로 사이트 뼈대를 만들어 dev 브랜치에 커밋 후 푸시했습니다.
좌측 메뉴의 branch에서 dev, master를 더블 클릭해서 각 브랜치를 옮겨다닐(checkout) 수 있습니다.

토끼와 거북이- image.017

초기 세팅을 마친 거북이와 토끼는 서로 나뉘어서 코딩을 하기로 했습니다.

토끼는 메인 페이지를 만들기 위해 ‘feature/main’이란 이름으로, 거북이는 달리기 신청 폼을 만들기 위해 ‘feature/form’이란 이름으로 dev브랜치의 최신 커밋으로부터 새로운 브랜치를 만들었습니다.
브랜치 앞에 ‘feature/‘처럼 슬래시로 구분된 이름을 달아주면 이 구분별로 브랜치를 묶어볼 수 있는 장점이 있습니다.

토끼와 거북이- image.018

토끼와 거북이가 각자 브랜치에서 코딩을 합니다.
시간이 흘러 토끼가 main 페이지를 다 만들어 dev브랜치에 ‘feature/main’브랜치를 합치려 합니다.
하지만 예고 없이 바로 합치기보다는 거북이에게 리뷰를 먼저 받고 합치고 싶습니다.

그러기 위해 하는 것이 Pull Request입니다.

토끼와 거북이- image.019

Github저장소에 있는 ‘Pull Request’버튼을 눌러
‘feature/main’에서 ‘dev’브랜치로 Pull Request(땡김 요청)를 보냅니다.

토끼와 거북이- image.020

그럼 저장소의 ‘Pull requests’섹션에서 토끼가 보낸 풀리퀘를 확인할 수 있습니다.
Files changed를 보니 잘 만들었네요 (맘에 안 들면 거부할 수 있습니다).
하단의 ‘Merge pull request’를 눌러 토끼의 커밋들을 dev브랜치에 합칩니다.

토끼와 거북이- image.021

그 와중에 거북이도 폼 제작을 마쳤습니다.
dev 브랜치(c)에 Pull Request를 보내려 보니 토끼의 새로운 코드가 추가되어 거북이가 처음에 땄던 dev 커밋(a)과 상태가 달라졌군요.
a와 b는 바로 합칠 수 있는데 b와 c를 섣불리 Merge 했다가는 서로의 코드가 충돌날 수 있겠는데요?
만약 거북이와 토끼가 같은 코드를 다르게 고쳤다면 에러가 나겠죠.

토끼와 거북이- image.022

사려깊은 거북이는 ‘선-머지 후-풀리퀘’를 보내기로 합니다.
dev의 최신 커밋(c)에서 마우스 오른쪽 버튼을 눌러 Merge를 합니다.
그랬더니 Merge Conflicts가 나네요.

토끼와 거북이- image.023

토끼와 거북이 둘 다 타이틀을 고쳤네요.
코드를 보니 거북이의 코드가 맞는 버전입니다. 회의에서 다정 버전으로 하자고 결론이 났거든요.
수동으로 토끼의 코드를 지우고 거북이의 코드를 남겨 commit+push합니다.

토끼와 거북이- image.024

거북이도 dev로 Pull request를 보냅니다.
토끼가 확인하고 merge하니 오류 없이 잘 됩니다.
거북이의 ‘feature/form’에는 dev브랜치의 코드도 잘 섞여있는 버전이 들어가 있었기 때문이죠.
토끼와 거북이- image.025

이제 dev에 토끼의 ‘메인 페이지’와 거북이의 ‘신청 폼’이 모두 반영된 깔끔한
코드가 올라왔습니다.
이 정도면 베타 버전으로 출시해도 되겠어요!

배포를 위해 dev에서 master 브랜치로 pull request를 보내고 머지를 합니다.
출시 가능한 버전이니까 master에 두면 되겠지요?
토끼와 거북이- image.026

그럼 한번 출시를 해볼까요?

저장소에서 ‘releases’를 누르고, 이번 업데이트의 버전과 세부 내용을 적어준 뒤 Publish Release 버튼을 누릅니다. (일반적인 버전 관리 규칙)

토끼와 거북이- image.027

첫 번째 릴리즈가 완성되었습니다!

우리 서비스가 업데이트 되는 기록을 남길 수 있고,  그 당시의 소스코드를 다운받을 수도 있습니다. 만약 이번 업데이트에 치명적인 버그가 있다면 지난 버전으로 빠르게 롤백할 수도 있습니다.

토끼와 거북이- image.028

React처럼 잘 관리되는 오픈소스의 저장소 release 노트를 보면
그 소스가 어떻게 관리되고 있는지 깔끔하게 확인할 수 있겠죠 🙂

토끼와 거북이- image.029

이렇게 커밋을 하고 git에 올렸을 때의 또 하나의 장점은 언제든지 어떤 상태로든 시간여행을 할 수 있다는 것입니다.

‘feature/main’브랜치에서 메인 페이지를 만들다가 ‘feature/comment’브랜치로 넘어가서 댓글 기능을 개발할 수도 있고, 거북이가 개발중인 ‘feature/request’로 넘어가서 거북이의 코드를 돌려볼 수도 있고, 내가 코딩을 이상하게 했을 때 내 브랜치의 기존 버전으로 reset할 수도 있습니다.


PART 2. Fork해와서 협업하기

토끼와 거북이- image.030

토끼와 거북이가 만든 ‘달사’가 흥했습니다. 너굴맨이 사이트를 써 보니 ‘댓글’기능이 있으면 좋을 것 같다는 생각이 들었습니다. 달사는 오픈소스니까 너굴맨이 기능을 추가해서 기여(contribution) 할 수 있죠.

달사 github repository페이지에서 Fork버튼을 누릅니다. 그러면 너굴맨의 계정으로 리포지토리가 복사됩니다.

토끼와 거북이- image.031

토끼와 거북이가 했던 방법처럼 원격 repo를 로컬에 받아오고, 댓글 기능을 커밋합니다.
Master 말고 Dev의 최신 커밋에서부터 작업해야 나중에 Pull Request를 Dev로 보낼 수 있겠죠? 토끼와 거북이의 Master에는 배포 가능한 코드만 들어가야 한다는 규칙이 있으니까요.

토끼와 거북이- image.032

믿음직스러운 너굴맨은 Pull request를 보내기 전에 토끼와 거북이의 원본 저장소에 변경 사항이 없나 확인합니다. 여기는 너굴맨이 fork뜬 저장소이기에 원본 저장소의 최신 커밋들을 볼 수 없습니다.

너굴맨은 Remotes > New Remote로 원본 저장소의 url을 ‘upstream’이라는 이름으로 추가해줍니다.

토끼와 거북이- image.033

그 와중에 토끼와 거북이가 열일해서 너굴맨이 딴 c커밋에서부터 검색 기능(a커밋)을 dev브랜치에 새로 추가했네요. 그냥 Pull request를 보내면 충돌이 일어날 수도 있겠습니다.
여기서 선머지 후풀리퀘스트를 할 수도 있겠지만, 깔끔한 코드 히스토리를 남기고 싶은 너굴맨은 ‘Rebase‘를 하기로 합니다.

토끼와 거북이- image.034

Rebase는 단어 그대로, 다시 베이스를 정하는 것입니다. 너굴맨의 코드는 C커밋에서 따서 현재 a커밋과 머지했을 때 충돌이 일어날 수 있습니다. 하지만 a커밋에서 브랜치를 땄다면 충돌 없이 한 번에 머지를 할 수 있겠죠? 여기서 a커밋에서 브랜치를 딴 것처럼 히스토리를 조작하는 것이 Rebase입니다.

토끼와 거북이- image.035

새로 베이스로 삼고 싶은 a커밋에서 ‘Rebase’를 눌러줍니다.
(충돌이 난다면 고쳐줍니다. Merge할 때 충돌 해결하는것과 동일하게 해 주시면 됩니다. 충돌 해결을 하면 다시 commit을 눌러주세요.)

그러면 내 컴퓨터에는 b커밋이 c에서 딴 게 아닌 a에서 땄던 것처럼 히스토리가 조작됩니다. 근데 이를 원격 저장소에도 push하려면, 그냥 push로는 에러가 납니다. 히스토리를 망가뜨리는 위험한 push니까요. 그래서 콘솔 창에서 ‘git push -f origin’이라는 명령어로 force push를 해 줍니다(소스트리에는 force push 기능이 없습니다).

이는 다른 사람이 이 브랜치를 수정하고 있을 가능성이 제로일 때만 해야합니다.

토끼와 거북이- image.036

fetch를 한 번 눌러 원격과 내 소스트리를 동기화 해줍니다.
베이스가 옮겨졌네요! 기존에 거북이가 머지를 했을 때는 ‘Merge pull request 어쩌고’ 이런 머지 커밋이 안 예쁘게 남았는데, 이제는 바로 깨끗하게 머지를 할 수 있는 상태가 되었습니다.

토끼와 거북이- image.037

포크된 내 리포지토리에서 pull request버튼을 누릅니다.
base fork를 토끼와 거북이의 dev브랜치, head fork를 너굴맨의 master브랜치로 해 줍니다.

토끼와 거북이- image.038

토끼가 확인했네요! 맘에 들었나 봅니다. 너굴맨의 코드를 머지했습니다.

토끼와 거북이- image.039

이제 너굴맨은 달사의 contributer로 등록되고, 너굴맨의 개인 페이지에도 달사 저장소가 뜨게 됩니다. 너굴맨 달사 커미터 축하!

토끼와 거북이- image.040

 

이렇게 토끼와 거북이가 단일 리포지토리에서 협업하는 것, 너굴맨이 오픈소스에 기여하는 것을 ‘달사’ 서비스 만들기를 예를 들어서 설명하였습니다.

개선점이나 피드백 해주시면 감사하겠습니다 🙂
모쪼록 처음 Github으로 협업을 하시는 분들께 도움이 되었으면 합니다.

 

토끼&거북이의 저장소: https://github.com/milooy/dalsa
너굴맨의 저장소: https://github.com/CODEINAE/dalsa

body overflow:hidden 이 모바일에서 안 먹을때 javascript로 해결방법

스크린샷 2017-05-09 오후 9.21.17

상황: 모바일에서 사이드 네비게이션이 열린 상태에서 스크롤시 body는 스크롤 되지 않게 하고싶다

Solution A: CSS

html,
body {
    overflow: hidden;
    position: relative;
    height: 100%;
}

PC에선 문제 없이 작동한다. 하지만 iOS 사파리랑 크롬에서는 무용지물.

Solution B: CSS – fix

body {
    position: fixed;
    width: 100%;
    height: 100%;
}

PC, 모바일 모두 되긴 하는데 Side nav를 닫은 후에는 position이 꼬임 -> DOM 클릭해도 이벤트가 활성화 안된다.

Solution C: jQuery

<body>
<div id="side-nav">사이드 네비게이션</div>
<div class="contents">여기에 body의 내용 넣는다</div>
</body>
$(function(){
    $('#side-nav').on('show.bs.offcanvas', function (e) {
      $('#sidenav-overlay').addClass('active');

      /* Disable scroll */
      $('.contents').on('scroll touchmove mousewheel', function(e){
        e.preventDefault();
        e.stopPropagation();
        return false;
      });
    });
    $('#sidenav-overlay').click(function() {
      $('#side-nav').offcanvas('hide');
      $('#sidenav-overlay').removeClass('active');

      /* Enable scroll */
      $('.contents').off('scroll touchmove mousewheel');
    });
  });

jQuery on handler로 스크롤을 막는다.
HTML에서 사이드 네비게이션과 Contents 영역을 분간해두면
사이드 네비게이션 자체에서는 스크롤 가능하게 할 수 있다.

Refer

http://stackoverflow.com/questions/3656592/how-to-programmatically-disable-page-scrolling-with-jquery

[Javascript]원하는 HTML 영역 프린트하기

스크린샷 2017-03-28 오전 10.48.19스크린샷 2017-03-28 오전 10.49.44

상황: 페이지의 일부만 프린트하고 싶다!

구글링 하면 2가지 방법이 나온다.
근데 둘다 내 상황에는 문제점이 있었다.

  1. 팝업으로 DOM을 복사해 프린트: 편하긴 한데 css, js가 떨어짐
  2. 전체 돔, 프린트 영역 원하는 돔 따로 저장한다음에 프린트영역만 살리고 프린트 후 전체 돔 살림: 편하긴 한데 js 이벤트 바인딩이 떨어짐

아래와 같이 display noneblock 을 toggle 해주는걸로 간단히 해결 가능하다.
React 사용중이라 jquery 말고 pure javascript로 코딩했다.

onPrint() {
const html = document.querySelector('html');
const printContents = document.querySelector('.modal-body').innerHTML;
const printDiv = document.createElement(&quot;DIV&quot;);
printDiv.className = &quot;print-div&quot;;

html.appendChild(printDiv);
printDiv.innerHTML = printContents;
document.body.style.display = 'none';
window.print();
document.body.style.display = 'block';
printDiv.style.display = 'none';
}

[CSS] div에 의도하지 않은 여백이 들어가 있을 때

Problem

btn-container라는 div에 btn-group 3개 div를 넣었더니 그 3개 div 사이에 틈이 생겼다.

%e1%84%89%e1%85%b3%e1%84%8f%e1%85%b3%e1%84%85%e1%85%b5%e1%86%ab%e1%84%89%e1%85%a3%e1%86%ba-2016-11-14-%e1%84%8b%e1%85%a9%e1%84%92%e1%85%ae-2-09-25

Solution

  1. whitespace를 주석처리
<div>text</div>
<!-- -->
<div>text</div>
<!-- -->
<div>text</div>
<!-- -->
<div>text</div>
<!-- -->
<div>text</div>
`
  1. 걍 다 붙여버리기.
    django에 {% spaceless %}태그 써도 됨.
<div>text</div>
<div>text</div>
<div>text</div>
<div>text</div>
<div>text</div>
  1. 잔망스런 엔터
<!-- 1 -->
<div>

text
<div>

text
<div>

text
<div>

text
<div>text</div>
<!-- 2 -->
<div>text</div>
<div>text</div>
<div>text</div>
<div>text</div>
<div>text</div>
  1. 부모 폰트사이즈 0, 자식에서 다시 키우기
#parent {
font-size: 0;
}

#child {
font-size: 16px;
}
  1. parent를 display: flex 설정하기
.parent {
display: flex;
}
.parent > div {
display: inline-block;
padding: 1em;
border: 2px solid #f00;
}

Refer

http://stackoverflow.com/questions/19038799/why-is-there-an-unexplainable-gap-between-these-inline-block-div-elements/19038875#19038875
(아웃사이더님이 알려주심 감사합니다)