본문 바로가기
TIL

2024.04.01 29일차 Javascript DOM, 이벤트

by Song.dev 2024. 4. 1.

 

DOM 조작 및 탐색

프로퍼티 설명
childNodes 자식노드를 모두 탐색하여 NodeList에 담아 반환. 요소 노드 + 텍스트 노드
children 자식 노드 중 요소 노드만 모두 탐색하여
HTMLColleciton객체에 담아 반환 (텍스트 노드X)
firstChild 첫 번째 자식 노드를 반환
lastChild 마지막 자식 노드를 반환
firstElementChild 첫 번째 자식 요소 노드를 반환
lastElementChild 마지막 자식 요소 노드를 반환
hasChildNodes() 자식 요소를 가지고 있는지 확인
parentNode 부모노드를 모두 탐색하여 NodeList로 반환
previousSibling 같은 부모를 가진 형제 노드 중에서 자신의 이전 형제 노드를 반환
nextSibling 같은 부모를 가진 형제 노드 중에서 자신의 다음 형제 노드를 반환
previousElementSibling 같은 부모를 가진 형제 노드 중에서 자신의 이전 형제 요소 노드를 반환
nextElementSibling 같은 부모를 가진 형제 노드 중에서 자신의 다음 형제 요소 노드를 반환

 

ChildNodes  vs  Children

  • childNodes는 줄을 맞추면 7개(공백 기준), 한 줄로 나열하면 3개로 달라지므로 예측하기 불편
  • children도 arraylike 이기 때문에 배열로 가져오면 배열고차함수도 쓸 수 있어서 더 편리

 

 

firstElementChild  vs  firstChild

  • firstChild \n 공백 출력
         <ul>\n<li></li>....</ul>  \n 부터 출력된 것
  • firstElementChild li.fr.apple 출력
  • Element 가 있는 프로퍼티를 사용해야 의도대로 요소를 가져올 수 있음

 

hasChildNodes()

  • div.box 에 접근해서 hasChildeNodes() 적용해보면
  • 자식 요소가 없으니 false를 예상하기 쉬우나 공백도 자식으로 판정해서 true

 

자식요소 존재 판별 구현

function hasChildren($tag) {
      // return $tag.children.length > 0;
      return !!$tag.children.length;  // 0을 논리로 바꾸면 false
}

 

 

parentElement, parentNode

  • parentElement, parentNode 둘다 부모 요소 잘 찾음

 ex)  ul#fruits 의 부모 요소 div.wrap 

  1. parentElement: div.wrap
  2. parentNode: div.wrap

 

DOM 제어와 디스트럭쳐링

const [$apple, $banana, $grape]
      = [...$wrap.firstElementChild.children];
      // $wrap 은 div, div의 첫번째 자식요소는 ul, ul의 children 배열을 분해

 

 

형제노드

  • previousElementSibling : 이전 형제 요소
  • nextElementSibling : 다음 형제 요소

 

 

    console.log($banana.nextElementSibling === $grape);
    console.log($apple.nextElementSibling.nextElementSibling === $grape); // 다음의 다음
    
    // 출력
    true
    true

 

기준 태그 탐색

 

기준 태그 위로 탐색  기준 태그 아래로 탐색 
closest('CSS선택자') querySelector('CSS선택자')

 

const $modifyInput = $label.querySelector('.modify-input');
해당 라벨에서 '.modify-input' 클래스를 가진 첫번째 요소 탐색
  <div id="wrapper">
    <section class="contentx">
      <ul class="list">
        <li class="items"><a href="#" class="link">10</a></li>
        <li class="items"><a href="#" class="link active">20</a></li>
        <li class="items"><a href="#" class="link">30</a></li>
      </ul>
      <ul class="list">
        <li class="items"><a href="#" class="link">40</a></li>
        <li class="items"><a href="#" class="link">50</a></li>
        <li class="items"><a href="#" class="link">60</a></li>
      </ul>
    </section>
  </div>
  
  
      const $active = document.querySelector('.active');
  
      console.log($active          // a =20
                  .parentElement // li
                  .parentElement // ul
                  .nextElementSibling // ul2
                  .lastElementChild  // li
                  .firstElementChild // a = 60
    ); 

    // 기준 태그로부터 위로 올라가면서 탐색하는 함수
    // closest('css selector')
    const $contentSect = $active.closest('section.contents');
    
    // 기준 태그 아래방향 탐색
    const $found 
      = $contentSect
          .querySelector('ul.list:nth-child(2)')
          .querySelector('li.items:last-child')
        ;
    console.log($found);

 

속성 노드

