TIL - 21.07.05 πŸ‘¨β€πŸ’» -Js. DOM

μ„±ν›ˆΒ·2021λ…„ 7μ›” 5일
0

TIL

λͺ©λ‘ 보기
21/59
post-thumbnail

TIL - 21.07.05 πŸ‘¨β€πŸ’»

Document Object Model


μžμ‹ μš”μ†Œ(λ…Έλ“œ) μ°ΎκΈ°

console.dir λ©”μ†Œλ“œλŠ” DOM을 객체둜 좜λ ₯ν•΄μ£ΌλŠ” λ©”μ†Œλ“œμ΄λ‹€.
console.dir(document.body)λ₯Ό μ½˜μ†”μ°½μ— μž…λ ₯ν•˜κ³  μ—¬λŸ¬ ν‚€ 쀑 children을 찾으면 객체둜 body의 μžμ‹ μš”μ†Œλ‘œ μ •λ¦¬λ˜μ–΄ μžˆλŠ” 것을 찾을 수 μžˆλ‹€.
이후 이 킀에 λ‹€μ‹œ μ ‘κ·Όν•˜λ €λ©΄ 객체에 λ‹· λ…Έν…Œμ΄μ…˜μœΌλ‘œ ν•΄λ‹Ή ν‚€ children을 μž…λ ₯ν•˜λ©΄ μ›ν•˜λŠ” μ •λ³΄λ§Œ 쑰회 ν•  수 μžˆλ‹€.

<body>
  <div>division 1</div>
  <div>division 2</div>
  <div>division 3</div>
</body>
console.dir(document.body.children) // { 0: div, 1: div, 2: div }

λΆ€λͺ¨ μš”μ†Œ(λ…Έλ“œ) μ°ΎκΈ°

λΆ€λͺ¨ μš”μ†Œλ₯Ό μ°Ύμ•„λ‚΄λŠ” λ©”μ†Œλ“œλŠ” 두가지가 μžˆλŠ”λ°, ν•˜λ‚˜λŠ” node.parentElement 이고, λ‹€λ₯Έ ν•˜λ‚˜λŠ” node.parentNode이닀.

  • node.parentElement λ©”μ†Œλ“œλŠ” λΆ€λͺ¨μ˜ μš”μ†Œ λ…Έλ“œλ§Œ λ°˜ν™˜ν•˜λŠ” λ©”μ†Œλ“œμ΄μ§€λ§Œ λ§Œμ•½ λΆ€λͺ¨ λ…Έλ“œκ°€ μ—†κ±°λ‚˜ λΆ€λͺ¨κ°€ 돔 μš”μ†Œκ°€ μ•„λ‹ˆλΌλ©΄ null을 λ°˜ν™˜ν•œλ‹€.

  • node.parentNode 의 κ²½μš°μ—” λΆ€λͺ¨ λ…Έλ“œκ°€ λ¬Έμ„œ λ…Έλ“œλΌλ„ λ…Έλ“œλ‘œ 잘 λ°˜ν™˜ν•œλ‹€.


DOMμ—μ„œ HTML μ‘°μž‘ν•˜κΈ°

DOMμ—μ„œ μ‘°μž‘μ— ν•„μš”ν•œ ν”„λ‘œμ„ΈμŠ€λŠ” 크게 4+1 가지이닀.
C(A)RUD둜 각각 Create, Append, Read, Update, Deleteλ₯Ό λœ»ν•œλ‹€.

  • Create λ©”μ†Œλ“œλ‘œλŠ” document.createElementκ°€ μžˆλ‹€.

  • Append λ©”μ†Œλ“œλ‘œλŠ” node.append와 node.appendChild, node.insertBefore등이 μžˆλ‹€.

  • Read λ©”μ†Œλ“œλ‘œλŠ” document.querySelector와 document.querySelectorAll이 있고
    κ΅¬ν˜• λ©”μ†Œλ“œλ‘œ document.getElementby~κ°€ μžˆλ‹€.

  • Update λ©”μ†Œλ“œλ‘œλŠ” λŒ€ν‘œμ μœΌλ‘œ node.textContent와 element.id, element.classList, 그리고 element.setAttribute 등이 μžˆλ‹€.

  • Delete λ©”μ†Œλ“œλ‘œλŠ” node.delete, node.removeChild, node.innerHTML = '', node.textContent = '' 등이 μžˆλŠ”λ° 이쀑 node.innerHTML을 μ΄μš©ν•œ λ©”μ†Œλ“œλŠ” λ³΄μ•ˆ 취약점 문제둜 잘 쓰이지 μ•ŠλŠ”λ‹€.


