[JavaScript] Node 객체


Node 객체


1. 소개

Node 객체는 DOM에서 시조와 같은 역할을 한다. 다시 말해서 모든 DOM 객체는 Node 객체를 상속 받는다. Node 객체의 위상을 그림으로 나타내면 아래와 같다. 

모든 엘리먼트, 텍스트, 주석등 HTML코드의 모든 문서의 구성요소들의 객체는 공통적으로 Node라는 객체를 상속받는다.

Node객체는 firstChild, lastChild, nextSibling등의 프로퍼티를 가지고 있어서 이를 이용해서 각각의 Node들을 기준으로해서 자식이 누구인지, 형제가 누구인지 알 수 있다.



2. 주요기능

Node 객체의 주요한 임무는 아래와 같다.


(1) 관계

엘리먼트는 서로 부모, 자식, 혹은 형제자매 관계로 연결되어 있다. 각각의 Node가 다른 Node와 연결된 정보를 보여주는 API를 통해서 문서를 프로그래밍적으로 탐색할 수 있다.

- Node.childNodes//유사배열

- Node.firstChild

- Node.lastChild

- Node.nextSibling

- Node.previousSibling

- Node.contains()

- Node.hasChildNodes()


(2) 노드의 종류

Node 객체는 모든 구성요소를 대표하는 객체이기 때문에 각각의 구성요소가 어떤 카테고리에 속하는 것인지를 알려주는 식별자를 제공한다. 

- Node.nodeType

- Node.nodeName


(3) 값

Node 객체의 값을 제공하는 API

- Node.nodeValue

- Node.textContent


(4) 자식관리

Node 객체의 자식을 추가하는 방법에 대한 API

- Node.appendChild()

- Node.removeChild()


3. Node 관계 API

Node 객체는 Node 간의 관계 정보를 담고 있는 일련의 API를 가지고 있다. 다음은 관계와 관련된 프로퍼티들이다.

- Node.childNodes: 자식노드들을 유사배열에 담아서 리턴한다.

- Node.firstChild: 첫번째 자식노드

- Node.lastChild: 마지막 자식노드

- Node.nextSibling: 다음 형제 노드

- Node.previousSibling: 이전 형제 노드


아래는 위의 API를 이용해서 문서를 탐색하는 모습을 보여준다.

