Romantic Developer : )

(Web JavaScript) 웹 자바스크립트 DOM (9) Node API 본문

Romantic Developer/Web JavaScript

(Web JavaScript) 웹 자바스크립트 DOM (9) Node API

Romantic_Developer 2018. 8. 7. 18:33

안녕하세요, 영감을 주는 개발자 방민방민입니다.


오늘은 자바스크립트에서 Node 가 가지고 있는 다양한 기능을 활용할 수 있는 API 에 대하여 학습해 보고자 합니다. 노드는 웹 프로그램에서 엘리먼트들이 가지는 관계를 의미합니다. 자주 사용되는 이러한 node API 의 종류는 아래와 같습니다.


(출처 : 생활코딩)


  • Node.childNodes
    자식노드들을 유사배열에 담아서 리턴한다.
  • Node.firstChild
    첫번째 자식노드
  • Node.lastChild
    마지막 자식노드
  • Node.nextSibling
    다음 형제 노드
  • Node.previousSibling
    이전 형제 노드




1.Node 관계 API


아래의 코드는 nodeAPI 를 활용하여 문서를 탐색하는것을 보여줍니다.

<body id="start">
<ul>
    <li><a href="./532">html</a></li> 
    <li><a href="./533">css</a></li>
    <li><a href="./534">JavaScript</a>
        <ul>
            <li><a href="./535">JavaScript Core</a></li>
            <li><a href="./536">DOM</a></li>
            <li><a href="./537">BOM</a></li>
        </ul>
    </li>
</ul>
<script>
var s = document.getElementById('start');
console.log(1, s.firstChild); // #text
var ul = s.firstChild.nextSibling
console.log(2, ul); // ul
console.log(3, ul.nextSibling); // #text
console.log(4, ul.nextSibling.nextSibling); // script
console.log(5, ul.childNodes); //text, li, text, li, text, li, text
console.log(6, ul.childNodes[1]); // li(html)
console.log(7, ul.parentNode); // body
</script>
</body>

노드간의 관계를 파악하는 것은 쉽지 않습니다.

일단 모든 엘리먼트들은 id 값이 start 인 body 태그에 소속되어 있습니다.


script 코드를 보겠습니다.

변수 s 에 id값이 start 인 body 태그를 저장하였습니다. 이후 콘솔을 통해 어떤 노드가 출력되는지 확인하시기 바랍니다.


- s.firstChild 는 첫번째 자식인 text 를 출력합니다. 이는 <ul></ul>일 것 같지만, 그렇지 않고 #text 를 출력합니다. 그 이유는 <body> 태그와 <ul> 태그 사이에 있는 줄바꿈 text가 하나의 차일드로 취급되기 때문입니다.