Create - document.createElement()

ν•΄λ‹Ή λ©”μ†Œλ“œλŠ” document 객체의 createElement()λ©”μ†Œλ“œλ₯Ό μ΄μš©ν•΄μ„œ μš”μ†Œλ₯Ό μƒμ„±ν•˜λŠ” λ©”μ†Œλ“œμ΄λ‹€.
μƒμ„±ν•˜λŠ” 방법은 λ³€μˆ˜ ν•˜λ‚˜λ₯Ό μ„ μ–Έν•˜κ³  거기에 createElementλ©”μ†Œλ“œλ‘œ μƒμ„±ν•œ μš”μ†Œλ₯Ό ν• λ‹Ήν•œλ‹€.
μ΄λ•Œ μš”μ†Œλ₯Ό μƒμ„±ν•œλ‹€κ³  HTML νƒœκ·Έμ— λ°”λ‘œ 적용 λ˜λŠ” 것이 μ•„λ‹ˆλΌ DOM tree에 μ—°κ²°λ˜μ§€ λͺ»ν•œ, 즉, λΆ• λ–  μžˆλŠ” μƒνƒœκ°€ λœλ‹€. κ·Έλž˜μ„œ 이 λΆ• λ– μžˆλŠ” μš”μ†Œλ₯Ό 돔 νŠΈλ¦¬μ— μ—°κ²° μ‹œν‚€λŠ” Append 과정이 ν•„μš”ν•œ 것이닀.

let addDiv = document.createElement('div')
console.log(addDiv) // <div></div>; 빈 div μ—˜λ¦¬λ¨ΌνŠΈκ°€ λ³€μˆ˜ addDiv에 ν• λ‹Ήλ˜μ—ˆλ‹€.

Append - node.append vs node.appendChild

두 λ©”μ†Œλ“œλ“€ μ „λΆ€ λΆ€λͺ¨ λ…Έλ“œμ— μžμ‹ λ…Έλ“œλ₯Ό μΆ”κ°€ν•˜λŠ” λ©”μ†Œλ“œ μ΄λ‚˜ λͺ‡κ°€μ§€ μ£Όμš”ν•œ 차이가 μžˆλ‹€.

  1. append λ©”μ†Œλ“œλŠ” DOM String, 즉 λ¬Έμžμ—΄μ„ μΆ”κ°€ν•  수 μžˆμœΌλ‚˜ appendChild λ©”μ†Œλ“œλŠ” λ…Έλ“œ μš”μ†Œλ§Œ μΆ”κ°€ν•  수 μžˆλ‹€.

  2. appendλŠ” λ°˜ν™˜ 값이 μ—†μœΌλ‚˜ (undefined) appendChildλŠ” μΆ”κ°€λœ λ…Έλ“œλ₯Ό λ°˜ν™˜ν•œλ‹€.

  3. appendλŠ” ν•œλ²ˆμ— μ—¬λŸ¬ λ…Έλ“œμ™€ λ¬Έμžμ—΄μ„ μΆ”κ°€ν•  수 μžˆμœΌλ‚˜ appendChildλŠ” ν•œλ²ˆμ— ν•œ λ…Έλ“œ μ”©λ§Œ μΆ”κ°€ ν•  수 μžˆλ‹€.

let addDiv = document.createElement('div');

