JS-ES6 (Web API - DOM)

짜스의 하루 ·2024년 5월 4일

DOM 이란?

Document Object Model 의 약어로 웹 문서를 제어하기 위해서 웹 문서를 객체화한 것을 말한다.
여기서 의미하는 객체는 값을 나타내는 프로퍼티와 어떠한 수행을 하는 메서드를 갖고 있다.

이렇게 웹 문서(HTML)를 객체화 함으로써 우리는 객체로 웹 문서를 제어할 수 있게 된다.

HTML 요소 조작하기

HTML 요소를 선택하기 위해서는 GET 메서드를 사용해야 한다
get 메서드

  • document.getElementById - 모든 HTML 요소에는 고유한 ID를 할당받을 수 있다
  • document.getElementsByClassName - HTML 클래스 명으로 요소를 찾을 수 있다.
  • document.getElementsByTagName - HTML 태그명으로 요소를 찾을 수 있다.
<ul id="list">
	<li class="item">
	<li class="item">
	<li class="item">
</ul>

라는 코드가 있다고 가정해보자

document.getElementById('list') 
document.getElementsByClassName('item') 
document.getElementsByTagName('li') 

순서대로 id 인 list를 선택하기, class 이름인 item을 선택하기, <>태그 이름인 li를 선택할 수 있다.

DOM 요소 쿼리

get 메서드와 같은 방법도 유용하지만, ID나 클래스, 태그 이름 같은 한 가지 조건이 아니라 다른 요소와의 관계를 사용해 원하는 요소를 찾는 훨씬 더 강력하고 범용적인 메서드도 있다.

querySelectorquerySelectorAllCSS 선택자를 사용해 요소를 찾는 메서드이다.

  • document.querySelector(CSS Selector) - 지정된 선택자에 일치하는 문서 내 첫 번째 요소를 반환한다. 일치하는 요소가 없으면 null을 반환한다.
  • document.querySelectorAll - 지정된 선택자에 일치하는 요소 목록을 반환한다.

HTML 요소 조작하기

콘텐츠 수정
textContentinnerHTML 프로퍼티를 통해 요소의 콘텐츠를 가져오거나 수정할 수 있다.

  • textContent - HTML 태그를 모두 제거하고 순수한 텍스트 데이터만 제공한다.
  • innerHTML - HTML 태그를 그대로 제공한다.

이게 무슨 말일까?
이렇게 <span> 태그를 붙여서 innerHTML, textContent를 각각 출력해보자


첫번째 사진이 innerHTML 프로터피를 사용해서 출력한 결과이다.
--> innerHTML 의 경우, 태그 를 같이 추가하는 (변경하는) 겻을 확인할 수 있다.
--> 반면, textContent의 경우, 태그는 제외한 후, 텍스트만 변경한 것을 확인할 수 있다.

속성 제어하기

  • setAttribute - 요소에서 주어진 이름의 속성값을 입력한다.
    html 태그에 placeholder = "운동을 입력해주세요"가 지정되어 있다.
document.querySelector('input').setAttribute('placeholder', '오늘 할 운동은 ?');

태그를 추가하자,
로 변경된 것을 확인할 수 있다 :)

  • getAttribute - 요소에서 주어진 속성의 값을 가져온다.
  • removeAttribute - 요소에서 주어진 이름의 속성을 제거한다.

HTML 요소 스타일링

요소 프로퍼티 직접 수정

  • style - style 프로퍼티를 사용하여 직접 수정
const btn = document.getElementById('button');
btn.style.color = 'white';
btn.style.backgroundColor = 'black';

CSS 클래스 이용

  • classList - classList 객체를 사용하여 class 를 수정
const btn = document.getElementById('button');
btn.classList.add('dark')
btn.classList.remove('dark')
  • 'dark'라는 클래스를 추가할 때, css 속성을 지정해 두었기 때문에, 변경이 되는 것이다.
.dark {
  color: wheat;
  background-color: black;
}

.light {
  color: black;
  background-color: wheat;
}

새로운 HTML 요소 만들기

새로운 요소 만들기

  • createElement - 지정한 tagName의 HTML 요소를 생성한다.
const element = document.createElement('p');

