[Web] JavaScript DOM 조작하기, 여러 메서드 + 예제

  HTML을 동적으로 다양하게 변화를 주기 위해 DOM API를 활용해야한다. 라이브러리를 쓰는 것 보다 속도 차이가 크지는 않지만 대체로 더 빠른 편이다.

참고
document. 의 api들 > https://www.w3schools.com/jsref/dom_obj_document.asp
element. 의 api들 > https://www.w3schools.com/jsref/dom_obj_all.asp

firstChild : 첫번째 자식 노드를 찾아준다. 하지만 공백이나 텍스트 노드를 가져 올 수 있다.
firstElementChild : 첫번째 element 자식 노드를 찾아준다.

DOM 조작 API
  • removeChild()
  • appendChild()
  • insertBefore()
  • clondNode()
  • replaceChild()
  • closest() //상위로 올라가면서 근접 엘리먼트 찾기


var div = document.createElement("div");
var str = documnent.createTextNode("오늘도 화이팅");
div.appendChild(str);

  크롬 개발자 도구를 활용해 실제 사이트에서 api들을 연습해 볼 수 있다.
react angular view 템플릿상으로 html을 만들어두고 변경이필요한 data 부분은 바꿔주면 자동으로 렌더링되는 자동화된 방법을 사용하기도 하지만 dom api를 조작하는 것이 표준적 방법이므로 익숙해지는 것이 필요하다. 


var sp1 = document.createElement("span");
var sp2 = document.getElementById("childElement");
var parentDiv = sp2.parentNode;
parentDiv.insertBefore(sp1,sp2); //sp2 앞에 sp1을 추가하는 메서드


var parent = document.selectQuery(".w3-example");
parent.innerText("<p>haha</p>");
//query create를 하지 않아도 childNode가 생성된다
parent.innerText = parrnet.innerText+"<p>haha</p>"


유용한 DOM 엘리먼트 속성
  • tagName : 엘리먼트의 태그명 반환
  • textContent : 노드의 텍스트 내용을 설정하거나 변환
  • nodeType : 노드의 노드 유형을 숫자로 변환
DOM 탐색 속성
  • childNodes : 엘리먼트의 자식 노드의 노드 리스트 반환 ( textNode, 주석 노드 포함)
  • firstChild : 엘리먼트의 첫번째 자식 노드 반환
  • firstElementChild : 첫번째 자식 엘리먼트 반환
  • parentElement : 엘리먼트의 부모 엘리먼트 반환
  • nextSibling : 동일한 노드 트리 레벨에서 다음 노드 반환
  • nextElementSibling : 동일한 노드 트리 레벨에서 다음 엘리먼트 반환
DOM 조작 메소드
  • removeChild() : 엘리먼트의 자식 노드 제거
  • appendChild() : 마지막 자식 노드를 추가
  • insertBefore() : 기존 자식노드 앞에 새 자식 노드 추가
  • cloneNode() : 엘리먼트의 자식 노드 복제
  • replaceChild() : 엘리먼트의 자식 노드 바꾸기
  • closest() : 상위로 올라가면서 가장 가까운 엘리먼트를 반환
HTML을 문자열로 처리해주는 DOM 속성 / 메소드
  • innerText : 지정된 노드와 모든 자손의 텍스트 내용을 설정하거나 반환
  • innerHTML : 지정된 엘리먼트의 내부 HTML을 설정하거나 반환
  • innsertAdjacentHTML() : HTML로 텍스트를 지정된 위치에 삽입

예제 실습

'fruit.html'

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <h1>selector test</h1>
  
  <ul>
    <li>apple</li>
    <li>orange</li>
    <li>banana</li>
    <li>grape</li>
    <li>strawberry</li>
  </ul>

</body>
</html>

문제1 ) insertBefore() 를 사용해 orange와 banana 사이에 과일을 추가하라

insertBefore의 문법 :
"부모노드.insertBefore(삽입할 자식 노드, 기준 자식 노드)"


var li = document.querySelector("ul li:nth-child(3)");
var ul = document.querySelector("ul");

var fruit = document.createElement("li");
var text = document.createTextNode("peach");
fruit.appendChild(text);

