...
브라우저 DOM 종류
브라우저는 HTML 문서를 로드한 후, 해당 문서에 대한 모델을 메모리에 생성한다.
이때 모델은 객체의 트리로 구성되는데, 이것을 DOM tree라 한다.
문서 노드(Document Node)
트리의 최상위에 존재하며 각각 요소, 어트리뷰트, 텍스트 노드에 접근하려면 문서 노드를 통해야 한다.
즉, DOM tree에 접근하기 위한 시작점(entry point)이다.
요소 노드(Element Node)
요소 노드는 HTML 엘리먼트를 표현한다.
어트리뷰트, 텍스트 노드에 접근하려면 먼저 요소 노드를 찾아 접근해야 한다. 모든 요소 노드는 요소별 특성을 표현하기 위해 HTMLElement 객체를 상속한 객체로 구성된다.
document.getElementById(id) // -> id 어트리뷰트 값으로 요소 노드를 한 개 선택
어트리뷰트 노드(Attribute Node)
어트리뷰트 노드는 HTML 요소의 어트리뷰트를 표현한다.
어트리뷰트 노드는 해당 어트리뷰트가 지정된 요소의 자식이 아니라 해당 요소의 일부로 표현된다.
따라서 해당 요소 노드를 찾아 접근하면 어트리뷰트를 참조, 수정할 수 있다.
document.querySelector('h1').id = 'heading'; // -> id 어트리뷰트의 값을 변경
텍스트 노드(Text Node)
텍스트 노드는 HTML 요소의 텍스트를 표현한다.
텍스트 노드는 요소 노드의 자식이며 자신의 자식 노드를 가질 수 없다.
즉, 텍스트 노드는 DOM tree의 최종하단이다.
요소의 텍스트는 텍스트 노드에 저장되어 있다. 텍스트 노드에 접근하려면 수순이 필요하다.
<> ~ </> 꺽쇠 한쌍이 곧 노드 객체 한 개다.
자바스크립트 브라우저 DOM 문법
DOM 요소 선택
코드 | 설명 |
document.getElementById("id명") | 해당 id명을 가진 요소 하나를 반환 |
document.querySelector("선택자") | 해당 선택자를 만족하는 요소 하나를 반환 |
document.getElementsByClassName("class명")[순서] | 해당 class명을 가진 모든 요소들을 배열에 담아 인덱스에 맞는 요소를 반환 Return: HTMLCollection (live) 배열 HTMLCollecion 는 foreach 문을 사용할 수 없다. |
document.getElementsByTagName("태그명")[순서] | 해당 태그명을 가진 모든 요소들을 배열에 담아 인덱스에 맞는 요소를 반환 Return: HTMLCollection (live) 배열 |
document.querySelectorAll("선택자명")[순서] | 해당 선택자를 만족하는 모든 요소들을 배열에 인덱스에 맞는 요소를 반환 Return: NodeList (non-live) 배열 nodeList 는 foreach 문을 사용할 수 있다. |
var selectedItem = document.getElementsByTagName("li"); // 모든 <li> 요소를 선택함.
for (var i = 0; i < selectedItem.length; i++) {
console.log(selectedItem[i]); // 태그 배열들을 순회함
}
document.getElementById("idname"); // id값 가져옴
document.getElementsByClassName("classname")[0]; // 선택된 요소중 첫번째 요소 => 인덱스 0
document.getElementsByTagName("button")[0];
// <div>태그들을 모두 가져오고 그중 첫번째 div태그에서, 클래스가 button인것들중에 첫번째 선택
document.getElementsByClassName("div")[0].getElementsByClassName("button")[0];
document.querySelector("#idname"); // 선택쿼리문 선택
document.querySelector("#idname, .classname"); // 여러 속성 선택 가능 !!
document.querySelectorAll(".classname")[0];
<div id="login-form">
<input type="text" placeholder="what is your name?" />
<button>Log In</button>
</div>
<script>
const $loginForm = document.querySelector('#login-form');
const $loginInput = $loginForm.querySelector('input'); // div태그의 자식을 선택하는 쿼리
const $loginButton = $loginForm.querySelector('button');
</script>
요소 배열 순회
getElementsByClassName 메소드의 반환값은 HTMLCollection이다.
HTMLCollection 으로 반환된 리스트는 실시간 유사배열이다.
실시간으로 Node의 상태 변경을 반영하기 때문에 loop가 필요한 경우 주의가 필요하다. (중간에 배열 값을 떼거나 변경하면 자동으로 인덱스 정렬을 한다.)
--> 해결책 : 어딘가 배열에 복사를 거쳐서 그걸로 이용한다.
const elems = document.getElementsByClassName('red');
// 유사 배열 객체인 HTMLCollection을 배열로 복사&변환한다.
// 배열로 변환된 HTMLCollection은 더 이상 live하지 않다.
// 배열값들을 복사를 해도, dom에 레퍼런스로 연결되어있기 때문에 문제없다.
console.log([...elems]); // [li#one.red, li#two.red, li#three.red]
[...elems].forEach(elem => elem.className = 'blue');
HTMLCollection vs NodeList
HTMLCollection | NodeList | |
공통점 | DOM API로 요소를 취득할 때 반환되는 이터러블 객체다. 따라서 for...of, 스프레드문법, 구조분해할당이 가능하다. | |
출처 | getElementsByTagName, getElementsByClassName | querySelectorAll, childNodes |
API에 전달한 인수를 만족하는지에 따라 객체 구성 원소 목록이 동적으로 바뀌는가? |
동적 | 정적 단, childNodes만 예외로 동적 |
HTML 선택쿼리문
객체 집합 | 설명 |
document.anchors | name 속성을 가지는 <a>요소를 모두 반환함. |
document.applets | applet 요소를 모두 반환함. (HTML5에서 제외됨) |
document.body | <body>요소를 반환함. |
document.cookie | HTML 문서의 쿠키(cookie)를 반환함. |
document.domain | HTML 문서가 위치한 서버의 도메인 네임(domain name)을 반환함. |
document.forms | <form>요소를 모두 반환함. |
document.images | <img>요소를 모두 반환함. |
document.links | href 속성을 가지는 <area>요소와 <a>요소를 모두 반환함. |
document.referrer | 링크(linking)되어 있는 문서의 URI를 반환함. |
document.title | <title>요소를 반환함. |
document.URL | HTML 문서의 완전한 URL 주소를 반환함. |
document.baseURI | HTML 문서의 절대 URI(absolute base URI)를 반환함. |
document.doctype | HTML 문서의 문서 타입(doctype)을 반환함. |
document.documentElement | <html>요소를 반환함. |
document.documentMode | 웹 브라우저가 사용하고 있는 모드를 반환함. |
document.documentURI | HTML 문서의 URI를 반환함. |
document.domConfig | HTML DOM 설정을 반환함. (더는 사용하지 않음) |
document.embeds | <embed>요소를 모두 반환함. |
document.head | <head>요소를 반환함. |
document.implementation | HTML DOM 구현(implementation)을 반환함. |
document.inputEncoding | HTML 문서의 문자 인코딩(character set) 형식을 반환함. |
document.lastModified | HTML 문서의 마지막 갱신 날짜 및 시간을 반환함 |
document.readyState | HTML 문서의 로딩 상태(loading status)를 반환함. |
document.scripts | <script>요소를 모두 반환함. |
document.strictErrorChecking | 오류의 강제 검사 여부를 반환함. |
DOM 요소 탐색
프로퍼티 | 특징 | 텍스트 노드 포함 여부 |
parentNode | 부모 노드를 탐색 | O |
parentElement | 부모 노드 탐색 | X |
childNodes | 하위 모든 노드 객체를 NodeList로 반환 Return: NodeList (live) |
O |
children | 하위 모든 노드 객체를 HTMLCollection으로 반환 Return: HTMLCollection (live) |
X |
firstChild | 직계 자식 객체 중에서 가장 첫번째 노드 객체를 반환 | O |
lastChild | 직계 자식 객체 중에서 가장 마지막 노드 객체를 반환 | O |
firstElementChild | 직계 자식 객체 중에서 가장 첫번째 노드 객체를 반환 | X |
lastElementChild | 직계 자식 객체 중에서 가장 마지막 노드 객체를 반환 | X |
hasChildNodes() | 자식 노드 객체의 존재 여부를 boolean으로 반환 | O |
children.length | 자식 노드 객체 수 반환 | X |
childElementCount | 자식 노드 객체 수 반환 | X |
previousSibling | 같은 부모를 가진 노드 객체 중에서 이전 노드 반환 | O |
nextSibling | 같은 부모를 가진 노드 객체 중에서 다음 노드 반환 | O |
previousElementSibling | 같은 부모를 가진 노드 객체 중에서 이전 노드 반환 | X |
nextElementSibling | 같은 부모를 가진 노드 객체 중에서 다음 노드 반환 | X |
<html>
<head>
</head>
<body>
<ul id="languages">
<li class="html">HTML</li>
<li class="css">CSS</li>
<li class="js">JS</li>
</ul>
<script>
const $css = document.querySelector('.css');
console.log($css.previousSibling);
console.log($css.nextSibling);
console.log($css.previousElementSibling);
console.log($css.nextElementSibling);
</script>
</body>
</html>
<html>
<head>
</head>
<body>
<ul id="languages">
<li class="html">HTML</li>
<li class="css">CSS</li>
<li class="js">JS</li>
</ul>
<script>
const $languages = document.getElementById('languages');
console.log($languages);
console.log($languages.childNodes);
console.log($languages.children);
console.log($languages.firstChild);
console.log($languages.lastChild);
console.log($languages.firstElementChild);
console.log($languages.lastElementChild);
console.log($languages.hasChildNodes());
console.log($languages.children.length);
console.log($languages.childElementCount);
</script>
</body>
</html>
DOM 요소 속성 조정
코드 | 설명 |
hasAttribute(attribute) | 지정한 어트리뷰트를 가지고 있는지 검사한다. |
getAttribute(attribute) | 어트리뷰트의 값을 취득한다. |
setAttribute(attribute, value) | 어트리뷰트와 어트리뷰트 값을 설정한다. |
removeAttribute(attribute) | 지정한 어트리뷰트를 제거한다. |
.attributes | 속성들을 모아서 배열로 반환 |
<!DOCTYPE html>
<html>
<body>
<input type="text">
<script>
const input = document.querySelector('input[type=text]');
console.log(input);
if (!input.hasAttribute('value')) { // value 어트리뷰트가 존재하지 않으면
// value 어트리뷰트를 추가하고 값으로 'hello!'를 설정
input.setAttribute('value', 'hello!');
}
// value 어트리뷰트 값을 취득
console.log(input.getAttribute('value')); // hello!
// value 어트리뷰트를 제거
input.removeAttribute('value');
</script>
</body>
</html>
코드 | 설명 |
element.id | id 어트리뷰트의 값을 취득 또는 변경한다. id 어트리뷰트가 존재하지 않으면 id 어트리뷰트를 생성하고 지정된 값을 설정한다. |
element.style.property | 해당 요소에 적용된 css속성의 값을 나타냅니다. |
element.attribute | 해당 요소의 속성을 나타냅니다. |
<form>
<input type="password" name="input"/>
</form>
input {
display: block;
background-color: red;
width: 200px;
height: 10px;
}
const input = document.querySelector("input");
input.style.display = "none";
input.style.width = "100px";
input.name = "output";
input.id = "input_id";
input.style['font-size'] = '2rem';
//주의할 점은 css 속성 값을 따옴표로 감싸서 문자열의 형태로 대입해야 된다는 것입니다.
DOM 클래스 속성 조정
코드 | 설명 |
element.className | class 어트리뷰트의 값을 취득 또는 변경한다. class 어트리뷰트가 존재하지 않으면 class 어트리뷰트를 생성하고 지정된 값을 설정한다. class 어트리뷰트의 값이 여러 개일 경우, 공백으로 구분된 문자열이 반환되므로 String 메소드 split(', ')를 사용하여 배열로 변경하여 사용한다. |
element.classList | 보통 부트스트랩같이, 클래스명으로 css 스타일을 조정하는 경우가 많다. 이때, classList에서 제공하는 메서드로 클래스명을 간편하게 조정 할 수 있다. add, remove, item, toggle, contains, replace 메소드를 제공한다. |
<p id="a11"class="document, aa, bb, cc, dd"></p>
<script>
let a = document.querySelector("#a11");
console.log(a.className) // "document, aa, bb, cc, dd"
let arr = a.className.split(", "); // [ 'document', 'aa', 'bb', 'cc', 'dd' ]
</script>
classList 메서드 | 기능 |
add() | 클래스 목록에 추가할 문자열을 1개 이상 인수로 전달한다. |
remove() | 클래스 목록에서 제거할 문자열을 1개 이상 인수로 전달한다. |
item() | 클래스 목록에서, 인수로 전달한 정수 인덱스에 위치한 문자열을 반환한다. |
contains() | 인수로 전달한 문자열이 클래스 목록에 존재하는 지 판단하여 boolean을 반환한다. |
replace() | 첫번째 인수로 전달한 문자열을 클래스 목록에서 찾은 후, 두번째 인수를 대입한다. |
toggle() | 첫번째 인수로 전달한 문자열이 클래스 목록에 있으면 삭제를, 없으면 추가한다. |
toggle(true/false) | 첫번째 인수가 클래스 목록에 이미 존재하는지 파악하지 않고, true는 첫번째 인수를 클래스 목록에 강제 추가, false는 첫번째 인수를 클래스 목록에서 강제 삭제한다. |
var element = document.createElement('tagName');
var addClass = element.classList.add('className'); // Class 추가
var removeClass = element.classList.remove('className'); // Class 삭제
var toggleClass = element.classList.toggle('className'); // Class 토글
// class가 존재한다면 제거하고 존재하지 않으면 class를 추가한다.
const div = document.createElement('div');
div.className = 'foo';
// our starting state: <div class="foo"></div>
console.log(div.outerHTML);
// use the classList API to remove and add classes
div.classList.remove("foo");
div.classList.add("anotherclass");
// <div class="anotherclass"></div>
console.log(div.outerHTML);
// if visible is set remove it, otherwise add it
div.classList.toggle("visible");
// add/remove visible, depending on test conditional, i less than 10
div.classList.toggle("visible", i < 10 );
console.log(div.classList.contains("foo"));
// add or remove multiple classes
div.classList.add("foo", "bar", "baz");
div.classList.remove("foo", "bar", "baz");
// add or remove multiple classes using spread syntax
const cls = ["foo", "bar"];
div.classList.add(...cls);
div.classList.remove(...cls);
// replace class "foo" with class "bar"
div.classList.replace("foo", "bar");
DOM 노드 조회
노드(Node) : HTML 페이지의 각 요소(html, head, body . . . 등)
- 요소 노드(Element Node) : HTML 태그
- 텍스트 노드(Text Node) : 요소 노드 안에 있는 글자
코드 | 설명 |
nodeName | 노드의 이름을 나타내는 문자열 반환 요소 노드 : 태그 이름의 영대문자 텍스트 노드 : #text 문서 노드 : #document |
nodeValue | 특정 노드에 첫번째 하위 텍스트 노드의 값(텍스트)을 조회/수정한다. firstChild로 텍스트 노드를 직접 찾아줘야 한다. 텍스트 노드가 아닌 노드에 적용하면 null을 반환한다. |
nodeType | 노드 고유의 타입을 조회. 수정X 요소 노드(element node) : 1 속성 노드(attribute node) : 2 텍스트 노드(text node) : 3 주석 노드(comment node) : 8 문서 노드(document node) : 9 |
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>JavaScript Node Access</title>
</head>
<body>
<h1>nodeName 프로퍼티</h1>
<p id="document"></p>
<p id="html"></p>
<h1 id="heading">nodeValue 프로퍼티</h1>
<p id="text1">텍스트</p>
<script>
// 문서의 첫 번째 자식 노드는 <!DOCTYPE html>이며, 두 번째 자식 노드는 <html>입니다.
console.log(document.childNodes[1].nodeName)
// 문서의 두번째 자식노드 html 의 첫 자식은 head 노드.
console.log(document.childNodes[1].childNodes[0].nodeName)
// 문서의 두번째 자식노드 html 의 세번째 자식노드 body의 첫 자식노드 h1의 첫 자식 노드 text노드
console.log(document.childNodes[1].childNodes[2].childNodes[0].nodeName)
// 아이디가 "heading"인 요소의 첫 번째 자식 노드(텍스트노드)의 노드값을 선택함.
console.log(document.getElementById("heading").firstChild.nodeValue)
</script>
</body>
</html>
💡 빈 텍스트 노드의 처리
현재 대부분의 주요 웹 브라우저는 모두 W3C DOM 모델을 지원하지만, 그 처리 방식에 있어 약간씩의 차이가 있다.
그중에서도 가장 큰 차이점은 띄어쓰기와 줄 바꿈을 처리하는 방식이다.
파이어폭스나 기타 브라우저들은 띄어쓰기나 줄 바꿈을 텍스트 노드(text node)로 취급한다.
하지만 익스플로러는 띄어쓰기나 줄 바꿈을 텍스트 노드로 취급하지 않는다.
따라서 자식 노드나 형제 노드를 이용하여 원하는 노드에 접근하려고 하면 브라우저 간에 차이가 발생하게 된다.
이 차이를 없애는 가장 손쉬운 방법은 nodeType 프로퍼티를 사용하여 선택된 요소의 타입을 검사하는 것이다.
DOM 쓰기
요소에 접근하면 요소의 여러 가지 속성에 접근하여 변경할 수 있다.
* 마크업이 포함된 콘텐츠를 추가하는 것은 크로스 스크립팅 공격(XSS: Cross-Site Scripting Attacks)에 취약하다
코드 | 설명 |
element.innerHTML | 해당 요소의 모든 자식 요소를 포함하는 모든 콘텐츠를 하나의 문자열로 취득할 수 있다. 이 문자열은 마크업을 포함한다. innerHTML에서 지정한 요소 태그를 제외한 안쪽 태그만 값을 가져온다 자기자신 미포함 |
element.outerHTML | outerHTML에서 지정한 요소 태그도 포함하여 전체 태그 값을 가져오고 선택한 엘리먼트를 포함해서 처리 자기자신 포함 |
document.write() document.writeln() |
인자로 전달한 내용을 DOM에 그릴 수 있다. ln은 개행문자 포함해서 출력 |
textContent | 요소의 텍스트 콘텐츠를 취득 또는 변경한다. 이때 마크업은 무시된다. textContent를 통해 요소에 새로운 텍스트를 할당하면 텍스트를 변경할 수 있다. 이때 순수한 텍스트만 지정해야 하며 마크업을 포함시키면 문자열로 인식되어 그대로 출력된다. |
element.insertAdjacentHTML | innerHTML로 기존 요소를 수정할 때 원본이 불필요하게 지워졌다가 다시 할당되는 것을 방지한다. 첫번째 인수로 아래 4가지 위치 문자열 중 하나를, 두번째 인수로 삽입할 마크업 문자열을 전달하면 된다. 태그가 열리기 직전에 삽입 : beforebegin 태그가 열린 직후에 삽입 : afterbegin 태그가 닫히기 직전에 삽입 : beforeend 태그가 닫힌 직후에 삽입 : afterend |
<div id="test">TEST</div>
<script type="text/javascript">
alert(document.getElementById('test').innerHTML);
//결과는 TEST
alert(document.getElementById('test').outerHTML);
//결과는 <div id="test">TEST</div>
</script>
const one = document.getElementById('one');
// 마크업이 포함된 콘텐츠 취득
console.log(one.innerHTML); // Seoul
// 마크업이 포함된 콘텐츠 변경
one.innerHTML += '<em class="blue">, Korea</em>';
// 마크업이 포함된 콘텐츠 취득
console.log(one.innerHTML); // Seoul <em class="blue">, Korea</em>
<div>
<h1>Cities</h1>
<ul>
<li id="one" class="red">Seoul</li>
<li id="two" class="red">London</li>
<li id="three" class="red">Newyork</li>
<li id="four">Tokyo</li>
</ul>
</div>
<script>
const ul = document.querySelector('ul');
console.log(ul.textContent); // 요소의 텍스트 취득
/*
Seoul
London
Newyork
Tokyo
*/
const one = document.getElementById('one');
console.log(one.textContent); // 요소의 텍스트 취득 // Seoul
one.textContent += ', Korea'; // 요소의 텍스트 변경
console.log(one.textContent); // Seoul, Korea
// 요소의 마크업이 포함된 콘텐츠 변경.
one.textContent = '<h1>Heading</h1>';
// 마크업이 문자열로 표시된다.
console.log(one.textContent); // <h1>Heading</h1>
</script>
<body>
<ul id="languages">
<li class="html">HTML</li>
<li class="css">CSS</li>
<li class="js">JS</li>
</ul>
<script>
const $languages = document.getElementById('languages');
$languages.insertAdjacentHTML('beforebegin', '<p>Insert into beforebegin</p>');
$languages.insertAdjacentHTML('afterbegin', '<p>Insert into afterbegin</p>');
$languages.insertAdjacentHTML('beforeend', '<p>Insert into beforeend</p>');
$languages.insertAdjacentHTML('afterend', '<p>Insert into afterend</p>');
</script>
</body>
innerHTML과 insertAdjacentHTML()은
크로스 스크립팅 공격(XSS: Cross-Site Scripting Attacks)에 취약하다.
텍스트를 추가 또는 변경시에는 textContent,
새로운 요소의 추가 또는 삭제시에는 DOM 조작 방식을 사용하도록 한다.
DOM 추가 / 삭제
innerHTML 프로퍼티를 사용하지 않고 새로운 콘텐츠를 추가할 수 있는 방법은 DOM을 직접 조작하는 것이다.
- 요소 노드 생성 createElement() 메소드를 사용하여 새로운 요소 노드를 생성한다. createElement() 메소드의 인자로 태그 이름을 전달한다.
- 텍스트 노드 생성 createTextNode() 메소드를 사용하여 새로운 텍스트 노드를 생성한다. 경우에 따라 생략될 수 있지만 생략하는 경우, 콘텐츠가 비어 있는 요소가 된다.
- 생성된 요소를 DOM에 추가 appendChild() 메소드를 사용하여 생성된 노드를 DOM tree에 추가한다. 또는 removeChild() 메소드를 사용하여 DOM tree에서 노드를 삭제할 수도 있다.
코드 | 설명 |
createElement(tagName) | 태그이름을 인자로 전달하여 요소를 생성한다. |
createTextNode(text) | 텍스트를 인자로 전달하여 텍스트 노드를 생성한다. Return: Text 객체 |
appendChild(Node) | 인자로 전달한 노드를 마지막 자식 요소로 DOM 트리에 추가한다. Return: 추가한 노드 |
removeChild(Node) | 인자로 전달한 노드를 DOM 트리에서 제거한다. Return: 추가한 노드 |
replaceChild(after, before) | 두번째 인수를 첫번째 인수로 덮어씌우는데, 두번째 인수는 이 메서드를 호출한 노드 객체의 자식 객체여야 한다. Return: 대체한 노드 |
.remove() | 현재 엘리먼트를 삭제. 삭제할 엘리먼트의 참조만 있으면 됨. |
insertBefore(newNode, childNode) | 첫번째 인수로 전달받은 노드를, 두번째 인수로 전달한 노드의 바로 앞에 삽입한다. 단, 두번째 인수는 이 메서드를 호출한 노드 객체의 자식 노드여야 한다. 만약 자식 노드가 아니라면 DOMException 에러가 나며, 두번째 인수에 null을 전달하면 appendChild와 마찬가지로 가장 마지막 자식 노드 위치로 추가된다. |
cloneNode() | 현재 노드를 복사해 반환합니다. 파라메터로 true/false 를 인자로 보낼 수 있습니다. true 면 노드가 담고 있는 텍스트 등 안의 태그 내용까지 복사됩니다. false 면 노드 외형만 복사합니다. |
before() after() |
자바스크립트 메서드 요소를 외부로 추가함 |
after(): 선택한 노드의 뒤에 추가함. before(): 선택한 노드의 앞에 추가함. 문자열, 엘리먼트(노드) 객체 모두 추가 요소로 사용 가능하며, 문자열을 인자로 넣을 경우 텍스트 노드가 추가됨. 반환값이 없음(undefined 반환) 나머지 파라메터로 여러 개의 노드와 문자열을 추가하는 것이 가능. |
prepend() append() |
자바스크립트 메서드 요소를 내부(자식)로 추가함 |
prepend(): 현재 엘리먼트의 첫번째 자식 엘리먼트 앞에 추가함. append(): 현재 엘리먼트의 자식 엘리먼트 중 마지막 엘리먼트 뒤에 추가함. 문자열, 엘리먼트(노드) 객체 모두 추가 요소로 사용 가능하며, 문자열을 인자로 넣을 경우 텍스트 노드가 추가됨. 반환값이 없음(undefined 반환) 나머지 파라메터로 여러 개의 노드와 문자열을 추가하는 것이 가능. |
createTextNode(), createElement() 이용한 노드 생성 및 추가 과정을 before()/after()/prepend()/append() 1개의 메서드로 줄여서 사용할 수 있기 때문에 코드 최적화 및 가독성 향상에 유리합니다.
가급적 before(), after() 메서드를 사용해 DOM 을 제어하는 것을 추천합니다.
HTML 도큐먼트 생성
// HTML 도큐먼트 생성
let newdoc = document.implementation.createHTMLDocument("HTML Document");
// div 태그 안에 내용을 만들어 넣음.
let div = newdoc.createElement("div");
div.innerHTML = "<span>HTML 문서 샘플</span>";
newdoc.body.appendChild(div); // 도큐먼트 바디(body)에 자식으로 붙임
//HTML 도큐먼트를 붙여넣을 아이프레임 참조 얻기
targetdoc = document.getElementById('newframe').contentDocument;
// 붙여넣을 새 노드를 복사해옴
let newnode = targetdoc.importNode(newdoc.documentElement, true);
// 아이프레임 도큐먼트를 새 노드로 대체
targetdoc.replaceChild(newnode, targetdoc.documentElement);
엘리먼트 생성
let div = document.createElement('div'); //div 태그 생성
let span = document.createElement('span'); //span 태그 생성
span.textContent = 'HTML 문서 샘플'; //span태그 안에 텍스트 넣음
div.appendChild(span); // span태그를 div 자식으로서 상속
document.body.appendChild(div); // div태그를 body 자식으로서 상속 -> 태그 추가
엘리먼트 삭제
document.getElementById('delid').remove(); //선택한 태그 자기자신 삭제
let parent = document.getElementById('parentdiv');
let child = document.getElementById('childspan');
parent.removeChild(child); //부모요소.removeChild(자식요소); 형식으로 부모 안에 있는 자식 삭제
엘리먼트 삽입, 이동
<!DOCTYPE html>
<html>
<head>
<title>Elemetnt Insert</title>
</head>
<body>
<ul id="friends">
<li class="animal">라이언</li>
<li class="fruit">어피치</li>
<li class="animal">프로도</li>
<li class="alien">콘
<ul class="keyword">
<li>3세</li>
<li>숏다리</li>
<li>초록괴수</li>
</ul>
</li>
</ul>
<ul class="icons">
<li>
<span>스몰</span>
<span>미디엄</span>
<span>빅</span>
</li>
</ul>
<ul id="newfriends">
</ul>
</body>
</html>
/* javascript */
// 새 <li> 엘리먼트 새성
let li = document.createElement('li');
let litext = document.createTextNode('무지');
li.appendChild(litext);
// 새 <li> 엘리먼트를 붙일 대상 부모 선택
let targetul = document.getElementById('friends');
targetul.appendChild(li); // friends ID를 가진 ul 자식들 맨끝에 새로 만든 <li> 태그를 붙임.
//이동할 엘리먼트 선택
let sourceli = document.querySelector('ul#friends li:first-child');
//삽입할 위치 기준 엘리먼트 선택
let targetli = document.querySelector('ul#friends li:last-child');
//붙여넣는 위치에 주의해야 함
//1뎁스 ul 마지막 요소가 아니라 중첩된 내부 ul 끝에 붙게됨.
targetli.after(sourceli);
// '무지' 앞에 붙은 이유는 '무지'는 스크립트로 추가된거고 원래 doument에서는 '초록괴수'가 끝이니까
//문자열을 가지는 li 엘리먼트를 생성해 자식 엘리먼트로 붙임
var appendli = document.createElement("li");
appendli.append("왕눈이");
document.querySelector('ul li ul').append(appendli);
//마지막 차일드 엘리먼트 선택
let selected = document.querySelector('.icons li span:last-child');
//추가할 span 노드 생성-
let newspan = document.createElement('span');
let newspantext = document.createTextNode('라지 ');
newspan.appendChild(newspantext);
//마지막 엘리먼트 앞에 추가
selected.parentNode.insertBefore(newspan,selected);
//animal 클래스 속성을 가진 li 엘리먼트들을 선택
let items = document.querySelectorAll('.animal');
// 나머지 파라메터로 newfriend ID 를 가지는 새 ul 에 선택한 엘리먼트들을 추가
document.querySelector('#newfriends').prepend(...items);
엘리먼트 교체
선택한 엘리먼트 자체를 새로 생성한 엘리먼트, 또는 선택한 다른 엘리먼트로 바꿀 수 있습니다.
replaceChild() 메서드는 현재 엘리먼트의 하위에 있는 자식 엘리먼트 1개를 새 엘리먼트로 교체합니다.
let p = document.createElement('p'); // <p> 엘리먼트 생성
let ptext = document.createTextNode('문단 텍스트 내용'); // 문단 내용 텍스트 생성
p.appendChild(ptext); // 문단 내용을 <p>에 텍스트 채움
let originalp = document.getElementById('paragraphid'); // 교체할 엘리먼트 선택
let oldp = originalp.parentNode.replaceChild(p, originalp); // 교체할 엘리먼트의 부모 태그에서 자식 엘리먼트 교체
DOM 요소 스타일 변경
좀전에 살펴본 Element.prototype.style 방식은 실무에서 제대로 작동하지 않을 수 있다.
이 방식은 인라인 스타일만 반환하기 때문이다.
실제로는 외부 *.css 파일, 외부 *.js 파일, 미디어쿼리, 이벤트, css 전처리기 사용 등, CSS 스타일을 외부에서 변경을 가하는 경우가 많다.
이때 window.getComputedStyle 메서드를 이용하면, 해당 요소에 전역적으로 적용된 모든 형태의 style을 반환 할 수 있다.
그리고 이 메서드는 두번째 인수를 거의 생략하지만, :after/:before 가상 선택자를 전달하면, 그에 대한 것도 모두 볼 수 있다.
window.getComputedStyle 메소드는 인자로 주어진 요소의 모든 CSS 프로퍼티 값을 반환한다.
<html>
<head>
<style>
.box {
width: 100px;
height: 50px;
background-color: red;
border: 1px solid black;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
const box = document.querySelector('.box');
const width = getStyle(box, 'width');
const height = getStyle(box, 'height');
const backgroundColor = getStyle(box, 'background-color');
const border = getStyle(box, 'border');
console.log('width: ' + width);
console.log('height: ' + height);
console.log('backgroundColor: ' + backgroundColor);
console.log('border: ' + border);
/*
* 요소에 적용된 CSS 프로퍼티를 반환한다.
* @param {HTTPElement} elem - 대상 요소 노드.
* @param {string} prop - 대상 CSS 프로퍼티.
* @returns {string} CSS 프로퍼티의 값.
*/
function getStyle(elem, prop) {
return window.getComputedStyle(elem, null).getPropertyValue(prop);
}
</script>
</body>
</html>
# 참고 자료
http://tcpschool.com/javascript/js_dom_element
https://poiemaweb.com/js-dom
https://ko.javascript.info/modifying-document
https://oursmalljoy.com/js-dom/
https://blogpack.tistory.com/670
이 글이 좋으셨다면 구독 & 좋아요
여러분의 구독과 좋아요는
저자에게 큰 힘이 됩니다.