<input id="name" type="text" value="Hi">
  • 문서가 파싱될 때 HTML 요소의 속성은 어트리뷰트 노드로 변환되어 요소 노드의 형제 노드로 추가됨
  • 모든 어트리뷰트 노드의 참조는 유사 배열 객체인 NamedNodeMap 객체에 담겨서
    요소 노드의 attributes 프로퍼티에 저장됨
  • 위 input의 속성(attribute)는 id, type, value 3개의 어트리뷰트 노드 생성

 

**  arraylike 이므로 속성 접근법은 1. 인덱스, 2. 키 

  <input type="email" id="account" value="abc@def.com">

  <script>
    
    const $input = document.querySelector('input');
    console.log($input.attributes);  // NamedNodeMap {0: type, 1: id, 2: value, type: type, id: id, value: value, length: 3}
    console.log($input.attributes[0]);
    console.log($input.attributes.type); // type="email"
    console.log($input.attributes.type.value); // email
    
    // $input.attributes.type.value = 'range';
    $input.attributes.id.value = 'acc';
    $input.attributes.value.value = 'abc@naver.com';

  </script>

 

 

 

속성 제어

속성 추가, 변경 속성값 참조 속성 삭제
setAttribute(name, value) getAttribute(name) removeAttribute(name)
$input.setAttribute('type','text') $input.getAttribute('value') $input.removeAttribute('style')

 

 

스타일 제어

$div.style.fontSize = '16px';
  • style 프로퍼티는 요소 노드의 인라인 스타일을 취득하거나 추가 또는 변경
  • style 프로퍼티를 참조하면 CSSStyleDeclaration 타입의 객체를 반환
  • 해당 객체는 css 프로퍼티에 대응하는 프로퍼티를 가짐
  • css속성을 사용할 때는 카멜케이스를 적용 ( ex. background-color → backgroundColor )

 

클래스 제어

  • className 프로퍼티는 HTML 요소의 class 속성 값을 취득하거나 변경
  • classList 프로퍼티는 class 속성의 정보를 담은 DOMTokenList 객체를 반환
  • 일반적으로 className 보다는 좀 더 유용한 메서드를 지원하는 classList로 클래스 제어 권장

 

 

**  className 으로 조작 : 기존 클래스 삭제되고 새로 입력하는 클래스 할당

**  classList 로 조작 : add를 통해 추가 가능

    // 클래스 조작
    $box.className = 'blue';  // box 클래스도 삭제되고 blue클래스만 적용
    $box.className = 'box blue';
    
    // 클래스 추가하기
     $box.classList.add('circle'); // box green circle
     $box.classList.add('aaa', 'bbb', 'ccc'); // box green circle aaa bbb ccc

 

classList 메서드

메서드명 설명
add(...className) 인수로 전달한 클래스를 추가
remove(...className) 인수로 전달한 클래스를 제거
item (index) index번째 해당하는 클래스 반환
contains(...className) 인수 클래스명과 일치하는 클래스 있는지 확인 (T/F 반환)
replace(old, new) 첫번째 인수 클래스를 두번째 인수 클래스로 교체
toggle(className) 인수로 전달한 클래스명이 속성으로 이미 존재하면 삭제
존재하지 않으면 추가

 

    $btn.addEventListener('click', () => {
      const CLASS_NAME = 'circle';
      const boxClassList = $box.classList;
      // 현재 박스가 네모모양이면 원으로 변경
      // 원이면 네모로 변경
     
      boxClassList.toggle(CLASS_NAME);
    });
    
    // label에 클래스 checked가 없으면 추가, 있으면 삭제
    e.target.closest('.checkbox').classList.toggle('checked');

 

data 어트리뷰트와 dataset 프로퍼티

  • data-aaa : aaa에 적절한 단어를 넣은 커스텀 속성을 제어할 수 있음   

 

    const [$user1, $user2] = [...document.querySelectorAll('.users li')];

    const pororoNumber = $user1.dataset.userNumber;
    console.log(typeof pororoNumber);  // string 숫자로 쓰려면 형변환
    
    const loopyRole = $user2.dataset.role;
    console.log(loopyRole);    // admin
    
    // 수정 (객체 문법)
    $user2.dataset.role = 'gold';
    // 추가
    $user1.dataset.userPhoneNumber = '01012344566';
    // 삭제
    delete $user1.dataset.userNumber;

 

이벤트

  • 브라우저는 클릭, 마우스 이동, 키보드 입력 등이 일어나면 이를 감지하여 특정한 타입의 이벤트 발생
  • 애플리케이션이 특정 이벤트에 반응하고 싶다면 이벤트에 대응하는 함수를 브라우저에게 알려주어 호출 위임 가능
  • 이 때 호출될 함수를 이벤트 핸들러라고 부르며
    이를 위임하는 것을 이벤트 핸들러 등록(binding)이라고 부름
  • 이벤트 드리븐 프로그래밍(event driven programming) 이벤트와 그에 대응하는 함수를 통해 프로그램의 흐름을 이벤트 중심으로 제어하는 방식을 

 

