-
[JS/DOM] 자바스크립트, 문서 객체 모델(Document Object Model) 탐색하기(1) - DOM TraversingFrontend 2019. 6. 10. 15:33
https://im-developer.tistory.com/100
지난번 글에서 DOM이 무엇인지에 대해서 다루었다.
오늘은 DOM에 어떻게 접근하고 탐색하는 지 본격적으로 다뤄보려고 한다.
HTML Element에 접근하기
지난 번 글에서 우리는 Element(요소)가 HTML 문서 상의 태그를 객체화시킨 것이라는 것을 알았다. 이제 그 객체화된 요소들에 어떻게 접근하는지 정리해보려고 한다. 일단 노드에 접근하기 위해서는 가장 최상위 노드인 document node를 통해야만 한다.
1) Tag의 id로 접근하기
document.getElementById('id');
<div id="container"> div입니다. </div> <script> var ele = document.getElementById('container'); console.log(ele); </script>
하나의 웹 문서에서 id는 유일성을 가져야한다. 무슨 말이냐 하면 위 코드에서 div에 container라는 id를 부여했다면, 이 웹 페이지 상에서 container라는 id를 가진 요소는 저 div가 유일해야 한다는 뜻이다. 그렇기 때문에 id로 element 요소를 찾으면 해당 id를 가진 태그 하나만 가져올 수 있다.
만약에 해당 id를 가진 요소가 없다면 변수 ele에는 null값이 리턴된다.
콘솔 창에 출력해보면 div#container가 출력되고 해당 요소에 마우스를 가져다대면 해당 요소가 웹 페이지 상에 표시된다.
2) Tag name으로 접근하기
document.getElementsByTagName("tagname");
<ul> <li>1번입니다.</li> <li>2번입니다.</li> <li>3번입니다.</li> </ul> <script> var ele = document.getElementsByTagName('li'); console.log(ele); </script>
위 코드는 태그 이름으로 dom을 가져온다. 해당 이름을 가진 모든 태그를 가져올 수 있으며 가져온 태그들은 유사 배열의 형태로 반환된다.
3) Class name으로 접근하기
document.getElementsByClassName("class-name");
<ul> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li>3번입니다.</li> </ul> <script> var ele = document.getElementsByClassName('list'); console.log(ele); </script>
class명으로 dom을 가져오는 방법이다. 해당 클래스를 가진 모든 요소를 탐색하여 유사 배열의 형태로 반환한다.
4) CSS selector로 접근하기
document.querySelector("#id"); → 단일 요소
document.querySelectorAll("tagname"); → 복수 요소
document.querySelectorAll(".class-name");
<ul id="myList"> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li>3번입니다.</li> </ul> <script> var list = document.querySelector('#myList'); var li = document.querySelectorAll('li'); var li2 = document.querySelectorAll('.list') console.log(list); console.log(li); console.log(li2); </script>
내가 제일 좋아하는 방법이다. 위의 방법들보다 훨씬 간편하게 css selector를 그대로 사용해서 태그에 접근할 수 있다. 위의 예제에서처럼 아이디는 '#id', 클래스 이름은 '.class-name'과 같은 형태로 가져올 수 있으며 그 이외의 다양한 css selector를 모두 사용할 수 있다.
<ul id="myList"> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li> <input type="checkbox" name="chk_box"> </li> </ul> <script> var chk = document.querySelector('input[name="chk_box"]'); console.log(chk); </script>
querySelector를 사용하면 이렇게 태그의 속성값으로도 손쉽게 접근할 수 있게 된다.
querySelector()를 사용하면 위에서부터 해당 속성을 가진 dom 하나만을 가져온다는 사실을 잊지 말자. querySelector('.class-name')이라고 하면 해당 클래스명을 가진 요소가 여러 개 있을지라도 맨 첫 번째 요소 하나만을 가져온다. 그러므로 복수의 요소를 모두 가져오고 싶다면 querySelectorAll()을 사용해야 한다.
(익스플로러 8 이상에서 모두 지원한다.)
DOM Traversing : 자식 노드 탐색
1) 부모 Element에서 요소 접근하기
<ul id="myList"> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li> <input type="checkbox" name="chk_box"> </li> </ul> <script> var list = document.querySelector('#myList'); var li = document.querySelectorAll('li'); var li2 = document.querySelectorAll('.list'); console.log(list); console.log(li); console.log(li2); </script>
이 코드를 다시 살펴보자. 우리가 document.querySelector()라고 쓰고 코드를 실행하면
브라우저는 document라는 최상위 노드에서부터 하위 노드를 차례로 읽으면서 원하는 요소를 탐색하기 시작한다.
ul#myList를 찾을 때도, 그 아래 자식 요소인 li 태그를 찾을 때도 모두 document에서 탐색한다. 여기서 li 태그는 #myList의 자식 요소이므로 #myList를 찾은 이후에 다시 li 태그를 document에서부터 탐색하는 것은 매우 비효율적으로 보인다.
<ul id="myList"> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li> <input type="checkbox" name="chk_box"> </li> </ul> <script> var list = document.querySelector('#myList'); var li = list.querySelectorAll('li'); var li2 = list.querySelectorAll('.list'); console.log(list); console.log(li); console.log(li2); </script>
따라서 우리는 위와 같이 부모 요소에서 li 태그를 탐색하도록 바꿔줄 수 있다.
2) Children : 모든 자식 요소에 접근하기
<ul id="myList"> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li> <input type="checkbox" name="chk_box"> </li> </ul> <script> var list = document.querySelector('#myList'); var li = list.children; console.log(list); console.log(li); </script>
만약에 ul#myList에서 특정 요소만 가져오는 것이 아니라 모든 자식 요소를 가져오고 싶다면
children이란 속성을 사용할 수 있다. children은 해당 dom의 자식 element node를 모두 가져와 유사 배열의 형태로 반환한다. 만약에 자식 요소가 하나도 없다면 빈 배열을 반환할 것이다.
위의 element.querySelectorAll()과 다른 점은 children으로 가져 온 HTMLCollection은 live라서 실시간으로 노드의 상태를 반영한다는 점이다. 이 점을 매우 유의해서 조작해야한다.
(이 속성은 익스플로러 9 이상부터 지원한다.)
3) firstElementChild : 첫 번째 자식 요소에 접근하기
<ul id="myList"> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li>3번입니다.</li> </ul> <script> var list = document.querySelector('#myList'); var li = list.firstElementChild; console.log(list); console.log(li); </script>
해당 dom의 첫 번째 자식 element node를 탐색한다.
(이 속성은 익스플로러 9 이상부터 지원한다.)
4) lastElementChild : 마지막 자식 요소에 접근하기
<ul id="myList"> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li>3번입니다.</li> </ul> <script> var list = document.querySelector('#myList'); var li = list.lastElementChild; console.log(list); console.log(li); </script>
해당 dom의 마지막 자식 element node를 탐색한다.
(이 속성은 익스플로러 9 이상부터 지원한다.)
5) childNodes속성에 대하여
자식 노드를 탐색할 때는 childNodes를 사용할 수도 있다. childNodes는 주어진 요소의 자식 노드 모음(collection)을 반환한다. 다만 childNodes는 element node만 반환하는 것이 아니라 해당 요소 하위에 존재하는 모든 text node와 comment node를 포함하여 반환한다. 심지어 태그 사이의 공백이나 띄어쓰기도 text node로 인지하여 반환한다.
<ul id="myList"> <!-- 주석 --> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li>3번입니다.</li> </ul> <script> var list = document.querySelector('#myList'); var li = list.childNodes; console.log(li); for (var i = 0; i < li.length; i++) { console.log(li[i].nodeType); } </script>
반환된 모든 노드가 무슨 형식인지 알고 싶으면 nodeType 속성을 사용하면 된다.
text는 3, comment는 8, tag는 1이란 숫자로 대치된다.
<ul id="myList"> <!-- 주석 --> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li>3번입니다.</li> </ul> <script> var list = document.querySelector('#myList'); var li = list.childNodes; console.log(li); for (var i = 0; i < li.length; i++) { if (li[i].nodeType === 1) { console.log(li[i]) } } </script>
이렇게 childNodes 중에서 nodeType이 1인 요소만 탐색하는 방법을 사용하여
element node만 분리해낼 수 있을 것이다.
그러나 위의 방법은 매우 번거롭기 때문에 children 속성을 사용하는 것이 좋다.
Constant Value Description Node.ELEMENT_NODE 1 An Element node like <p> or <div> Node.TEXT_NODE 3 The actual Text inside an Element or Attr. Node.CDATA_SECTION_NODE 4 A CDATASection, such as <!CDATA[[ … ]]>. Node.PROCESSING_INSTRUCTION_NODE 7 A ProcessingInstruction of an XML document, such as <?xml-stylesheet … ?>. Node.COMMENT_NODE 8 A Comment node, such as <!-- … -->. Node.DOCUMENT_NODE 9 A Document node. Node.DOCUMENT_TYPE_NODE 10 A DocumentType node, such as <!DOCTYPE html>. Node.DOCUMENT_FRAGMENT_NODE 11 A DocumentFragment node.
DOM Traversing : 형제 노드 탐색
1) previousElementSibling / nextElementSibling
<ul id="myList"> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li>3번입니다.</li> </ul> <script> var list = document.querySelector('#myList'); var li = list.firstElementChild.nextElementSibling; var li2 = list.lastElementChild.previousElementSibling; console.log(li); console.log(li2); </script>
위 속성은 해당 dom의 형제 element node를 탐색한다.
previousElementSibling은 해당 요소 바로 전 요소를 가져오고,
nextElementSibling은 해당 요소 바로 다음 요소를 가져온다.
만약 이 전/ 혹은 다음 요소가 존재하지 않는 경우 null값을 반환한다.
(이 속성은 익스플로러 9 이상부터 지원한다.)
2) previousSibling / nextSibling
아까 childNodes와 동일하게 element node만 탐색하는 것이 아니라 text node를 포함하므로 사용하기 번거롭기 때문에 잘 사용하지 않는 속성이다.
DOM Traversing : 부모 노드 탐색
1) parentNode
<ul id="myList"> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li>3번입니다.</li> </ul> <script> var li = document.querySelector('li'); var list = li.parentNode; console.log(list); </script>
parentNode를 통해 해당 요소의 부모 요소에 접근할 수 있다.
function getParents (ele) { console.log(ele.parentNode); if (ele.parentNode) { getParents(ele.parentNode); } } var li = document.querySelector('li'); getParents(li);
parentNode를 여러 번 사용하여 상위로 계속 거슬러올라갈 수 있다.
<ul> - <body> - <html> - #document
최종적으로 document node까지 타고 올라가고 그 이상은 null값이 출력된다.
2) parentElement
<ul id="myList"> <li class="list">1번입니다.</li> <li class="list">2번입니다.</li> <li>3번입니다.</li> </ul> <script> var li = document.querySelector('li'); var list = li.parentElement; console.log(list); </script>
해당 요소의 부모 element node를 탐색한다.
function getParents (ele) { console.log(ele.parentElement); if (ele.parentElement) { getParents(ele.parentElement); } } var li = document.querySelector('li'); getParents (li);
parentNode와 똑같은 역할을 하지만 element 요소만 탐색하므로 <html>까지 거슬러올라간 이후에
그 상위 요소에 접근하려고 하면 null값을 출력한다.
반응형'Frontend' 카테고리의 다른 글
[JS/DOM] 자바스크립트, 돔 조작 시 주의할 점 (Live Collection vs Static Collection) (2041) 2019.06.16 [JS/클로져] 자바스크립트, 클로저를 이용한 버튼 클릭 시 나타나는 툴팁 (734) 2019.06.12 [JS/클로져] 자바스크립트의 Lexical scoping과 Closure (2) (1808) 2019.06.10 [JS/Array] slice()와 splice()의 차이점 (2045) 2019.06.07 [JS/Recursion] 자바스크립트, 재귀함수에 대하여 (Recursion) (2168) 2019.06.05 COMMENT