정보실

웹학교

정보실

javascript 전문가처럼 DOM 사용

본문

DOM에 대한 두려움을 멈추고 그것을 최대한 활용하여 실제로 사랑하기 시작하는 방법.


2008 년에 전문 웹 개발자로 처음 일을 시작했을 때 HTML, CSS 및 PHP를 알고 있었습니다. 동시에 JavaScript라고 하는 것을 배우고 있었기 때문에 요소를 표시하고 숨기고 드롭 다운 메뉴와 같은 멋진 작업을 수행 할 수 있었습니다.

당시에는 주로 클라이언트 용 CMS 시스템을 만든 소규모 회사에서 일했으며 여러 파일 업 로더가 필요했습니다. 당시 네이티브 JavaScript로는 불가능했던 것입니다.


일부 검색 후 Flash와 MooTools라는 JavaScript 라이브러리를 기반으로 멋진 솔루션을 찾았습니다. MooTools는이 멋진 $ 기능을 사용하여 DOM 요소를 선택했으며 진행률 표시 줄 및 Ajax 요청과 같은 모듈과 함께 제공되었습니다. 몇 주 후에 나는 jQuery를 발견했고 날아 갔습니다.


더 이상 장황하고 복잡한 DOM 조작이 아니라 쉽고 체인 가능한 선택기가 있으며 유용한 플러그인도 많이 있습니다.


2019 년과 세계는 프레임 워크에 의해 지배됩니다. 지난 10 년 동안 웹 개발자로 시작한 경우 "원시"DOM에 거의 노출되지 않을 가능성이 있습니다. 필요하지 않을 수도 있습니다.


Angular 및 React와 같은 프레임 워크가 jQuery의 인기를 크게 떨어 뜨렸음에도 불구하고 여전히 전 세계 모든 웹 사이트의 약 74 %로 추정되는 엄청난 6,600 만 개의 웹 사이트에서 사용되고 있습니다.


jQuery의 유산은 매우 인상적이며 표준에 영향을 미친 방법의 좋은 예는 jQuery의 $ 함수를 모방 한 querySelector 및 querySelectorAll 메소드입니다.


아이러니하게도 이 두 가지 방법은 jQuery의 가장 많이 사용되는 기능인 DOM 요소의 쉬운 선택을 대체하여 jQuery가 인기를 잃은 가장 큰 원인 일 수 있습니다.


그러나 기본 DOM API는 장황합니다.


내 말은, $.와 document.querySelectorAll입니다.


이것이 바로 개발자가 네이티브 DOM API를 사용하지 못하게 하는 이유입니다. 그러나 실제로는 그럴 필요가 없습니다.


네이티브 DOM API는 훌륭하고 엄청나게 유용합니다. 그렇습니다. 장황하지만, 저수준 빌딩 블록이기 때문에 추상화를 구현하기 때문입니다. 추가 키 입력에 대해 걱정이 된다면 모든 최신 편집기와 IDE는 뛰어난 코드 완성을 제공합니다. 여기에 표시된 것처럼 가장 자주 사용하는 기능의 별칭을 지정할 수도 있습니다.


요소 선택 


단일 요소 


유효한 CSS 선택기를 사용하여 단일 요소를 선택하려면 다음을 사용하십시오.


document.querySelector(/* your selector */) 


여기에서 선택기를 사용할 수 있습니다.


document.querySelector('.foo') // class selector
document.querySelector('#foo') // id selector
document.querySelector('div') // tag selector
document.querySelector('[name="foo"]') // attribute selector
document.querySelector('div + p > span') // you go girl! 


일치하는 요소가 없으면 null을 반환합니다.


여러 요소 


여러 요소를 선택하려면 다음을 사용하십시오.


document.querySelectorAll('p') // selects all <p> elements 