--> p태그를 생성하는 코드이다.

  • appendChild - 항상 마지막 자식 요소로 추가
const li = document.createElement('li');
li.textContent = 'Hello';
document.querySelector('ul').appendChild(li);

--> 새로운 <li>요소를 생성하고, 해당 요소에 'Hello'라는 텍스트 콘텐츠를 추가한 후, 이를 문서에 있는 <ul> 요소의 자식으로 추가하는 것을 의미한다.

  • insertBefore - 추가할 곳 위치 앞에 추가한다
    --> 매개변수를 두 개 받는다. 첫 번째 매개변수는 삽입할 요소이고, 두 번째 매개변수는 삽입할 위치를 정하는 요소이다.
const firstItem = document.querySelector('.first-item');
const li = document.createElement('li');
li.textContent = 'Hello';
document.querySelector('ul').insertBefore(li, firstItem);

--> 새로운 <li> 요소를 생성한 후, insertBefore 메서드로 자식 요소를 추가 --> "li를 firstItem 앞에 추가한다" 를 의미!

오늘의 운동을 추가할 수 있는 이런 페이지가 있다고 가정해보자.
1 . appendChild()

const li = document.createElement('li');
const ul = document.querySelector('.list');
const inputBox = document.querySelector('input');

const addBtn = document.querySelector('#button');

addBtn.addEventListener('click', () => {
  li.textContent = inputBox.value;
  ul.appendChild(li);
});
  • 새로운 <li> 요소를 생성하고 이를 li 변수에 할당
  • 클래스가 'list'로 지정된 <ul> 요소를 찾아 ul 변수에 할당
  • <input> 요소를 찾아 inputBox 변수에 할당
  • id가 'button'인 요소를 찾아 addBtn 변수에 할당
  • addBtn에 'click' 이벤트 리스너를 추가한다
    --> inputBox의 값(input 요소에 입력된 텍스트)을 가져와서 새로 생성한 <li>요소의 텍스트 콘텐츠로 설정한다.
  • 그리고 그 요소를 ul에 추가한다.

  1. insertBefore
const addBefore = document.querySelector('#before');
const targetLi = document.querySelector('#target');
addBefore.addEventListener('click', () => {
  li.textContent = inputBox.value;
  ul.insertBefore(li, targetLi);
});
  • id가 'before'인 요소를 찾아 addBefore 변수에 할당
  • id가 'target'인 요소를 찾아 targetLi 변수에 할당
  • addBefore에 'click' 이벤트 리스너를 추가
    --> inputBox의 값(input 요소에 입력된 텍스트)을 가져와서 새로 생성한 <li> 요소의 텍스트 콘텐츠로 설정
  • ul에 새로운 <li>요소를 targetLi 요소의 앞에 삽입

  1. remove - 해당 요소를 제거

먼저, 스쿼트 옆에 버튼을 눌렀을 때, 스쿼트가 삭제되도록 코드를 작성해보자

const removeBtn = document.querySelector('.remove-btn');

removeBtn.addEventListener('click', () => {
  const targetLi = document.querySelector('li#target');
  targetLi.remove();
});


이제는 추가 버튼을 눌렀을 때, text 옆에 X 삭제 버튼이 같이 생기면서, 삭제 버튼을 눌렀을 때, 삭제할 수 있도록 코드를 한번 짜보자!

addBtn.addEventListener('click', () => {
  const li = document.createElement('li');
  li.textContent = inputBox.value;

  // 새로운 삭제 버튼 생성
  const button = document.createElement('button');
  button.textContent = '❌';

  // 삭제 버튼 이벤트 리스너 추가
  button.addEventListener('click', (e) => {
    e.target.parentNode.remove();
  });

  li.appendChild(button);
  ul.appendChild(li);
});

빨간색 X 버튼이 생기고, x를 누를 때, 여러개 동시에 생기는 것을 확인할 수 있다!