<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(<body>태그와 <ul>태그 사이의 공백(Node타입은 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>




4. 노드 종류 API

노드 작업을 하게 되면 현재 선택된 노드가 어떤 타입인지를 판단해야 하는 경우가 있다. 이런 경우에 사용할 수 있는 API가 nodeType, nodeName이다. 

- Node.nodeType: node의 타입을 의미한다. 

- Node.nodeName: node의 이름 (태그명을 의미한다.)


(1) Node Type

노드의 종류에 따라서 정해진 상수가 존재한다. 아래는 모든 노드의 종류와 종류에 따른 값을 출력하는 예제다.

for(var name in Node){

   console.log(name, Node[name]);

}

실행결과)

ELEMENT_NODE 1 (엘리먼트 노드의 값은 1-->상수)

ATTRIBUTE_NODE 2 

TEXT_NODE 3 

CDATA_SECTION_NODE 4 

ENTITY_REFERENCE_NODE 5 

ENTITY_NODE 6 

PROCESSING_INSTRUCTION_NODE 7 

COMMENT_NODE 8 

DOCUMENT_NODE 9 

DOCUMENT_TYPE_NODE 10 

DOCUMENT_FRAGMENT_NODE 11 

NOTATION_NODE 12 

DOCUMENT_POSITION_DISCONNECTED 1 

DOCUMENT_POSITION_PRECEDING 2 

DOCUMENT_POSITION_FOLLOWING 4 

DOCUMENT_POSITION_CONTAINS 8 

DOCUMENT_POSITION_CONTAINED_BY 16 

DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC 32





숫자는 외우기 힘들다.. 그래서 대안은....


아래 예제는 노드 종류 API를 이용해서 노드를 처리하는 예제다. 어떤함수가 실행될 때 그 함수가 자기 자신을 호출하는 것을 재귀함수라고 하는데 본 예제는 재귀 함수의 예를 보여준다.

<!DOCTYPE html>

<html>

<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){

// 첫번째 인자: 우리가 조회하려고 하는 루트 엘리먼트 지정 

//두번째 인자: 각각의 엘리먼트를 조회할 때 마다 그 순번에 해당하는 엘리먼트를 사용자가 작성한 함수의 인자로 전달(함수가 전달된다.--> 콜백)

    if(target.nodeType === 1){

    //nodeType이 1(Element)인 경우만 로직 수행--> 텍스트 노드를 배제하기 위함

    //if(target.nodeName === 'A')//a태그만 출력하기 위함

        callback(target);

  // callback 함수가 실행 위함(매개변수는 document.getElementById('start'))

        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>


5. 노드 변경 API


(1) 노드 추가

노드의 추가와 관련된 API들은 아래와 같다.

- appendChild(child): 노드의 마지막 자식으로 주어진 엘리먼트 추가

- insertBefore(newElement, referenceElement): appendChild와 동작방법은 같으나 두번째 인자로 엘리먼트를 전달 했을 때 이것 앞에 엘리먼트가 추가된다.


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

- document.createElement(tagname): 엘리먼트 노드를 추가한다.

- document.createTextNode(data): 텍스트 노드를 추가한다. 


<ul id="target">

    <li>HTML</li>

    <li>CSS</li>

</ul>

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

<input type="button" onclick="callInsertBefore();" value="insertBefore()" />

<script>

    function callAppendChild(){

        var target = document.getElementById('target');

        var li = document.createElement('li');//<li></li> 태그를 만든다.(text가 비어있음)

        var text = document.createTextNode('JavaScript');//text노드를 만든다.

        li.appendChild(text); // 위의 두개 코드를 결합한다.--> <li>JavaScript</li>

        target.appendChild(li); // <ul>태그 자식인 <li>태그의 맨 끝에 추가된다.

    }

 

    function callInsertBefore(){

        var target = document.getElementById('target');

        var li = document.createElement('li');

        var text = document.createTextNode('jQuery');

        li.appendChild(text);

        target.insertBefore(li, target.firstChild);//<ul>태그 자식인 <li>태그의 맨 앞에 추가된다.

    }

</script>

<li> 앞쪽에 추가된다.--> <ul>,<li>태그 사이의 text 노드 영역에 추가된다.


(2) 노드 제거

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


- removeChild(child)

<ul>

    <li>HTML</li>

    <li>CSS</li>

    <li id="target">JavaScript</li>

</ul>

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

<script>

    function callRemoveChild(){

        var target = document.getElementById('target');

        target.parentNode.removeChild(target);// target의 부모Node로 갔다가 다시 그 부모의 자식인 target을 삭제--> DOM이  비판받는 이유

    }

</script>


(3) 노드 바꾸기

노드 바꾸기에는 아래 API가 사용된다.


- replaceChild(newChild, oldChild)

<ul>

    <li>HTML</li>

    <li>CSS</li>

    <li id="target">JavaScript</li>

</ul>

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

<script>

    function callReplaceChild(){

        var a = document.createElement('a');

        a.setAttribute('href', 'http://opentutorials.org/module/904/6701');

        a.appendChild(document.createTextNode('Web browser JavaScript'));

 

        var target = document.getElementById('target');

        target.replaceChild(a,target.firstChild);//a: 교체대상 태그, target.firstChild: 교체하려고 하는 내용

    }

</script>


6. 문자열로 노드 제어

노드 변경 API에서는 여러 메소드를 이용해서 노드를 제어하는 방법에 대해서 알아봤다. 그런데 이 방식은 복잡하고 장황하다. 좀 더 편리하게 노드를 조작하는 방법을 알아보자.


(1) innerHTML

innerHTML는 문자열로 자식 노드를 만들 수 있는 기능을 제공한다. 또한 자식 노드의 값을 읽어올 수도 있다. 

<ul id="target">

    <li>HTML</li>

    <li>CSS</li>

</ul>

<input type="button" onclick="get();" value="get" />

<input type="button" onclick="set();" value="set" />

<script>

    function get(){

        var target = document.getElementById('target');

        alert(target.innerHTML); // id가 target의 하위 HTML 태그의 text노드 값 까지 그대로 화면에 출력된다.

    }

    function set(){

        var target = document.getElementById('target');

        target.innerHTML = "<li>JavaScript Core</li><li>BOM</li><li>DOM</li>";// 기존의 노드가 새롭게 변경한 코드로 변경된다.--> id가 target의 하위태그를 문자열로 지정할 수 있다.

    }

</script>

                                                         

(2) outerHTML

outerHTML은 선택한 엘리먼트를 포함해서 처리된다.

<ul id="target">

    <li>HTML</li>

    <li>CSS</li>

</ul>

<input type="button" onclick="get();" value="get" />

<input type="button" onclick="set();" value="set" />

<script>

    function get(){

        var target = document.getElementById('target');

        alert(target.outerHTML);

    }

    function set(){

        var target = document.getElementById('target');

        target.outerHTML = "<ol><li>JavaScript Core</li><li>BOM</li><li>DOM</li></ol>";

    }

</script>

innerHTML: 자신을 제외한 하위 엘리먼트가 대상 

outerHTML: 자신을 포함한 하위 엘리먼트까지 대상


(3) innerText, outerText

innerHtml, outerHTML과 다르게 이 API들은 값을 읽을 때는 HTML 코드를 제외한 문자열을 리턴하고, 값을 변경할 때는 HTML의 코드를 그대로 추가한다.

<ul id="target">

    <li>HTML</li>

    <li>CSS</li>

</ul>

<input type="button" onclick="get();" value="get" />

<input type="button" onclick="set();" value="set" />

<script>

    function get(){

        var target = document.getElementById('target');

        alert(target.innerText);

    }

    function set(){

        var target = document.getElementById('target');

        target.innerText = "<li>JavaScript Core</li><li>BOM</li><li>DOM</li>";//태그 내용값(text)만 출력되는 것이 아니라 그 태그까지 출력된다.

    }

</script>


(4) insertAdjacentHTML()

좀 더 정교하게 문자열을 이용해서 노드를 변경하고 싶을 때 사용한다.

<ul id="target">

    <li>CSS</li>

</ul>

<input type="button" onclick="beforebegin();" value="beforebegin" />

<input type="button" onclick="afterbegin();" value="afterbegin" />

<input type="button" onclick="beforeend();" value="beforeend" />

<input type="button" onclick="afterend();" value="afterend" />

<script>

    function beforebegin(){

        var target = document.getElementById('target');

        target.insertAdjacentHTML('beforebegin','<h1>Client Side</h1>');

    }

    function afterbegin(){

        var target = document.getElementById('target');

        target.insertAdjacentHTML('afterbegin','<li>HTML</li>');

    }

    function beforeend(){

        var target = document.getElementById('target');

        target.insertAdjacentHTML('beforeend','<li>JavaScript</li>');

    }

    function afterend(){

        var target = document.getElementById('target');

        target.insertAdjacentHTML('afterend','<h1>Server Side</h1>');

    }

</script>



                               

'WebFont-end > JavaScript' 카테고리의 다른 글

[JavaScript] Text 객체  (0) 2014.12.23
[JavaScript] Document 객체  (0) 2014.12.23
[JavaScript] Element 객체  (0) 2014.12.22
[JavaScript] HTMLCollection 객체  (0) 2014.12.22
[JavaScript] HTMLElement 객체  (0) 2014.12.22