document.querySelectorAll과 같은 방식으로 document.querySelectorAll을 사용할 수 있습니다. 유효한 CSS 선택기가 수행하는 유일한 차이점은 querySelector가 단일 요소를 리턴하는 반면 querySelectorAll은 찾은 요소를 포함하는 정적 NodeList를 리턴합니다. 요소가 없으면 빈 NodeList를 반환합니다.


NodeList는 배열과 같은 반복 가능한 객체이지만 실제로는 배열이 아니므로 동일한 방법이 없습니다. forEach를 실행할 수 있지만 map, reduce 또는 find와 같은 예제는 사용할 수 없습니다.


배열 메소드를 실행 해야 하는 경우 destructuring 또는 Array.from을 사용하여 배열로 간단히 전환 할 수 있습니다.


const arr = [...document.querySelectorAll('p')];orconst arr = Array.from(document.querySelectorAll('p'));arr.find(element => {...});  // .find() now works


querySelectorAll 메소드는 getElementsByTagName 및 getElementsByClassName과 같은 메소드와 다릅니다. 이러한 메소드는 라이브 콜렉션 인 HTMLCollection을 리턴하고 querySelectorAll은 정적 NodeList를 리턴합니다.


따라서 getElementsByTagName('p')을 수행하고 하나의 <p>가 문서에서 제거되면 반환 된 HTMLCollection에서도 제거됩니다.


그러나 querySelectorAll('p')을 수행하고 하나의 <p>가 문서에서 제거되면 반환 된 NodeList에 여전히 존재합니다.


또 다른 중요한 차이점은 HTMLCollection은 HTMLElements 만 포함 할 수 있고 NodeList는 모든 유형의 Node를 포함 할 수 있다는 것입니다.


상대 검색 


문서에서 반드시 querySelector(All)를 실행할 필요는 없습니다. HTMLElement에서 실행하여 상대 검색을 실행할 수 있습니다.


const div = document.querySelector('#container');
div.querySelectorAll('p') // finds all <p> tags in #container only 


그러나 여전히 장황합니다! 


추가 키 입력에 대해 여전히 우려되는 경우 두 가지 방법을 모두 별칭으로 지정할 수 있습니다.


const $ = document.querySelector.bind(document);
$('#container');
const $$ = document.querySelectorAll.bind(document);
$$('p');


DOM 트리 올라 가기 


DOM 요소를 선택하기 위해 CSS 선택기를 사용한다는 것은 DOM 트리 아래로만 이동할 수 있다는 것을 의미합니다. 부모를 선택하기 위해 트리를 올라갈 CSS 선택기는 없습니다.


그러나 가장 가까운 CSS 메소드를 사용하는 closest() 메소드로 DOM 트리를 이동할 수 있습니다.


document.querySelector('p').closest('div'); 


document.querySelector( 'p')가 선택한 단락의 가장 가까운 부모 <div> 요소를 찾습니다. 이 호출을 연결하여 트리를 더 진행할 수 있습니다.


document.querySelector('p').closest('div').closest('.content'); 


요소 추가 


DOM 트리에 하나 이상의 요소를 추가하는 코드는 빠르게 장황하게 표시되는 것으로 유명합니다. 페이지에 다음 링크를 추가한다고 가정 해 보겠습니다.


<a href="/home" class="active">Home</a> 


다음을 수행해야 합니다.


const link = document.createElement('a');
a.setAttribute('href', '/home');
a.className = 'active';
a.textContent = 'Home';
document.body.appendChild(link);


이제 10 가지 요소에 대해 이 작업을 수행해야 한다고 상상해보십시오.


적어도 jQuery를 사용하면 다음을 수행 할 수 있습니다.


$('body').append('<a href="/home" class="active">Home</a>'); 


다음과 같은 고유 항목이 있습니다.


document.body.insertAdjacentHTML('beforeend',
'<a href="/home" class="active">Home</a>'); 