addDiv.append('Hello!'); // undefined;
console.log(addDiv); // <div>Hello!</div>;
addDiv.appendChild('Hi, there!'); 
// TypeError: Failed to execute 'appendChild' on 'Node'
// appendChild λŠ” λ…Έλ“œλ§Œ μ μš©μ‹œν‚¬ 수 있으며 appendλŠ” λ¬Έμžμ—΄λ„ μΆ”κ°€ν•  수 μžˆλ‹€.

document.body.append(addDiv); // undefined;
document.body.appendChild(addDiv); // <div>Hello!</div>;

// appendλŠ” λ°˜ν™˜ν•˜λŠ” 값이 μ—†μ§€λ§Œ appendChildλŠ” μ μš©ν•œ λ…Έλ“œλ₯Ό λ°˜ν™˜ν•œλ‹€.

let addSpan = document.createElement('span');
let addP = document.createElement('p');

document.body.append(addDiv, addSpan, 'Yeah!', addP); // undefined
document.body appendChild(addDiv, addSpan, 'Yeah!', addP); 
// <div>Hello!</div>; κ°€μž₯ 처음 적은 λ³€μˆ˜λ§Œ μ μš©λœλ‹€.

node.insertBefore()

insertBefore λ©”μ†Œλ“œλŠ” appendChild와 ꡉμž₯히 μœ μ‚¬ν•˜λ‹€.
ν•˜λ‚˜ 차이 점은 appendChild둜 λ…Έλ“œλ₯Ό μ μš©μ‹œν‚€κ²Œ 되면 λΆ€λͺ¨ λ…Έλ“œ μ•„λž˜ ν˜•μ œ λ…Έλ“œλ“€ μ‚¬μ΄μ—μ„œλ„ μ΅œν›„λ―Έμ— μΆ”κ°€λ˜λ‚˜, insertBefore λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ ν˜•μ œ λ…Έλ“œ μ‚¬μ΄μ—μ„œ 어디에 좔가할지 지정할 수 μžˆλ‹€.

문법은 parentNode.insertBefore(newNode, referenceNode) 이며 newNodeκ°€ referenceNode μ•žμ— 적용되게 λœλ‹€.

λ§Œμ•½ referenceNodeμžλ¦¬μ— null이 였면 append 와 ν˜•μ œ λ…Έλ“œμ€‘ κ°€μž₯ λ§ˆμ§€λ§‰μ— μœ„μΉ˜ν•˜κ²Œ λœλ‹€.

<!--insertBefore 적용 μ „-->
<body>
  <div class='box'>division 1</div>
  <div>division 2</div>
  <div>division 3</div>
</body>
let addDiv = document.createElement('div');
addDiv.textContent = 'Hello!';
document.body.insertBefore(addDiv, document.body.children[0]);
//<div>Hello!</div>; appendChild와 같이 적용된 λ…Έλ“œ λ°˜ν™˜λœλ‹€.
// document.body.children[0] λŒ€μ‹  document.querySelector('.box')λ₯Ό μ‚¬μš©ν•΄λ„
// 같은 κ²°κ³Όκ°€ λ‚˜μ˜¨λ‹€.
<!--insertBefore 적용 ν›„-->
<body>
  <div>Hello!</div>
  <div class='box'>division 1</div>
  <div>division 2</div>
  <div>division 3</div>
</body>

Read - .querySelector() vs .querySelectorAll()

document.querySelector()의 κ²½μš°λŠ” 이전 ν¬μŠ€νŒ…μ—λ„ μž μ‹œ μ–ΈκΈ‰ν•œ 적이 μžˆλ“―, CSS μ…€λ ‰ν„°λ₯Ό 인자둜 λ°›μœΌλ©° μΌμΉ˜ν•˜λŠ” 것을 νƒμƒ‰ν•˜λŠ”λ°, κ°€μž₯ 처음 찾은 것을 λ°˜ν™˜ν•˜κ³  탐색을 μ€‘μ§€ν•œλ‹€.

