✏️기록하는 즐거움
article thumbnail
반응형

😎 오늘의 잘한점

액션

DOM 클래스를 제어하는 실습 문제를 풀 때 어떻게 하면 코드량을 줄일 수 있을지 고민하여 작성했다.

칭찬

모든 button을 선택해서 이벤트 리스너를 추가하고, 클래스 이름을 추가하는 부분이 반복되는 것을 깨달았다.

따라서 반복문과 slice를 사용해서 코드를 간략하게 만들었고, 수업 시간에 이를 공유했다😀

단순히 코드를 작성하는 것 뿐만 아니라 메서드나 반복문이 사용되는 원리를 알게되니 응용하기가 수월했다.

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
    <style>
        ul {
            padding: 0;
        }

        .canvas {
            margin: 0 auto;
            width: 400px;
            height: 400px;
            background-color: black;
        }

        .btn-list {
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 10vw;
            margin-top: 30px;
        }

        .canvas.red {
            background-color: red;
        }

        .canvas.yellow {
            background-color: yellow;
        }

        .canvas.green {
            background-color: green;
        }
    </style>
</head>

<body>
    <div class="canvas"></div>
    <ul class="btn-list">
        <li><button type="button" class="btn-red">빨강</button></li>
        <li><button type="button" class="btn-yellow">노랑</button></li>
        <li><button type="button" class="btn-green">초록</button></li>
    </ul>
    <script>
        // 💡 버튼을 클릭했을 때 해당 색상의 배경색을 가지게 하자!
        const box = document.querySelector(".canvas");
        const colorBtns = document.querySelectorAll("button");

        for (let i = 0; i < colorBtn.length; i++) {
            colorBtns[i].addEventListener('click', function () {
                box.classList.toggle(colorBtns[i].className.slice(4));
            })
        }
    </script>
</body>

</html>

✔️ 오늘의 개선점

문제

DOM 클래스 제어 실습에서 예외 상황을 처리하지 못했다.

원인

button의 click 이벤트 리스너에 box.classList.toggle("클래스명")을 하게 되면 다른 button을 클릭했을 때 기존의 class명이 남아있게 되어 css 우선 순위 원칙에 따라 스타일이 적용된다.

따라서 목표했던 동작이 제대로 되지 않는다. 이 때 버튼을 토글로 만들 것이냐 아니냐에 고민을 했었다.

추후 생각해보니 토글 버튼보다는 버튼을 한번 클릭하고, 다시 해당 버튼을 클릭했을 때 그 스타일이 유지되는 것이 사용자에게는 의미상 맞을것 같다는 생각이 들었다.

 

토글 스위치(toggle switch)
토글이란 하나의 설정 값으로부터 다른 값으로 전환하는 것이다.
예를 들어 스위치 ON/OFF처럼 두 가지 상태를 갖고 있어서 ON에서 스위치를 한 번 더 누르면 OFF로,
OFF에서 스위치를 한 번 더 누르면 ON으로 동작하는 스위치이다.

 

액션 플랜

box.className = "canvas";
box.classList.add(colorBtns[i].className.slice(4));

따라서 classList.toggle() 에서 classList.add()로 메서드를 수정했고,

기존의 스타일과 겹치지 않게 하기 위해 box의 className을 초기화해주는 코드인 box.className = "canvas"를 추가하였다.


💡 오늘의 배운점

배움

스프린트 회고

  • 이력서는 연대기식의 이력서가 아닌 하는걸 보여주는 역량순 이력서를 작성하는게 중요하다.
  • 코딩 테스트 공부 노하우 
    ⚪ 흰색 지대 어떻게 풀어야 할지 잘 떠오른다 / 적용하고 있고 무슨 내용인지 알겠다
    🔘 회색 지대 아리까리하다 / 들어봤고 알고는 있는데 내가 잘 쓰지는 못하는 것 같다
    ⚫ 검은색 지대 감도 안 잡힌다
    • 문제를 해결하기 위한 아이디어를 얻어 회색 지대에 해당하는 문제를 풀어나가기.
    • 그러다보면 회색 지대에 있던 문제가 흰색 지대로 넘어가게 되고, 검은색 지대에 있던 문제가 회색 지대로 넘어오게 된다.
    • 단, 회색 지대의 문제를 풀었다고 해서 바로 흰색 지대로 넘기지 말고 동일한 문제를 다른 방식으로 접근해볼 것!
  • 프로젝트의 목표 설정하기
    • 프로젝트는 이 기술을 사용해서 어떤 가치를 만들어 냈는지가 중요하다.
    • 사용자의 문제 해결이라는 목적이 있어야 잘하고 있는지 측정하기가 쉽다.
    • 기술적으로만 몰입하면 오버 엔지니어링이 되면서 어디까지 해야할지 점점 미궁 속으로 빠지게 된다.
    • 따라서 목적을 계속 상기시키면서 사용자에게 좋은 가치를 만들고, 유용한 기능을 만들어보자.

JavaScript DOM

DOM이란?

DOM(Document Object Model)은 HTML 문서를 트리 형태로 구조화하여 프로그래밍 언어가 DOM 구조에 접근할 수 있는 방법을 제공한다.