이벤트 타입

자주 쓰이는 이벤트 타입

  타입 설명
마우스





click 요소 위에서 마우스 왼쪽 버튼을 눌렀을 때
(터치스크린이 있는 장치에선 탭 했을 때) 발생
dblclick 요소 위에서 마우스 왼쪽 버튼을 두번 빠르게 눌렀을 때 발생
contextmenu 요소 위에서 마우스 오른쪽 버튼을 눌렀을 때 발생
mouseover 
mouseup
마우스 커서를 요소 위로 움직였을 때, 커서가 요소 밖으로 움직였을 때 발생
mouseleave
mouseout
요소 위에서 마우스 왼쪽 버튼을 누르고 있을 때, 마우스 버튼을 뗄 때 발생
mousemove  마우스를 움직일 때 발생
키보드 keydown, keyup 사용자가 키보드 버튼을 누르거나 뗄 때 발생




submit 사용자가 <form>을 제출할 때 발생
change 사용자가 <input>과 같은 요소에 값이 변할 때 발생
focus 사용자가 <input>과 같은 요소에 포커스 할 때 발생
blur 사용자가 <input>과 같은 요소에 포커스를 해제할 때 발생
  resize 브라우저 창의 크기를 변경할 때 발생
  scroll 웹페이지 또는 HTML 요소를 스크롤할 때 연속적으로 발생

 

이벤트 핸들러

  • 특정 타입의 이벤트가 발생했을 때 브라우저가 실행하는 함수
  • 사용자가 만든 이벤트 처리 함수를 브라우저에게 위임하는 개념
  • 이런 행위를 이벤트 핸들러 바인딩

**  이벤트 핸들러 바인딩 3가지 방법

      1. 어트리뷰트 방식  |  2. 프로퍼티 방식  |  3.콜백함수 방식

1. 어트리뷰트 방식

<div class="box" onclick="boxClickHandler()"></div>
  • 이벤트 핸들러를 HTML 요소에 직접 지정하는 방법
  • on + 이벤트 타입으로 이루어진 속성명을 적고 속성값으로 이벤트 핸들러 함수의 호출문
  • 서로 다른 이벤트라면 하나의 요소에 여러 이벤트 지정 가능
  // 서로 다른 이벤트라면 여러 개 지정 가능
  <div class="box" 
    onmouseleave="boxClickHandler()" 
    onmouseover="makeTextHandler()"></div>   // 오렌지 박스에 마우스 올리면 초록박스에 텍스트

 

2. 프로퍼티 방식

 $b1.onclick = sayHelloHandler;  함수 자체를 전달
$element.onclick = function () { 이벤트 실행문 };
  • 이벤트 핸들러를 요소 노드의 프로퍼티로 추가하는 방식
  • HTML과 자바스크립트를 분리하여 코딩할 수 있어 장점
  • 하나의 요소에 하나의 이벤트 핸들러만 바인딩할 수 있어 단점
    //이벤트 핸들러
    function sayHelloHandler() {
      alert('hello!😁');
    }
    const $b1 = document.getElementById('b1');
    $b1.onclick = sayHelloHandler; // 함수 전달  cf. sayHelloHandler() 반환값을 전달
    
    const $b2 = document.getElementById('b2');
    $b2.onmouseover = () => {
      $b2.style.width = '150px';
    };
    
    // 바인딩 할 수 없음
    $b3.onmouseleave = sayHelloHandler; // 이전 바인딩은 삭제되고 새로운 바인딩만 연결됨
    // 이벤트 핸들러 제거
    $b3.onmouseleave = null;

 

3. 콜백함수 방식

$btn.addEventListener('click', hellohandler);
  • addEventListener 메서드는 첫번째 인수로 이벤트 종류를 나타내는 문자열의 이벤트 타입을 전달
  • 이벤트타입은 접두사 on을 붙이지 않음
  • 두 번째 인수로는 이벤트 핸들러 함수를 콜백으로 전달
  • 동일한 요소에서 동일한 이벤트에 대해 하나 이상의 이벤트 핸들러를 등록 가능
  • 기존 핸들러 삭제 시 수동으로 해야하고 핸들러는 기명함수여야 함
    // 기존 핸들러를 지우고 싶으면 수동으로 지워야 함
    // 수동으로 지울 때는 반드시 핸들러가 기명함수이어야 함
    $btn.removeEventListener('click', hellohandler);