간단하게 코드 해석해보고 넘어가자

  • 사용자가 "addBtn" 버튼을 클릭하면 addBtn의 클릭 이벤트 리스너가 실행된다.
  • 클릭 이벤트 리스너는 새로운 리스트 아이템을 만든다. 이를 위해 먼저 새로운 li 요소를 생성하고, 해당 요소의 텍스트 내용을 입력 상자(inputBox)에 입력된 값으로 설정한다.
  • 다음으로, 삭제 버튼을 생성한다. 이 버튼에 '❌'라는 텍스트 내용을 추가한다.
  • 삭제 버튼에 클릭 이벤트 리스너를 추가합니다. 이 리스너는 클릭된 버튼의 부모 노드인 리스트 아이템(li)을 삭제한다.
  • 마지막으로, 생성된 삭제 버튼을 리스트 아이템에 추가하고, 해당 리스트 아이템을 ul에 추가한다.

여기서, parentNode를 사용하지 않고

button.addEventListener('click', () => {
    li.remove();
  });

로 실행해도 코드는 제대로 실행되는 것을 확인할 수 있다.

추가적인 기능

inputBox.value = ' ';
inputBox.focus();

텍스트 상자에 글자를 입력 후, 추가 버튼을 누르면, 입력 상자(inputBox)의 값을 비우고, 그 상자에 포커스를 맞추는 기능이다.

이러한 작은 기능들이 페이지를 작동할 때 더 쉽게 작동할 수 있도록 도움을 준다.

데이터 속성

이건 처음 보는 속성이자 기능(?) 인 것 같다

HTML5에서는 데이터(data-) 속성을 도입했다.
이 속성을 사용해서 HTML 요소에 임의의 데이터를 추가할 수 있습니다.
--> 브라우저는 이 데이터 속성을 완전히 무시하므로 자바스크립트에서 쉽게 요소에 관한 정보를 읽거나 수정할 수 있다.

dataset

HTML 요소의 dataset 프로퍼티로 데이터(data-) 속성을 읽거나 수정할 수 있다.
예시로 알아보자

사과를 눌렀을 때, 사과 이미지가 나오고, '사과를 선택했습니다' 라는 문구가 나오도록 코드를 작성해보자

<body>
  <h2>과일</h2>
  <ul>
    <li data-img="../img/apple.jpg">사과</li>
    <li data-img="../img/tomatoes.jpg">토마토</li>
    <li data-img="../img/blueberries.jpg">블루베리</li>
  </ul>
  <h2 class="selected-item">과일을 선택해주세요.</h2>
  <img style="width: 200px" src="" alt="과일" />
</body>

우선 HTML을 살펴보면, img 태그에 이미지 위치를 저장하는 src 속성에 빈 값으로 저장해 두었다.
그리고 사과, 토마토, 블루베리에 각각 data-img 속성을 사용해 이미지 주소를 저장해 두었다.

const liList = document.querySelectorAll('li');
const img = document.querySelector('img');
const text = document.querySelector('.selected-item');

liList[0].addEventListener('click', (event) => {
  img.setAttribute('src', event.target.dataset.img);
  text.textContent = event.target.textContent + '를 선택했습니다.';
});
  • document 객체의 querySelectorAll() 메서드를 사용하여 HTML 문서 내의 모든 <li> 요소를 선택하고, 이를 liList라는 변수에 저장한다.
  • document 객체의 querySelector() 메서드를 사용하여 HTML 문서 내의 첫 번째 <img>요소를 선택하고, 이를 img라는 변수에 저장한다.
  • document 객체의 querySelector() 메서드를 사용하여 HTML 문서 내에서 클래스가 'selected-item'인 요소를 선택하고, 이를 text라는 변수에 저장한다.
  • liList 배열의 첫 번째 요소에 이벤트 리스너를 추가한다. 클릭 이벤트가 발생하면, 해당 함수가 실행된다.
  • 함수는 이벤트 객체를 매개변수로 받으며, 클릭된 요소의 data-img 속성값을 가져와서 img 요소의 src 속성에 설정하고, 선택된 요소의 텍스트를 가져와서 '를 선택했습니다.' 문자열과 함께 textContent에 설정한다.


사과를 선택하자, 이렇게 이미지와 텍스트가 정상적으로 출력하는 것을 확인할 수 있다.
그럼 나머지 토마토, 블루베리도 똑같은 코드를 설정해주어야 하는데, 그럼 스크립트 파일에 중복된 코드가 3개가 되는 것이다.
또한 다른 기능을 추가하고 싶을 때에도 3개 모두 추가를 해주어야 한다.