document.querySelectorAll()의 κ²½μš°λŠ” μΈμžμ— ν•΄λ‹Ήν•˜λŠ” λͺ¨λ“  μš”μ†Œλ₯Ό λΆ€λͺ¨ μžμ‹ 관계 상관없이 νƒμƒ‰ν•˜κ³  Array-like Object, μœ μ‚¬ 객체둜 λ°˜ν™˜ν•œλ‹€.
이 μœ μ‚¬ 객체와 for 문을 μ΄μš©ν•΄ μš”μ†Œμ— μΌκ΄„μ μœΌλ‘œ μ μš©μ‹œν‚¬ 수 μžˆμ§€λ§Œ, 배열같이 보여도 배열은 μ•„λ‹ˆλΌμ„œ mapκ³Ό 같은 λ°°μ—΄ μ „μš© λ©”μ†Œλ“œκ°€ μž‘λ™ν•˜μ§€ μ•ŠλŠ”λ‹€.
(forEach()의 κ²½μš°λŠ” μœ μ‚¬λ°°μ—΄μ˜ ν”„λ‘œν† μ— κΈ°μž…λ˜μ–΄ μžˆκΈ°μ— μž‘λ™ν•œλ‹€.)

λ°°μ—΄ μ „μš© λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„  이 μœ μ‚¬ 배열을 μ§„μ§œ λ°°μ—΄λ‘œ λ³΅μ‚¬ν•΄μ•Όν•˜λŠ”λ° κ·Έ λ°©λ²•μœΌλ‘œλŠ”, Array.from λ©”μ†Œλ“œλ₯Ό μ΄μš©ν•˜λŠ” 방법, μ „κ°œ ꡬ문을 μ΄μš©ν•΄ λ³΅μ‚¬ν•˜λŠ” 방법, slice()λ₯Ό μ΄μš©ν•΄ λ³΅μ‚¬ν•˜λŠ” 방법 등이 μžˆλ‹€.

<body>
  <div class='box'>division 1</div>
  <div class='box'>division 2</div>
  <div class='box'>division 3</div>
</body>
console.log(document.querySelector('.box')) 
// <div class='box'>division 1</div>; κ°€μž₯ λ¨Όμ € 찾은 μš”μ†Œλ₯Ό λ°˜ν™˜ν•œλ‹€.

console.log(document.querySelectorAll('.box'))
// [div.box, div.box, div.box]; λ°°μ—΄ κ°™μ•„ λ³΄μ΄λŠ” μœ μ‚¬ 객체둜 λ°˜ν™˜ν•œλ‹€.

let boxes = document.querySelectorAll('.box')
for( i = 0; i < boxes.length; i++) {
  boxes[i].style.color = 'red';
}
// μƒμˆ ν•œ μ˜ˆμ œμ™€ 같이 νƒμƒ‰λœ μš”μ†Œ 전체에 μ μš©μ‹œν‚¬ 수 μžˆλ‹€.

getElementBy~

이 κ΅¬μ‹œλŒ€ λ©”μ†Œλ“œλ“€ μ—­μ‹œ μ°ΎλŠ” κ°€μž₯ 첫번재 μš”μ†Œλ₯Ό λ°˜ν™˜ν•˜κ³  μ€‘μ§€ν•˜μ§€λ§Œ querySelectorAll 같이 μž‘λ™ν•˜λŠ”κ²Œ λ‘˜ μžˆλŠ”λ° λ°”λ‘œ getElementByTagNameκ³Ό getElementByClassName 이닀.
이 두 λ©”μ†Œλ“œλŠ” querySelectorAll κ³Ό λΉ„μŠ·ν•˜κ²Œ λΆ€λͺ¨μžμ‹κ΄€κ³„ 상관없이 ν•΄λ‹Ήν•˜λŠ” 것 μ „λΆ€ μ„ νƒν•˜λ©° μ„ νƒν•œ μš”μ†Œλ₯Ό μœ μ‚¬ 객체둜 λ°˜ν™˜ν•œλ‹€.


Update - textContent, classList, id, setAttribute