DOM 구조에 접근할 수 있다는 의미는 DOM element를 변수나 상수에 할당하여 사용할 수 있으면서 문서 구조, 스타일, 내용 등을 변경할 수 있다는 의미이다.

이때 각각의 요소와 속성, 콘텐츠를 표현하는 단위를 '노드(Node)'라고 한다.

 

DOM 트리 접근하기

DOM 트리는 document 객체를 통해 HTML 문서에 접근할 수 있다. MDN에서 설명하고 있는 document는 브라우저가 불러온 웹 페이지를 나타내며, 페이지 콘텐츠(DOM 트리)의 진입점 역할을 수행한다고 한다.

++) ⚠️ getElementById의 경우 특정 요소 노드가 아닌 document에서만 사용할 수 있는 메서드이므로 사용에 주의하자

++) querySelector, querySelectorAll 메서드는 getElement** 메서드보다 다소 느리다고 알려져 있지만, CSS 선택자 문법을 사용하여 구체적인 조건과 일관된 방식으로 요소 노드를 취득할 수 있다는 장점이 있다.

메서드 반환  일치하는 항목이 없을 때 반환 값 매개변수 예시
getElementById(id) 해당하는 id와 일치하는 요소 null 문자열 document.getElementById("myId")
getElementsByClassName(names) 해당하는 class와 일치하는 모든 요소들을 가진 HTMLCollection 빈 HTMLCollection 문자열 document.getElementsByClassName("my-class");
getElementsByTagName(tagName) 해당하는 태그 이름과 일치하는 모든 요소들을 가진 HTMLCollection 빈 HTMLCollection 문자열 document.getElementsByTagName("div")
querySelector(selectors) 해당하는 선택자 또는 선택자 그룹과 일치하는 요소 중 첫 번째 요소 null
/ CSS 선택자 문법에 맞지 않는 경우
DOMException 에러
문자열 (CSS 선택자) document.querySelector("#myId")
querySelectorAll(selectors)
해당하는 선택자 또는 선택자 그룹과 일치하는 모든 요소를 가진 NodeList 빈 NodeList
/ CSS 선택자 문법에 맞지 않는 경우는
DOMException 에러
문자열(CSS 선택자) document.querySelectorAll(".my-class")

 

HTMLCollection과 NodeList

getElements...querySelectorAll 은 조건에 일치하는 모든 요소를 탐색하기 때문에 공통적인 기능을 수행한다.

하지만 결과값을 콘솔창에 출력해보면 getElements...HTMLCollection, querySelectorAllNodeList라는 것을 알 수 있다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>HTMLCollection vs. NodeList</title>
  </head>
  <body>
    <p>p태그1</p>
    <p>p태그2</p>
    <p>p태그3</p>

    <script>
      console.log(document.getElementsByTagName("p")); // HTMLCollection(3) [p, p, p]
      console.log(document.querySelectorAll("p")); // NodeList(3) [p, p, p]
    </script>
  </body>
</html>

공통점

  • HTMLCollection과 NodeList는 모두 유사 배열 객체(array-like object) 형태이다.
  • 내부의 데이터에 접근하기 위해서 배열처럼 원소의 인덱스로 접근한다.

차이점

  • HTMLCollection은 HTML 요소만 포함하지만, NodeList는 모든 유형의 DOM 요소(개행, 주석 등)를 포함한다.
  • NodeList는 forEach 같은 배열 메소드를 사용할 수 있지만, HTMLCollection은 또 다른 기능을 지원한다.
  • HTMLCollection은 실시간으로 업데이트되어 해당 객체의 각 속성에 대한 변경사항이 즉시 반영된다.
    NodeList도 DOM의 변경 사항이 컬렉션에 자동으로 반영되지만, 유비쿼터스 메서드인 document.querySelectorAll()은 변경 사항이 반영되지 않는 정적 NodeList를 반환한다.
<body>
    점심 식단
    <article id="container">
        <ul>
            <li>탕수육</li>
            <li class="item-second">유산슬</li>
            <li>짜장면</li>
        </ul>
    </article>
    저녁 식단
    <article>
        <ul>
            <li class="item-second">된장국</li>
            <li id="item-second">김치찌게</li>
            <li>해장국</li>
        </ul>
    </article>

    <script>
        const container = document.getElementById("container");

        console.log(container.querySelectorAll("li"));
        console.log(container.getElementsByTagName("li"));

        // 요소 추가
        // DOM 요소에 변경사항이 NodeList에 반영되지 않는다.
        const listItem = document.createElement("li");
        container.append(listItem);
    </script>
</body>

CodeSandbox 결과 화면
브라우저 콘솔 결과 화면

기존에 li 요소가 3개 존재했고, createElement() append()를 통해 새로운 li를 추가했을 때 콘솔로 출력된 값을 보면 NodeList에는 새로 추가한 li가 반영이 안됐지만, HTMLCollection에는 반영이 된 것을 확인할 수 있다.

다만, 위 사진은 CodeSandbox, 아래는 브라우저 콘솔창인데 표시하는 것에 약간의 차이가 존재한다.

 