ul.insertBefore(fruit,li);

문제2) 위의 문제를 InsertAdjacentHTML() 을 사용하여 풀어라
insertAdjacentHTML의 문법:
"부모노드.insertAdjacentHTML('position',"html") "
position에는 아래의 4가지가 있다.

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


li.insertAdjacentHTML('beforebegin',"<li>peach</li>");

문제3) apple 엘리먼트를 grape와 strawberry 사이에 위치하도록 옮겨라

var li = document.querySelector("li:nth-child(1)");
var berry = document.querySelector("li:nth-child(5)");
var ul = document.querySelector("ul");
ul.insertBefore(li,berry);
! insertBefore 는 clone 하는 것이 아니라 이미 존재하는 노드를 다른 위치로 이동시켜준다.

문제4) class가 red인 노드만 삭제하라
(위 html에 apple, orange 가 red 클래스에 추가된 상태)

처음에는 querySeletor로 element를 받아왔는데 첫번째 red 인 apple 만 삭제가 되었다. 다음으로 getElementsByClassName 메소드를 사용했는데 이번에도 apple 만 삭제가 되었다. 개발자 도구를 켜 확인해보니 undefined element에 대해 remove를 수행할 수 없다는 오류가 나왔다.
찾아보니 getElementsByClassName 는 live list를 반환하기 때문에 내가 remove를 수행하면 첫번째 엘리먼트가 지워지고 나서 두번째 엘리먼트가 첫번째 엘리먼트가 되기 때문에 반복문을 돌면서 두번째 index로 지우려고 해도 그 자리엔 아무것도 없기때문에 오류가 발생한다. 따라서 querySeletorAll()을 사용해야 한다.


var red = document.querySelectorAll('.red');
for (var i=0;i<red.length;i++){
  red[i].remove();
}

var red = document.querySelectorAll('.red');

red.forEach(function(el){
  el.remove();
})


var reds = document.querySelectorAll(".red");
var ul = document.querySelector("ul");
for(var i=0;i<reds.length;i++){
  ul.removeChild(reds[i]);
}

! remove()는 IE 에서 호환되지 않는다. 호환성이 좋은 removeChild를 쓰는 것도 꼭 알아둘 것

문제 5) section 태그의 자손중 blue 라는 클래스를 가진 노드가 있다면 그 하위에 있는 h2 노드를 삭제해라


<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <h1>selector test</h1>
  
    
<section>
  <h2> red section </h2>
  <ul>
    <li class="red">apple</li>
    <li class="red">orange</li>
    <li>banana</li>
    <li>grape</li>
    <li>strawberry</li>
  </ul>
</section>
  
  <Br>
  
<section>
  <h2> blue section </h2>
  <ul>
    <li class="green blue">apple</li>
    <li class="red">orange</li>
    <li>banana</li>
    <li>grape</li>
    <li>strawberry</li>
  </ul>
</section>

</body>
</html>

1. 가장 비효율적으로 내가 쓴 답


var blue = document.getElementsByClassName("blue");
for(var i=0;i<blue.length;i++){
  var parent = blue[i].parentElement;
  var section = parent.parentElement;
  var h2 = section.getElementsByTagName("h2");
  for(var j=0;j<h2.length;j++)
    h2[j].remove();
}

2. closest 메서드 활용

var bluenode = document.querySelectorAll("section .blue");
for(var i =0;i<bluenode.length;i++){
  var section = bluenode[i].closest("section");
  var h2 = section.querySelector("h2");
  section.removeChild(h2);
}

3. for문을 forEach로

var bluenode = document.querySelectorAll("section .blue");
bluenode.forEach(function(element){
  var section = element.closest("section");
  var h2 = section.querySelector("h2");
  section.removeChild(h2);
})

4. Array.from() 메서드 사용


var bluenode = document.querySelectorAll("section .blue");
Array.from(bluenode).forEach(function(v){
  var section = v.closest("section");
  var h2 = section.querySelector("h2");
  section.removeChild(h2);
});


참고
위키피디아 DOM 설명 : https://en.wikipedia.org/wiki/Document_Object_Model


No comments:

Powered by Blogger.