insertAdjacentHTML 메소드를 사용하면 첫 번째 매개 변수로 표시되는 임의의 유효한 HTML 문자열을 DOM에 네 위치에 삽입 할 수 있습니다.


  • 'before begin': 요소 앞에
  • 'afterbegin': 첫 번째 자식 이전의 요소 내부
  • 'beforeend': 마지막 자식 이후 요소 내부
  • 'afterend': 요소 뒤


<!-- beforebegin -->
<p>
<!-- afterbegin -->
foo
<!-- beforeend -->
</p>
<!-- afterend --> 



또한 새 요소를 훨씬 쉽게 삽입 할 정확한 지점을 지정합니다. 이 <p> 바로 앞에 <a>를 삽입한다고 가정하십시오. insertAdjacentHTML이 없으면 다음을 수행해야 합니다.


const link = document.createElement('a');
const p = document.querySelector('p');
p.parentNode.insertBefore(link, p);


이제 당신은 할 수 있습니다 :


const p = document.querySelector('p');p.insertAdjacentHTML('beforebegin', '<a></a>');


DOM 요소를 삽입하는 동등한 방법도 있습니다.


const link = document.createElement('a');
const p = document.querySelector('p');
p.insertAdjacentElement('beforebegin', link);


그리고 텍스트 :


p.insertAdjacentText('afterbegin', 'foo'); 


요소 이동 


insertAdjacentElement 메소드를 사용하여 동일한 문서에서 기존 요소를 이동할 수도 있습니다. insertAdjacentElement로 삽입 된 요소가 이미 문서의 일부인 경우 단순히 이동됩니다.


이 HTML이 있는 경우 :


<div class="first">
<h1>Title</h1>
</div>
<div class="second">
<h2>Subtitle</h2>
</div>


<h2>는 <h1> 뒤에 삽입됩니다.


const h1 = document.querySelector('h1');
const h2 = document.querySelector('h2');
h1.insertAdjacentElement('afterend', h2);


복사되지 않고 단순히 이동됩니다.


<div class="first">
<h1>Title</h1>
<h2>Subtitle</h2>
</div>
<div class="second">

</div>


요소 교체 


replaceWith 메소드를 사용하여 DOM 요소를 다른 DOM 요소로 바꿀 수 있습니다.


someElement.replaceWith(otherElement); 


대체 된 요소는 document.createElement로 작성된 새 요소이거나 이미 동일한 문서의 일부인 요소 일 수 있습니다 (이 경우 복사되지 않고 다시 이동 됨).


<div class="first">
<h1>Title</h1>
</div>
<div class="second">
<h2>Subtitle</h2>
</div>
const h1 = document.querySelector('h1');
const h2 = document.querySelector('h2');
h1.replaceWith(h2);// result:<div class="first">
<h2>Subtitle</h2>
</div>
<div class="second">

</div>


요소 제거 


remove 메소드를 호출하십시오.


const container = document.querySelector('#container');
container.remove(); // hasta la vista, baby 


예전 방식보다 훨씬 낫습니다.


const container = document.querySelector('#container');
container.parentNode.removeChild(container); 


원시 HTML에서 요소 만들기 


insertAdjacentHTML 메소드를 사용하면 원시 HTML을 문서에 삽입 할 수 있지만 원시 HTML에서 요소를 작성하여 나중에 사용하려면 어떻게 해야 합니까?

이를 위해 DomParser 객체와 그 메소드 parseFromString을 사용할 수 있습니다. DomParser는 HTML 또는 XML 소스 코드를 DOM 문서로 구문 분석하는 기능을 제공합니다. parseFromString 메소드를 사용하여 요소가 하나만 있는 문서를 작성하고 해당 요소만 리턴 합니다.


const createElement = domString => new DOMParser().parseFromString(domString, 'text/html').body.firstChild;const a = createElement('<a href="/home" class="active">Home</a>');


DOM 검사 


표준 DOM API는 DOM을 검사하는 편리한 방법도 제공합니다. 예를 들어, 일치는 요소가 특정 선택기와 일치하는지 여부를 결정합니다.