- 변수 ul에 s.firstChild(#text).nextSibling를 담습니다. 같은 레벨을 갖는 다음 태그를 의미합니다. 여기에서는 <ul> 태그가 됩니다.


- ul.nextSibling 은 </ul> 뒤의 <script>가 될 것같지만, 두 태그 사이에 있는 줄바꿈 문자가 되기 때문에 또다시 #text 가 됩니다. 


- ul.nextSibling.nextSibling 가 드디어 <script></script> 태그가 됩니다.


- ul.childNodes 는 ul에 속해있는 <li>태그 들과, 줄바꿈 문자가 됩니다. 그러므로 li 와 text 를 반복적으로 출력합니다.


- ul.childNodes[1]  ul 에 속에있는 두번째 노드인 <li>html</li>가 됩니다.([0] 은 #text 일 것입니다.


- ul.parentNode 는 ul 의 부모 노드임으로 body 를 출력합니다.



2. Node 종류 API


아래의 코드를 먼저 실행해 보겠습니다. 

<!DOCTYPE>
<html>
	<head>
		
	</head>
	<body id="start">
		<ul>
		    <li><a href="./532">html</a></li> 
		    <li><a href="./533">css</a></li>
		    <li><a href="./534">JavaScript</a>
		        <ul>
		            <li><a href="./535">JavaScript Core</a></li>
		            <li><a href="./536">DOM</a></li>
		            <li><a href="./537">BOM</a></li>
		        </ul>
		    </li>
		</ul>
		<script>
			for(var name in Node){
			   console.log(name, Node[name]);
			}
		</script>
	</body>

</html>


이는 아래의 내용을 출력합니다.

jsPractice.html:20 ELEMENT_NODE 1
jsPractice.html:20 ATTRIBUTE_NODE 2
jsPractice.html:20 TEXT_NODE 3
jsPractice.html:20 CDATA_SECTION_NODE 4
jsPractice.html:20 ENTITY_REFERENCE_NODE 5
jsPractice.html:20 ENTITY_NODE 6
jsPractice.html:20 PROCESSING_INSTRUCTION_NODE 7
jsPractice.html:20 COMMENT_NODE 8
jsPractice.html:20 DOCUMENT_NODE 9
jsPractice.html:20 DOCUMENT_TYPE_NODE 10
jsPractice.html:20 DOCUMENT_FRAGMENT_NODE 11
jsPractice.html:20 NOTATION_NODE 12
jsPractice.html:20 DOCUMENT_POSITION_DISCONNECTED 1
jsPractice.html:20 DOCUMENT_POSITION_PRECEDING 2
jsPractice.html:20 DOCUMENT_POSITION_FOLLOWING 4
jsPractice.html:20 DOCUMENT_POSITION_CONTAINS 8
jsPractice.html:20 DOCUMENT_POSITION_CONTAINED_BY 16
jsPractice.html:20 DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC 32

이를 통해 각각의 노드들이 어떤 nodeName(이름), 과 어떤 nodeType(번호) 를 가지는지 조회할 수 있습니다.

예를 들어 ELEMENT 노드는 1번 노드타입을 가지고 있습니다. 우리가 기존에 배웠던 atrribute , text, document 등등의 노드들이 어떤 타입값을 갖고 있는지 확인 할 수 있습니다.


아래의 코드는 노드 네임을 활용한 재귀함수의 좋은 예입니다. 

<!DOCTYPE>
<html>
	<head>
		
	</head>
	<body id="start">
		<ul>
		    <li><a href="./532">html</a></li> 
		    <li><a href="./533">css</a></li>
		    <li><a href="./534">JavaScript</a>
		        <ul>
		            <li><a href="./535">JavaScript Core</a></li>
		            <li><a href="./536">DOM</a></li>
		            <li><a href="./537">BOM</a></li>
		        </ul>
		    </li>
		</ul>
		<script>
			function traverse(target, callback){//익명함수 callback을 인자로
				if(target.nodeType === Node.ELEMENT_NODE){//1 은 element node 를 나타냄
					callback(target);
					var c = target.childNodes;
					for(var i = 0; i<c.length; i++){
						traverse(c[i],callback);
					}
				}
			}
			traverse(document.getElementById('start'),function(elem){console.log(elem);
			});

		</script>
	</body>

</html>

이는 아래의 결과를 출력합니다.

즉 재귀함수를 통해 각각의 노드에 들려 그 내용을 출력하는 예제입니다.

재귀에 대한 내용은 프로그래밍적 부분이므로 차후에 다른 포스팅에서 설명하겠습니다.

여기에서는



3. Node 변경 API


먼저 노드를 추가하고 제거하는 등의 활용이 Node 변경 API 를 통하여 가능합니다.

노드의 추가와 관련된 API 는 아래와 같습니다. (출처 : 생활코딩)


노드를 추가하기 위해서는 추가할 엘리먼트를 생성해야 하는데 이것은 document 객체의 기능이다. 아래 API는 노드를 생성하는 API이다.



아래의 코드를 보도록 하겠습니다.

<!DOCTYPE>
<html>
	<head>
		
	</head>
	<body>
		<ul id="target">
			<li>HTML</li>
			<li>CSS</li>
		</ul>

		<input type="button" onclick="callAppendChild();" value="appendChild()"/>
		<input type="button" onclick="callInsertBefore();" value="callInsertBefor()"/>

		<script>
			function callAppendChild(){
				var target = document.getElementById('target’);//target 노드를 저장
				var li = document.createElement('li’);//li엘리먼트 생성
				var text = document.createTextNode('JavaScript’);//text 노드 생성
				li.appendChild(text);//li 엘리먼트에 text 노드를 차일드로 추가
				target.appendChild(li);//li엘리먼트를 target 의 차일드노드로 추가
			}

			function callInsertBefore(){
				var target = document.getElementById('target’);//target 노드를 저장
				var li = document.createElement('li’);//li 엘리먼트를 생성
				var text = document.createTextNode('JQuery’);//text 노드 생성
				li.appendChild(text);//text 노드를 li의 차일드로 추가
				target.insertBefore(li,target.firstChild);//li 엘리먼트를 target 의 first Child 로 추가
			}
		</script>
	</body>
</html>


이는 아래의 결과를 출력합니다.



이와 같이 각각의 노드를 생성하여 appendChild() 의 경우 추가하고자 하는 노드를 가장 마지막 child로 더해주고 intertBefore()의 경우에는 추가하고자 하는 노드를 추가하는 노드를 인자로 주는 노드의 이전 노드로 저장합니다. 이 경우에는 (li, target.firstChild)를 주었기 때문에, 첫번째 자식노드인 HTML 이전에 추가되는것을 확인할 수 있습니다.


노드를 제거하고 바꾸는 코드도 존재합니다.(출처: 생활코딩)


노드 제거를 위해서는 아래 API를 사용한다. 이 때 메소드는 삭제 대상의 부모 노드 객체의 것을 실행해야 한다는 점에 유의하자.


노드의 제거는 위에서 말한것과 같이 반드시 부모 노드의 객체에서 실행해야합니다.

아래의 코드를 보겠습니다

<html>

<!DOCTYPE>
<html>
	<head>
		
	</head>
	<body>
		<ul id="target">
			<li>HTML</li>
			<li>CSS</li>
			<li>JavaScript</li>
		</ul>

		<input type="button" onclick = "callRemoveChild()" value="callRemoveChild">

		<script>
			function callRemoveChild(){
				var target = document.getElementById('target');
				target.parentNode.removeChild(target);//지우고자 하는 target 의 부모 노드에서 실행
			}
		</script>
	</body>
</html>

이 코드를 통해 버튼을 누르면, 전체 id 값이 target 을 갖는 해당 ul 엘리먼트 전부가 삭제됩니다. 그러므로 하위 li 엘리먼트인 HTML, CSS , JavaScript 가 모두 삭제됩니다.

이때, 삭제되는 코드를 보면, target.parentNode.removeChild(target) 로, target으로 하는 엘리먼트 노드의 부모 노드를 한번 호출하고, 그 부모노드에서 자식노드를 삭제할 수 있었습니다.


이는 많은 비판을 받고 있는 부분이기도 합니다. 지우고자 하는 노드를 바로 삭제할 수 없고, 부모노드를 알아야만 하는 구조이기 때문입니다. 그러나 이것은 설계상의 규약이기때문에 JS 를 공부하는 우리로써는 받아들여야 할 부분입니다.


아래는 노드를 변경하는 예제입니다.

<!DOCTYPE>
<html>
	<head>
		
	</head>
	<body>
		<ul>
			<li>HTML</li>
			<li>CSS</li>
			<li id="target">JavaScript</li>
		</ul>

		<input type="button" onclick = "callReplaceChild()" value="callReplaceChild">

		<script>
			function callReplaceChild(){
				var a = document.createElement('a');//a 태그 엘리먼트를 생성
				a.setAttribute('href','http://aaa.com');//a 태그의 속성중 href 를 변경
				a.appendChild(document.createTextNode('web browser JavaScirpt'));//a 태그의 자식 택스트를 생성
				var target = document.getElementById('target');//target 엘리먼트 지정
				target.replaceChild(a,target.firstChild);//target 엘리먼트의 첫번째 자식을 변경
			}
		</script>
	</body>
</html>

주석에서 볼 수 있는 바와 같이 변경하고자 하는 앨리먼트의 부모노드에서 replaceChild(newNode,oldNode)를 해주면 해당 자식 엘리먼트가 변하는 것을 알 수 있습니다.



오늘은 Node API 라는 다소 어려운 내용을 정말 긴 포스팅으로 알아보았습니다.


API 를 통해 어떤 노드를 조회하고, 변경, 삭제 등을 하는 API 는 웹사이트의 프로그래밍 적인 동작에 있어서 매우 중요하다고 할 수 있습니다.


지금까지 영감을 주는 개발자 방민방민이었습니다! :)