element.textContentλŠ” μš”μ†Œμ— λ¬Έμžμ—΄μ„ μΆ”κ°€ν•˜λŠ” λ©”μ†Œλ“œμ΄κ³ ,
element.classList μš”μ†Œμ— 클래슀λ₯Ό μΆ”κ°€ν•˜λŠ” λ©”μ†Œλ“œμ΄λ©°,
element.id μš”μ†Œμ— idλ₯Ό μΆ”κ°€ν•˜λŠ” λ©”μ†Œλ“œμ΄λ‹€.
setAttributeλŠ” 아이디와 ν΄λž˜μŠ€λŠ” λ¬Όλ‘ , μš”μ†Œμ— μ „λ°˜μ μΈ 속성을 λΆ€μ—¬ν•˜κ²Œ λ„μ™€μ£ΌλŠ” λ©”μ†Œλ“œμ΄λ‹€.
문법은 element.setAttribue('name', 'value') 이닀.

classList 같은 경우 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” 클래슀λ₯Ό μΆ”κ°€ν•˜κ±°λ‚˜ μ œκ±°ν•΄λ„ μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.

textContent vs innerHTML

λ‘κ°œ λ©”μ†Œλ“œ μ „λΆ€ HTML λ‚΄μš©μ— κ°„μ„­ν•  수 μžˆμœΌλ‚˜
innerHTML 같은 κ²½μ›¨λŠ” HTML을 직접 μˆ˜μ •ν•˜λŠ” μ μ—μ„œ λ³΄μ•ˆ 취약점이 λ°œμƒν•˜λ©° 이λ₯Ό λ…Έλ¦° 곡격은 <script> νƒœκ·Έλ₯Ό μ΄μš©ν•œ XSS Attack이 λŒ€ν‘œμ μ΄λ‹€.

Delete - remove vs removeChild

node.remove()와 node.removeChild()λŠ” λ‘˜λ‹€ λ…Έλ“œλ₯Ό μ‚­μ œν•˜λŠ” λ©”μ†Œλ“œμ΄μ§€λ§Œ 차이 점이 λͺ‡κ°€μ§€ μžˆλ‹€.

remove 같은 κ²½μš°λŠ” λ©”μ†Œλ“œλ₯Ό μ‹€ν–‰ν•  경우 λ…Έλ“œ 데이터λ₯Ό μ‚­μ œν•œ ν›„ λ°˜ν™˜ 없이 (undefined) λ©”μ†Œλ“œκ°€ μ’…λ£Œλœλ‹€.
removeChild 같은 κ²½μš°λŠ” μ •ν™•νžˆ μ„ νƒν•œ μš”μ†Œλ₯Ό 돔 νŠΈλ¦¬μ—μ„œ 연결을 λŠμ–΄λ‚΄ 접근이 μ•ˆλ˜κ²Œ ν•˜λŠ” 것이닀.
이와 λ™μ‹œμ— 연결을 λŠμ–΄λ‚Έ 값을 λ°˜ν™˜ν•΄μ„œ λ‹€λ₯Έ λ³€μˆ˜μ— ν• λ‹Ήν•  μˆ˜λ„ μžˆλ‹€.
λ§Œμ•½ ν• λ‹Ήν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄ 가비지 μ»¬λ ‰μ…˜μ— μ˜ν•΄ μžλ™μ μœΌλ‘œ μ •λ¦¬λœλ‹€.

<body>
  <div class='box'>division 1</div>
  <div class='box'>division 2</div>
  <div class='box'>division 3</div>
</body>
document.querySelector('.box').remove(); // undefined; ν•΄λ‹Ή λ…Έλ“œλ₯Ό μ‚­μ œν•œλ‹€.
document.querySelector('.box').removeChild(); 
// <div class='box'>division 2</div>; ν•΄λ‹Ή λ…Έλ“œμ™€ 돔 νŠΈλ¦¬μ™€μ˜ 연결을 λŠμ–΄λ‚΄κ³  
// ν•΄λ‹Ή λ…Έλ“œλ₯Ό λ°˜ν™˜. μ΄λŒ€λ‘œ λ‘”λ‹€λ©΄ 가비지 셀렉터에 μ˜ν•΄ λ°μ΄ν„°λŠ” μ‚­μ œλœλ‹€.