<p class="foo">Hello world</p>const p = document.querySelector('p');p.matches('p');     // true
p.matches('.foo'); // true
p.matches('.bar'); // false, does not have class "bar"


contains 메소드를 사용하여 요소가 다른 요소의 자식인지 확인할 수도 있습니다.


<div class="container">
<h1 class="title">Foo</h1>
</div>
<h2 class="subtitle">Bar</h2>const container = document.querySelector('.container');
const h1 = document.querySelector('h1');
const h2 = document.querySelector('h2');
container.contains(h1); // true
container.contains(h2); // false


compareDocumentPosition 메소드를 사용하면 요소에 대한 자세한 정보를 얻을 수 있습니다. 이 방법을 사용하면 한 요소가 다른 요소보다 앞에 오거나 뒤에 오는지 또는 이러한 요소 중 하나에 다른 요소가 포함되어 있는지 확인할 수 있습니다. 비교 된 요소 간의 관계를 나타내는 정수를 반환합니다.


다음은 이전 예와 동일한 요소를 가진 예입니다.


<div class="container">
<h1 class="title">Foo</h1>
</div>
<h2 class="subtitle">Bar</h2>const container = document.querySelector('.container');
const h1 = document.querySelector('h1');
const h2 = document.querySelector('h2');
// 20: h1 is contained by container and follows container
container.compareDocumentPosition(h1);
// 10: container contains h1 and precedes it
h1.compareDocumentPosition(container);
// 4: h2 follows h1
h1.compareDocumentPosition(h2);
// 2: h1 precedes h2
h2.compareDocumentPosition(h1);


compareDocumentPosition에서 리턴 된 값은 비트가 이 메소드에 제공된 인수를 기준으로 노드 간의 관계를 나타내는 정수입니다.


따라서 node.compareDocumentPostion (otherNode) 구문을 고려하면 반환 값의 의미는 다음과 같습니다.


1 : 노드가 동일한 문서의 일부가 아닙니다

2 : otherNode가 node보다 우선

4 : otherNode가 노드를 따릅니다.

8 : otherNode는 노드를 포함

16 : otherNode는 노드에 포함됩니다


위의 예에서 container.compareDocumenPosition(h1)은 h1이 컨테이너에 포함되어 있으므로 16을 예상 할 수 있는 20을 반환하는 비트 중 하나 이상이 설정 될 수 있습니다. 그러나 h1은 container(4)를 따르므로 결과 값은 16 + 4 = 20입니다.



MutationObserver 인터페이스를 통해 모든 DOM 노드의 변경 사항을 관찰 할 수 있습니다. 여기에는 텍스트 변경, 관찰 된 노드에 추가 또는 제거되는 노드 또는 노드 속성 변경이 포함됩니다.


MutationObserver는 DOM 요소 및 해당 하위 노드에서 발생하는 거의 모든 변경 사항을 관찰 할 수 있는 매우 강력한 API입니다.


새로운 MutationObserver는 생성자를 콜백 함수로 호출하여 생성됩니다. 이 콜백은 관찰 된 노드에서 변경이 발생할 때마다 호출됩니다.


const observer = new MutationObserver(callback); 


요소를 관찰하려면 첫 번째 매개 변수로 관찰 할 노드와 두 번째 매개 변수로 옵션이 있는 객체를 사용하여 관찰자의 관찰 메소드를 호출해야 합니다.


const target = document.querySelector('#container');
const observer = new MutationObserver(callback);
observer.observe(target, options);



관찰이 호출 될 때까지 대상의 관찰이 시작되지 않습니다.