--> 이러한 중복과 가독성을 위해 반복적으로 사용하는 것들을 함수로 묶어서 사용하면 된다.

liList[0].addEventListener('click', selectItem);
liList[1].addEventListener('click', selectItem);
liList[2].addEventListener('click', selectItem);

function selectItem(event) {
  img.setAttribute('src', event.target.dataset.img);
  text.textContent = event.target.textContent + '를 선택했습니다.';
}
  • liList 배열의 각 요소에 대해 addEventListener() 메서드를 사용하여 클릭 이벤트에 대한 이벤트 리스너를 추가한다. 모든 liList 요소에 동일한 이벤트 리스너 함수 selectItem을 연결한다.
  • selectItem 함수는 클릭된 요소의 데이터 속성인 data-img 값을 가져와서 이미지 요소의 src 속성에 설정하고, 선택된 요소의 텍스트를 가져와서 '를 선택했습니다.' 문자열과 함께 textContent에 설정한다.

또한 여기서 또 잘 살펴보면

liList[0].addEventListener('click', selectItem);
liList[1].addEventListener('click', selectItem);
liList[2].addEventListener('click', selectItem);

이 부분이 중복적으로 사용되는 것을 확인할 수 있다. 이를 어떻게 해결하면 좋을 까?

document.querySelectorAll('li');를 사용하여 <li> 요소를 선택하면, NodeList 객체가 반환된다. 이는 배열과 유사하지만 완전한 배열은 아니다. NodeList는 실제 배열이 아니므로 배열의 메서드를 직접 사용할 수는 없다.
forEach() 메서드는 각 요소에 대해 주어진 함수를 한 번씩 실행한다. 이것은 반복 가능한(iterable) 객체에 대해 유용한 일반적인 반복 작업이다. 따라서 NodeList에서도 forEach() 메서드를 사용하여 각 요소에 대한 반복 작업을 수행할 수 있다!

liList.forEach((li) => {
  li.addEventListener('click', selectItem);
});

--> liList.forEach()를 사용하여 각 요소에 대해 클릭 이벤트에 대한 이벤트 리스너를 추가하면 된다!

이벤트 등록하기

인라인 (Inline)

이벤트 대상의 HTML 태그 속성으로 지정, this를 통해 이벤트가 발생한 대상 요소를 참조할 수 있다.

<button onclick="alert('hello');">버튼</button>

프로퍼티 리스너 (Property Listener)

이벤트 대상이 되는 객체의 프로퍼티로 이벤트 등록

  • HTML
<button id="btn">버튼</button>
  • JavaScript
const btn = document.getElementById('btn')
btn.onclick = function () {
	alert('hello');
}

AddEventListener()
이벤트를 등록하는 가장 권장되는 방식이며, 하나의 이벤드 대상 요소에 여러개의 이벤트를 등록할 수 있다.

btn2.addEventListener('click', () => {
  alert('안녕 나는 addEventListener 이지롱 ');
});

이벤트 제거하기

  • removeEventListener - addEventListener 로 등록했던 이벤트를 제거한다.
    위와 같이, removeEventListener()를 사용하려면, 함수로 정의되어 있어야 한다!

Event 객체

쉽게 설명하자면, 이벤트 객체는 이벤트가 발생했을 때, 해당 이벤트에 대한 정보를 담고 있는 객체이다.

btn.addEventListener('click', function(event) {
	console.log('event: ', event);
})

이벤트 종류

마우스 이벤트

  • mousedown - 마우스가 요소안에서 클릭이 눌릴 때
  • mouseup - 마우스가 요소안에서 클릭이 해제될 때
  • mouseenter - 마우스 포인터가 요소 안으로 진입 했을 때
  • mouseleave - 마우스 포인터가 요소 밖깥으로 나갔을 때
  • mousemove - 마우스 포인터가 요소 안에서 움직일 때

.rectangle {
  border: 1px solid black;
  width: 300px;
  height: 150px;
  background-color: green;
}

.rectangle-red {
  border: 1px solid black;
  width: 300px;
  height: 150px;
  background-color: red;
}
rectangle.addEventListener('mousedown', (e) => {
  rectangle.classList.add('rectangle-red');
});

