• [JS30] Hold Shift and Check Checkboxes

    2019. 2. 27.

    by. 나나 (nykim)


    Day 10의 [Hold Shift and Check Checkboxes]입니다. 다중 선택가능한 체크박스를 만드는 내용인데,생각보다 어려웠어요 (´;∧;`)
    더 열심히 해야지 하는 다짐과 함께 글 정리 시작합니다. 하다보면 이것도 익숙해지겠죠! ヽ(〃・ω・)ノ 랄라


    1. 마크업 및 셀렉팅

    우선 마크업부터 진행합니다. 강의에서는 checkbox로 했었는데, 여기선 편의상 button으로 진행합니다.

    <button>11111111111111</button>
    <button>2222222222222</button>
    <button>333333333333333</button>
    <button>444444444444</button>
    <button>555555555555555</button>
    <button>6666666666666</button>
    <button>77777777777777</button>

    이후 스크립트를 작성합시다. 우선 버튼들을 querySelectorAll()로 셀렉팅해서 가져온 뒤, 클릭 이벤트를 걸어줍니다.

    const buttons = document.querySelectorAll("button");
    function handleClick(e){
       this.classList.toggle("on");
    }
    buttons.forEach(button => button.addEventListener("click", handleClick));

    2. 논리 구조 생각하기

    자, 여기까지는 평범한 기능입니다. 클릭하면 on 클래스가 붙고, 다시 클릭하면 on 클래스가 사라지죠.
    이제 Shift 키를 누른 채로 버튼을 클릭하면 그 사이에 있는 모든 버튼에 on 클래스를 붙이려고 합니다. 이건 클릭 이벤트만 잘 작성하면 되는데 여기서부터가 사알짝 고비가 찾아오져 ヽ(°▽、°)ノ
    이럴 땐 당황하지 말고 찬찬히 생각해봅니다.

    1. 우선은 클릭한 버튼을 파악합니다. let lastChecked라는 변수를 만든 다음, 버튼을 클릭할 때마다 let lastChecked = this가 되도록 합니다.

    2. Shift 키를 눌렀는지 파악합니다. e.shiftKey==true인 경우 이벤트가 발생되도록 합니다.

    3. 모든 버튼을 루프합니다. 이때, on 되어있는 버튼을 만나면 "지금부터 만나는 모든 버튼에다 on을 붙여야지~" 가 되도록 설정합니다. 그러다가 마지막으로 클릭했던 버튼(lastChecked)을 만나면 "이제 on클래스 붙이는 거 끝!"이 되게끔합니다.
      이걸 위해 inBetween이라는 변수를 만듭니다. 얘는 평소에 false였다가, 루프를 돌면서 on이 붙은 버튼을 만나면 true가 됩니다. 이 변수가 true인 동안 만나는 모든 버튼은 on클래스가 붙을 예정입니다. lastChecked의 버튼을 만나면 변수는 false가 됩니다.

    ...
    역시 뭔소리인가 싶은데 좀 더 자세히 풀어써보겠습니다.
    우선, 그냥 클릭했을 때 상황입니다.

    1. 버튼2를 그냥 클릭한다

    2. 버튼2에게 on클래스를 붙인다

    3. 버튼2를 lastChecked로 둔다

    이번에는 쉬프트 키를 누른채로 클릭했을 때 상황입니다.

    1. 버튼6을 쉬프트 클릭한다.

    2. 버튼6에게 on클래스를 붙인다

    3. 루프를 돈다

    4. lastChecked(이미 on클래스가 붙은 요소)를 만나면 inBetween을 true로 한다

    5. inBetween이 ture라면 루프를 도는 동안 버튼에게 on을 붙인다

    6. 쉬프트 클릭한 요소(=this)를 만나는 경우 inBetween을 false로 한다

    위 상황은 2(활성화) 상태에서 6을 쉬프트클릭했을 때 3,4,5를 활성화 시키는 로직입니다.
    그럼, 5(활성화) 상태에서 2를 쉬프트클릭한 경우에는 3,4가 활성화가 되어야겠죠? 해당 로직은 아래와 같습니다.

    1. 버튼5를 그냥 클릭한다

    2. 버튼5에게 on클래스를 붙인다

    3. 버튼5를 lastChecked로 둔다

    1. 버튼2를 쉬프트 클릭한다.

    2. 버튼2에게 on클래스를 붙인다

    3. 루프를 돈다

    4. 쉬프트 클릭한 요소(=this)를 만나는 경우 inBetween을 true로 한다

    5. inBetween이 ture라면 루프를 도는 동안 버튼에게 on을 붙인다

    6. lastChecked(이미 on클래스가 붙은 요소)를 만나면 inBetween을 false로 한다

    잘 보면 조금 전과 살짝 다른 부분이 있습니다. 바로 inBetween을 true/false로 만드는 조건입니다. 저걸 달리 표현하면, '쉬프트 클릭한 요소나 lastChecked를 만나면 inBetween을 반대로 한다'가 되지 않을까요?


    3. 코드 작성

    위의 작성한 논리대로 코드를 써봅시다. 우선 함수 밖에 lastChecked 변수를 만들고, 클릭한 요소가 여기에 들어가게 합니다. 또, inBetween은 매 클릭마다 false가 되게끔 선언해줍니다.

    const buttons = document.querySelectorAll("button");
    let lastChecked;
    function handleClick(e){
      let inBetween = false;
      this.classList.toggle("on");
      lastChecked = this;
    }
    buttons.forEach(button => button.addEventListener("click", handleClick));

    다음은 쉬프트키를 누른 채로 클릭했을 때 루프를 돌게 해야합니다. forEach문을 사용합니다.

    const buttons = document.querySelectorAll("button");
    let lastChecked;
    
    function handleClick(e) {
    let inBetween = false;
    this.classList.toggle("on");
    if (e.shiftKey) {
      buttons.forEach(button=>{
        //루프 시작
      })
    }
    lastChecked = this;
    }
    buttons.forEach(button => button.addEventListener("click", handleClick)); 

    그 다음 할 일은, '쉬프트 클릭한 요소나 lastChecked를 만나면 inBetween을 반대로 한다'였죠? 이 말을 코드로 풀어써줍니다.

    ...
    
    if (e.shiftKey) {
    buttons.forEach(button => {
      if (button == lastChecked || button == this) {
        inBetween = !inBetween;
      }
    })
    }
    
    ...

    다음은 'inBetween이 ture라면 루프를 도는 동안 버튼에게 on을 붙인다'를 할 차례입니다.

    ...
    
    if (e.shiftKey) {
    buttons.forEach(button => {
      if (button == lastChecked || button == this) {
        inBetween = !inBetween;
      }
      if (inBetween) {
        list.classList.add("on")
      }
    })
    }
    
    ...

    이제 작성이 끝났습니다^ㅁ^ 코드는 아래와 같아요!

    const buttons = document.querySelectorAll("button");
    let lastChecked;
    
    function handleClick(e) {
    let inBetween = false;
    this.classList.toggle("on");
    if (e.shiftKey) {
      buttons.forEach(button => {
        if (button == lastChecked || button == this) {
          inBetween = !inBetween;
        }
        if (inBetween) {
          button.classList.add("on")
        }
      })
    }
    lastChecked = this;
    }
    buttons.forEach(button => button.addEventListener("click", handleClick)); 


    4. 최종코드

    아래는 wesbos씨가 작성한 최종코드입니다. on클래스가 checked로 바뀌었다고 보면 됩니다 :>

    const checkboxes = document.querySelectorAll(
    '.inbox input[type="checkbox"]'
    );
    let lastChecked;
    function handleCheck(e) {
    let inBetween = false;
    if (e.shiftKey && this.checked) {
      checkboxes.forEach(checkbox => {
        console.log(checkbox);
        if (checkbox === this || checkbox === lastChecked) {
          inBetween = !inBetween;
        }
        if (inBetween) {
          checkbox.checked = true;
        }
      });
    }
    lastChecked = this;
    }
    checkboxes.forEach(checkbox =>
    checkbox.addEventListener("click", handleCheck)
    );


    댓글 0