이 옵션 객체는 다음 키를 사용합니다.


  • attributes : true로 설정하면 노드 속성에 대한 변경 사항이 감시됩니다.
  • attributeFilter : 감시 할 속성 이름의 배열. 속성이 true이고 이것이 설정되지 않은 경우 노드의 모든 속성에 대한 변경 사항이 감시됩니다
  • attributeOldValue : true로 설정하면 변경이 발생할 때마다 속성의 이전 값이 기록됩니다
  • characterData : true로 설정하면 텍스트 노드의 텍스트에 대한 변경 사항이 기록되므로 HTMLElements가 아닌 Textnode에서만 작동합니다. 이것이 작동하려면 관찰되는 노드가 텍스트 노드이거나 관찰자가 HTMLElement를 모니터링 하는 경우 하위 노드의 변경 사항도 모니터링 하려면 옵션 하위 트리를 true로 설정해야 합니다.
  • characterDataOldValue : true로 설정하면 변경 될 때마다 문자화 된 데이터의 이전 값이 기록됩니다.
  • subtree : 관찰중인 요소의 하위 노드에 대한 변경 사항도 관찰하려면 true로 설정하십시오.
  • childList : 하위 노드 추가 및 제거를 위한 요소를 모니터 하려면 true로 설정하십시오. 하위 트리가 true로 설정되면 하위 노드의 추가 및 제거를 위해 하위 요소도 감시됩니다.

observe를 호출하여 요소 관찰이 시작되면 MutationObserver 생성자에 전달 된 콜백은 발생한 변경 사항과 두 번째 매개 변수로 호출 된 관찰자를 설명하는 MutationRecord 객체의 배열로 호출됩니다.


MutationRecord에는 다음과 같은 속성이 있습니다.

  • type : 변경 유형 (속성, characterData 또는 childList)
  • target : 변경된 요소 (속성, 문자 데이터 또는 자식 요소)
  • addedNodes : 추가 된 노드 목록 또는 추가 된 경우 비어있는 NodeList
  • removedNodes : 제거 된 노드 목록 또는 제거되지 않은 경우 비어있는 NodeList
  • attributeName : 변경된 속성의 이름 또는 속성이 변경되지 않은 경우 null
  • previousSibling : 추가 또는 제거 된 노드의 이전 형제 또는 null
  • nextSibling : 추가 또는 제거 된 노드의 다음 형제 또는 null


속성과 자식 노드의 변경 사항을 관찰하고 싶다고 가정 해 봅시다.


const target = document.querySelector('#container');
const callback = (mutations, observer) => {
mutations.forEach(mutation => {
switch (mutation.type) {
case 'attributes':
// the name of the changed attribute is in
// mutation.attributeName
// and its old value is in mutation.oldValue
// the current value can be retrieved with
// target.getAttribute(mutation.attributeName)
break;
case 'childList':
// any added nodes are in mutation.addedNodes
// any removed nodes are in mutation.removedNodes
break;
}
});
};
const observer = new MutationObserver(callback);observer.observe(target, {
attributes: true,
attributeFilter: ['foo'], // only observe attribute 'foo'
attributeOldValue: true,
childList: true
});


대상 관찰이 끝나면 관찰자의 연결을 끊을 수 있으며 필요한 경우 takeRecords 메소드를 호출하여 콜백에 아직 전달되지 않은 보류 중인 돌연변이를 가져옵니다.


const mutations = observer.takeRecords();
callback(mutations);
observer.disconnect(); 


DOM을 두려워하지 마십시오 


DOM API는 비록 장황한 API 임에도 불구하고 매우 강력하고 다목적입니다. 개발자가 추상화를 구축 할 수 있도록 낮은 수준의 빌딩 블록을 제공하기 위한 것이므로 명확하고 명확한 API를 제공하기 위해서는 자세한 설명이 필요합니다.


추가 키 입력으로 인해 키를 최대한 활용할 수 있는 것을 두려워해서는 안됩니다.


DOM은 매일 사용하기 때문에 모든 JavaScript 개발자에게 필수적인 지식입니다. 두려워하지 말고 최대한 활용하십시오.






  • 트위터로 보내기
  • 페이스북으로 보내기
  • 구글플러스로 보내기
  • 카카오톡으로 보내기

페이지 정보

조회 13회 ]  작성일19-09-07 22:13
DOM

웹학교