rectangle.addEventListener('mouseup', (e) => {
  rectangle.classList.remove('rectangle-red');
});

'mousedown' 이벤트에서는 'rectangle-red' 클래스를 추가하여 배경을 빨간색으로 변경하고, 'mouseup' 이벤트에서는 'rectangle' 클래스를 제거하고 기본적인 속성인 'rectangle'를 유지하도록 코드를 작성해 보았다.
원래 기본 상태 마우스를 눌렀을 때

요로코롬 이벤트가 적용된 것을 확인할 수 있다!

rectangle.addEventListener('mouseenter', () => {
  rectangle.classList.add('rectangle-blue');
  rectangle.classList.remove('rectangle-yellow');
});

rectangle.addEventListener('mouseleave', () => {
  rectangle.classList.remove('rectangle-blue');
  rectangle.classList.add('rectangle-yellow');
});

mouseenter 기능을 사용하면, 마우스가 상자 안에 들어가면, 파란색으로 변하고, 상자 밖으로 나오면 노란색으로 바뀌도록, 설정해 두었다.

이런식으로 마우스 이벤트를 적용하면 된다!

유용한 이벤트 속성&메서드

  • event.clientX event.clientY - 브라우저 화면 기준 X, Y 좌표
  • event.pageX event.pageY - 전체화면 기준(스크롤 포함) X, Y좌표
  • event.target.getBoundingClientRect() - 요소의 크기와 뷰포트로 부터 상대적인 위치를 반환
rectangle.addEventListener('mousedown', (event) => {
  console.log('clientX : ', event.clientX, 'clientY :', event.clientY);

  console.log('pageX : ', event.pageX, 'pageY :', event.pageY);
});

이렇게 현재 내가 보고 있는 브라우저 화면 기준으로 X, Y 좌표를 출력하거나, 전체 화면의 기준의 X,Y의 값을 출력해보자

요렇게 출력되는 것을 확인할 수 있다.

그렇다면, X 와 Y 좌표를 활용해서 내가 클릭한 곳에 동그란 원을 화면에 띄어보도록 해보자

나는 화면 전체에 어디든지 동그란 원을 띄우기 위해 body 태그 전체를 가지고 온다.

const body = document.querySelector('body');
.circle {
  height: 50px;
  width: 50px;
  background-color: red;
  position: absolute;
  border-radius: 50%;
  /* left: 400px;
  top: 100px; */
}

이후 css 속성으로 circle의 속성을 지정해 둔다.

body.addEventListener('click', (event) => {
  const div = document.createElement('div');
  div.classList.add('circle');
  div.style.left = event.pageX - 25 + 'px';
  div.style.top = event.pageY - 25 + 'px';

  body.appendChild(div);
});
  • body.addEventListener('click', (event) => { ... }) : body 요소에 클릭 이벤트 리스너를 추가한다 즉, 페이지의 어느 곳이든 클릭이 발생하면 이벤트가 실행된다.
  • const div = document.createElement('div'); : 새로운 div 요소를 생성한다.
  • div.classList.add('circle'); : 새로 생성된 div 요소에 위에 정의한 'circle' 클래스를 추가한다.
  • div.style.left = event.pageX - 25 + 'px';: 생성된 div 요소의 가로 위치를 설정한다. event.pageX클릭이 발생한 페이지의 X 좌표를 나타내며, 여기서 25를 빼는 이유는 원의 중심이 되는 위치로 설정하기 위해서이다. 'px'는 픽셀 단위를 나타낸다.
  • div.style.top = event.pageY - 25 + 'px'; : 생성된 div 요소의 세로 위치를 설정한다. event.pageY는 클릭이 발생한 페이지의 Y 좌표를 나타내며, 마찬가지로 25를 빼는 이유는 원의 중심이 되는 위치로 설정하기 위해서이다.
  • body.appendChild(div); : body 요소에 생성된 div 요소를 추가한다. 따라서 클릭할 때마다 새로운 div 요소가 생성되어 페이지에 추가된다.

이렇게 클릭한 위치에 원이 정 가운데로 설정되는 것을 확인할 수 있다!