let div2 = document.querySelector('.box').removeChild(); 
// 이런 μ‹μœΌλ‘œ λ‹€λ₯Έ λ³€μˆ˜μ— ν• λ‹Ήν•œλ‹€λ©΄ 가비지 컬렉터에 μ˜ν•΄ μ‚­μ œλ˜μ§€ μ•Šκ³  후에 
// μž¬μ‚¬μš© ν•  수 μžˆλ‹€.

기타 λ©”μ†Œλ“œ


node.cloneNode

cloneNode λ©”μ†Œλ“œλŠ” λ…Έλ“œλ₯Ό λ³΅μ œν•˜λŠ” λ©”μ†Œλ“œμ΄λ‹€.
문법은 λ‹€μŒκ³Ό κ°™μœΌλ©° node.cloneNode(deep) 이 쀑 deep은 λ³΅μ‚¬μ˜ 깊이λ₯Ό κ²°μ •ν•˜λŠ” μΈμžμ΄λ‹€.
trueλ₯Ό λ„£μœΌλ©΄ κΉŠμ€ 볡사λ₯Ό ν•˜λ©° falseλ₯Ό λ„£μœΌλ©΄ 얕은 볡사λ₯Ό ν•˜κ²Œ λœλ‹€.


GOOD πŸ˜‰

μ˜€λŠ˜μ€ DOM ꡬ쑰 μ „λ°˜κ³Ό 이λ₯Ό μˆ˜μ •ν•˜λŠ” 법을 μ΅ν˜”λ‹€.
μœ νš¨μ„± 검사 μŠ€ν”Œλ¦°νŠΈλ₯Ό μ§„ν–‰ν•˜λ©° 이벀트 ν•Έλ“€λŸ¬μ— λŒ€ν•œ 감을 μž‘μ•˜λ‹€.

BAD πŸ˜₯

νŽ˜μ–΄ν”„λ‘œκ·Έλž˜λ°ν•˜λ©΄μ„œ 혼자 ν­μ£Όν•΄μ„œ μ²˜λ¦¬ν•΄λ‚˜κ°”λ‹€.

TO DO πŸ”₯

  • DOM μˆ™λ ¨
  • 이벀트
  • κ³ μ°¨ν•¨μˆ˜
  • React

Retrospect 🧐

μ˜€λŠ˜μ€ μ „λ°˜μ μœΌλ‘œ νŽ˜μ–΄ν”„λ‘œκ·Έλž˜λ°μ— λŒ€ν•œ ν›„νšŒκ°€ 많이 λ‚¨λŠ”λ‹€.

이번 νŽ˜μ–΄λ‹˜λ„ μ•„μ£Ό μ  ν‹€ν•˜μ‹  λΆ„ μ΄μ—ˆλŠ”λ° μ˜κ²¬μ„ λ‚˜λˆŒ 사이도 없이 λ‚˜ 혼자 ν­μ£Όν•΄μ„œ 과제λ₯Ό μ²˜λ¦¬ν•΄ λ‚˜κ°”λ‹€.
쀑간 쀑간 λ„ˆλ¬΄ λ§‰λ‚˜κ°”λ‚˜ μ‹Άμ–΄μ„œ ν˜•μ‹μ μœΌλ‘œ μ˜κ²¬λ„ μ—¬μ­€λ΄€μ§€λ§Œ λ‚΄κ°€ λ‹€μ‹œ 생각해도 λ„ˆλ¬΄ ν˜•μ‹μ μ΄μ—ˆμ–΄μ„œ νŽ˜μ–΄ 뢄이 μ—„μ²­ 기운이 빠지셨을 것 κ°™λ‹€.

μ„œλ‘œ μ–»μ–΄κ°€λŠ”κ²Œ 많게 νŽ˜μ–΄ ν”„λ‘œκ·Έλž˜λ°ν•˜μž.

Reference πŸ™‡

https://developer.mozilla.org/

profile
μ–΄λ–»κ²Œ 이걸 ν’€μ–΄λ‚Ό 수 μžˆμ„κΉŒ

0개의 λŒ“κΈ€