성능면에서 봤을 때 효율적으로 DOM을 접근하는 방법

지금까지는 document 객체를 통해 DOM에 접근하곤 했다.

document로 접근하게 되면 HTML 전체 페이지를 탐색하게 되어 문서의 구조가 깊어짐에 따라 성능이 떨어질 수 있다.

<body>
    <article id="container">
        <ul>
            <li class="item-first">탕수육</li>
            <li class="item-second">유산슬</li>
            <li>짜장면</li>
        </ul>
    </article>
</body>

만약, 문서의 구조가 위와 같다면 document 라는 넓은 범위에서 탐색하는 것보다

container를 탐색하고, container 에서 내부를 탐색하는 식의 방법이 더 권장된다.

const container = document.querySelector("#container");

// const item1 = document.querySelector(".item-first");
// const item2 = document.querySelector(".item-second");
const item1 = container.querySelector(".item-first"); // document가 아닌 범위를 좁혀 탐색!
const item2 = container.querySelector(".item-second");

 

객체의 키 값이 숫자인 경우, 점 표기법 사용이 안되는 경우

  • 점 표기법은 변수 명명 규칙을 지킨 키 값의 경우에만 가능하다.

 

DOM 제어하기

  1. 이벤트 추가하기

target.addEventListener(type, listener)

  • 이벤트의 타입에는 click, mouseover, mouseout, wheel 등 다양한 이벤트를 감지한다.
 

Event reference | MDN

Events are fired to notify code of "interesting changes" that may affect code execution. These can arise from user interactions such as using a mouse or resizing a window, changes in the state of the underlying environment (e.g. low battery or media events

developer.mozilla.org

  • listener 함수의 인수에는 이벤트에 대한 정보가 담겨있다.
  • addEventListener의 두 번째 인수에는 콜백 함수를 전달한다.
  1. 클래스 제어하기
  • classList 객체를 통해 요소의 class 속성을 제어할 수 있다.
  • classList.add() : 클래스를 추가한다. 해당 하는 클래스가 요소에 이미 존재한다면 무시한다.
  • classList.remove() : 클래스를 제거한다.
  • classList.toggle() : 클래스를 토글한다. 해당 클래스가 요소에 없으면 넣어주고 true를 반환하고, 이미 존재한다면 제거하고 false를 반환한다.
  • classList.contains() : 해당하는 클래스가 있는지 확인한다. => 반환 값은 boolean이다.

 

  1. 요소 제어하기
  • document.createElement(target) : target 요소를 생성한다.
  • document.createTextNode(target) : target 텍스트를 생성한다.
  • element.appendChild(target) : target 요소를 element의 자식으로 위치시킨다.
  • element.removeChild(target) : element의 자식 요소인 target을 제거한다.
    • target이 element의 자식이 아닌 경우 NotFoundError(DOMException)이 발생한다.
    • targetnull인 경우 TypeError가 발생한다.
  • element.append(target) : target 요소를 element의 자식으로 위치시킨다.

 

appendChild() 와 append()의 차이

Node.appendChild() Element.append()
Node 객체만 자식 노드로 위치시킬 수 있다. Node 객체 뿐만 아니라 문자열 객체도 자식 노드로 위치시킬 수 있다.
추가한 Node 객체를 반환한다. 반환 값이 없다.
하나의 노드만 추가할 수 있다. 여러 노드와 문자열을 추가할 수 있다.

 

의미

평상시에 DOM에 대해서 많이 들어는 봤지만 정확한 개념이 확립되지는 않았다.

이번에 수업을 들으면서 DOM이란 문서 객체 모델로, 말 그대로 문서를 객체 모델화한 것이라고 이해했다.

따라서 DOM 요소에 접근하여 제어 할 수 있다는 것이 포인트라고 생각하며 아직은 이 정도까지만 이해하기로 했다.

DOM 메서드명이 직관적이라 가볍게 다루는 것에는 크게 어려움이 없지만 깊게 들어갈수록 헷갈리는 부분이 존재했다.

특히나 NodeList가 라이브 객체일 때와 정적 객체일 때의 동작이 헷갈렸다.

그렇지만 실습 과정을 통해 완벽히는 아니더라도 80%는 이해했지 않았나...!!!

앞으로 계속해서 연습하다보면 수월하게 사용할 수 있을 것이라 생각한다 ✊✊


🙋‍♀️ 오늘의 질문

querySelectorAll()로 반환되는 NodeList는 정적이라고 하였는데 왜 textContext 메서드로 DOM을 변경했을 때는 실시간으로 반영이 되는 것일까?
<body>
    <p>깐풍기</p>

    <script>
        const p = document.querySelectorAll("p")

        console.log(p); // "탕수육"으로 변경된 DOM 요소가 탐색된다.

        p[0].textContent = "탕수육";
    </script>
</body>

👉 새롭게 추가되는 노드나 삭제되는 노드의 업데이트는 되지 않지만, 이미 존재를 알고 있는 노드의 컨텐츠에 대한 변경사항은 업데이트가 된다.

반응형
profile

✏️기록하는 즐거움

@nor_coding

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!