키보드 이벤트

  • keydown : 키보드의 키가 눌렸을 때 발생하는 이벤트
  • keypress : 키보드의 문자 키가 입력되었을 때 발생하는 이벤트
  • keyup : 키보드의 키가 눌렸다가 떼어졌을 때 발생하는 이벤트
inputBox.addEventListener('keypress', (e) => {
  console.log('keypress');
});

inputBox.addEventListener('keydown', (e) => {
  console.log('keydown');
});

inputBox.addEventListener('keyup', (e) => {
  console.log('e.key : ' + e.key);
  console.log('e.keyCode : ' + e.keyCode);
});

이런식으로 코드를 작성할 수 있다.
여기서, e.key 는 키보드에 입력한 문구를 출력하는 것이고, e.keyCode 문자의 아스키코드 코드 값을 반환한다.

연습 코드

inputBox.addEventListener('keypress', (e) => {
  // 키가 입력될 때마다 inputBox의 배경색을 노란색으로 변경
  inputBox.style.backgroundColor = 'yellow';
});

inputBox.addEventListener('keyup', (e) => {
  // 입력이 끝나면 inputBox의 배경색을 다시 원래대로 되돌림
  inputBox.style.backgroundColor = '';
});

키보드의 키가 눌렸을 때, inputBox의 배경을 노란색으로 변경하고, 입력이 끝나는 즉시 다시 원래대로 되돌리는 코드이다. 글자가 입력될 때 칸이 노란색으로 변한 것이다!

포커스 이벤트

  • focus : 요소가 포커스를 받았을 때 발생하는 이벤트
  • blur: 요소가 포커스를 잃었을 때 발생하는 이벤트
  • change : 입력 요소의 값이 변경될 때 발생하는 이벤트

focus

inputBox.addEventListener('focus', () => {
  alert('focus 성공');
});


inputBox에 포커스가 발생했을 때, 알림창을 뜰 수 있도록 코드를 작성해 보았다.
그럼 포커스에서 벗어날 때는 어떻게 작성하면 될까?

blur

inputBox.addEventListener('blur', () => {
  alert('blur 실행');
});

change

inputBox.addEventListener('change', () => {
  console.log('입력값 변경');
});

입력 값이 변할 때마다, 콘솔에 '입력값 변경'을 띄우도록 코드를 작성해 보았다.

폼 이벤트

  • submit - 양식(Form)이 제출하기전에 발생 하는 이벤트 입니다. 주로 전송될 값을 유효성 체크할 때 사용한다.
<h2>form</h2>
  <form onsubmit="alert('submit 성공?')">
    <input type="text" name="username">
    <button type="submit">제출하기</button>
  </form>
  <h2>Image</h2>
  <img src="image.jpg" alt="이미지" />
  • <form> 요소는 사용자 입력을 받기 위한 HTML 폼을 정의한다.
  • onsubmit 속성을 사용하여 폼이 제출될 때 실행할 JavaScript 코드를 지정한다. 여기서는 제출이 발생할 때 "submit 성공?"이라는 경고창을 표시하게 된다.

진행(progress) 이벤트

  • error 이벤트를 간단하게 설명하고 넘어가자
<img src="image.jpg" alt="이미지" />

주소를 잘못 입력한 img 태그가 하나 있다. 주소를 잘 못 입력했더니 이렇게 출력되는 것을 확인할 수 있다.

이러한 에러를 처리할 코드를 한 번 작성해보자

img.addEventListener('error', () => {
  console.log('error!');
});

img에 이벤트를 부여했는데, error가 발생했을 시, console에 error! 를 출력하도록 했다

이때, 이미지 로딩에 실펴하면,
이미지의 기본값을 설정해두어 화면에 출력해보도록 하면 어떨까?

img.addEventListener('error', (e) => {
  console.log('error!');
  img.src = '../img/apple.jpg';
});

img에 error 가 발생한다면, img의 src의 값을 '../img/apple.jpg'로 변경하라! 라는 의미이다.

사과 이미지가 정상적으로 출력되는 것을 확인할 수 있다!

profile
2024. 01. 02 ~ 백앤드 공부 시작, 2024. 04.01 ~ 프론트 공부 시작

0개의 댓글