서버에서는 ES6 기능 중 무엇이 지원되는지 확실히 알 수 있고, 자바스크립트 엔진을 선택할 수 있습니다. 반면 웹에서는 작성한 코드를 실행할 자바스크립트 엔진을 선택하는 것은 불가능하고, 어떤 브라우저인지 알 수 있는 믿을 만한 정보조차 없습니다. 사용자의 환경을 컨트롤 하지 않는 한 ES5를 사용해야 합니다. 트랜스컴파일을 통해 ES6를 ES5로 바꾸면됩니다.
18.2 문서 객체 모델
DOM 즉 문서객체모델은 HTML 문서의 구조를 나타내는 표기법인 동시에 브라우저가 HTML 문서를 조작하는 핵심입니다. DOM은 트리 구조로 표현합니다. DOM 트리는 노드(node)로 구성됩니다. 루트 노트를 제외한 모든 노드엔 부모가 있고, 자식노드는 있어도 되고 없어도 됩니다. 루트 노트는 문서(document)이며, 자식 노드는 html요소 하나 뿐입니다. DOM 트리의 모든 노드는 Node 클래스의 인스턴스 입니다. Node 객체에는 트리 구조를 나타내는 parentNode와 childNodes 프로퍼티, 자신에 대한 프로퍼티인 nodeName과 nodeType 프로터피가 있습니다. DOM은 노드로만 구성됩니다. 하지만 모든 노드가 html 요소는 아닙니다. 예를 들어, 문단 태그 p는 html 요소지만, p태그의 텍스트는 텍스트 노드입니다. 노드는 거의 html 요소이고, ‘요소’라고 하면 그건 ‘요소 노드’를 가리킵니다.
<!DOCTYPE html> <htmllang="en"> <head> <metacharset="UTF-8" /> <metaname="viewport"content="width=device-width, initial-scale=1.0" /> <metahttp-equiv="X-UA-Compatible"content="ie=edge" /> <title>Simple html</title> <style> .callout { border: 1px solid #ff0080; margin: 2px 4px; padding: 2px 6px; } .code { background: #ccc; margin: 1px 2px; padding: 1px 4px; font-family: monospace; } </style> </head> <body> <header> <h1>Simple Html</h1> </header> <divid="content"> <p>This is a <i>simple</i> HTML file.</p> <divclass="callout"> <p>this is as fancy as...we'll get!</p> </div> <p> IDs (such as <spanclass="code">#content</span>) are unique (there can only be one per page). </p> <p> Classes (such as <span>.callout</span>) can be used on many elements </p> <divid="callout2"class="callout fancy"> <p>A single HTML element can have multiple classes</p> </div> </div> </body> </html>
1 2 3 4 5 6 7 8
//document에서 시작해서 DOM 전체를 순회하며 콘솔출력 functionprintDOM(node, prefix) { console.log(prefix + node.nodeName); for (let i = 0; i < node.childNodes.length; i++) { printDOM(node.childNodes[i], prefix + "\t"); } } printDOM(document, "");
모든 노드에는 nodeType, nodeName 프로퍼티가 있다. nodeType은 그 노드가 어떤 타입인지 나타내는 정수입니다.
18.3 용어 사용
부모 노드는 직접적인 부모 즉 바로 윗 단계를 말한다. 자식 노드 역시 직접적인 자식이다. 자손은 자식, 자식의 자식등을 발한다. 조상은 부모, 부모의 부모 등을 말한다.
18.4 get 메서드
document.getElementById는 id를 이용해 요소를 찾는다. document.getElementsByClassName는 주어진 클래스 이름에 해당하는 요소를 찾는다. document.getElementsByTagName은 주어진 태그 이름에 해당하는 요소를 찾는다.
18.5 DOM 요소 쿼리
document.querySelector와
document.querySelectorAll은 css 선택자를 사용해 요소를 찾는 메서드이다.
18.6 DOM 요소 조작
모든 요소에는 textContent와 innerHTML 프로퍼티가 있다. 이 프로퍼티를 통해 요소의 콘텐츠에 접근하거나 수정 할 수 있다. textContent는 HTN 태그를 제거한 순수한 텍스트 데이터만 제공하며, innerHTML을 통해 HTML 태그를 수정하면 DOM이 그에 맞게 변경된다.
1 2 3 4 5 6
// ## 18.6 DOM 요소 조작 const para1 = document.getElementsByTagName("p")[0]; para1.textContent; //"This is simple HTML..." para1.innerHTML; //"This is simple HTML..."" para1.textContent = "Modified HTML file"; para1.innerHTML = "Modified HTML file";
18.7 새 DOM 요소 만들기
document.createElement로 새 노드를 만들 수 있다. 이 메서드는 새 요소를 만들지만 DOM 요소에 추가하지는 않습니다. DOM에 추가하는건 따로 해야한다. insertBefore과 appendChild 메서드를 사용해 DOM에 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 13
//요소 생성 const p1 = document.createElement("p"); const p2 = document.createElement("p");
//요소에 텍스트 생성 p1.textContent = "i was created dynamically"; p2.textContent = "i was also created dynamically";
요소의 스타일을 바꿀땐 그에 맞는 css 클래스를 새로 만들고, 그 클래스를 원하는 요소에 지정 classList.add 메서드로 클래스 추가 가능 classList.remove 메서드로 클래스 제거 가능
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
functionhighlightParas(containing) { if (typeof containing === "string") containing = newRegExp(`\\b${containing}\\b`, "i"); const paras = document.getElementsByTagName("p"); console.log(paras); for (let p of paras) { if (!containing.test(p.textContent)) continue; p.classList.add("highlight"); } } highlightParas("unique");
//클래스 제거 functionremoveParaHighlights() { const paras = document.querySelectorAll("p.highlight"); for (let p of paras) { p.classList.remove("highlight"); } }
모든 요소에는 addEventListener라는 메서드가 있다. 이 메서드를 통해 이벤트가 일어났을 때 호출할 함수를 저장할 수 있다. 호출할 함수는 Event 타입의 객체 하나만을 매개변수로 받는다. 이벤트 객체에는 해당 이벤트에 관한 정보가 모두 존재 click 이벤틍[는 클릭한 좌표를 나타내는 clinetX, clientY 프로퍼티가 있고, 이벤트가 일어난 요소를 나타내는 target 프로퍼티가 있다. 기본 핸들로가 지정된 이벤트도 있다. 링크 등이 그 예제 이런 기본 이벤트 핸들러를 막기위해선 preventDefault()를 사용
18.10.1 이벤트 버블링과 캡쳐링
html은 계층적이므로 이벤트를 꼭 한곳에만 처리해야하는 건 아니다. 먼 조상부터 시작하는 캡쳐링과 이벤트가 일어난 요소에서 시작해 거슬러 올라가는 버블링이 있다. 이벤트 핸들러에는 다른 핸들러가 어떻게 호출될지 영향을 주는 세 가지 방법이 있다. preventDefault : 이벤트를 취소한다. 취소된 이벤트가 전달은 되지만 defaultPrevented 프로퍼티가 true로 바뀐째 전달되고, 브라우저는 defaultPrevented :true로 바뀐 이벤트를 무시하므로 아무일도 일어나지 않는다. stopPropagation : 이벤트를 현재 요소에서 끝내고 더는 전달되지 않게 막는다. 즉 해당 요소에 연결된 이벤트 핸들러는 동작하지만, 다른 요소에 연결된 이벤트 핸들러는 동작하지 않습니다. stopImmediatePropagation : 다른 이벤트 핸들러, 심지어 현재 요소에 연결된 이벤트 핸들러도 동작하지 않게 막는다.
//3.버튼 캡쳐 단계에서 이벤트 전달을 중지해보자 addEventLogger(body, "capture"); addEventLogger(body, "bubble"); addEventLogger(div, "capture", "cancel"); addEventLogger(div, "bubble"); addEventLogger(button, "capture", "stop"); addEventLogger(button, "bubble"); /* //3.버튼을 눌렀을때 결과 capture : BODY capture : DIV(calceled) capture : BUTTON(calceled) bubble : BUTTON(calceled) */ //3.결론 : 버튼 요소에서 이벤트 전달이 멈춤 캡쳐링까지는 진행하고 멈추게 했지만, 버튼의 버블링 이벤트는 여전히 발생한다. 다만 div나 body 요소는 이벤트 버블링을 받지 못한다.
//4.버튼의 캡쳐단계에서 즉시 멈추게 만들기 addEventLogger(body, "capture"); addEventLogger(body, "bubble"); addEventLogger(div, "capture", "cancel"); addEventLogger(div, "bubble"); addEventLogger(button, "capture", "stop!"); addEventLogger(button, "bubble"); /* //4.버튼을 눌렀을때 결과 capture : BODY capture : DIV(calceled) capture : BUTTON(calceled) */ //4.결론 : 캡쳐 단계에서 이벤트 전달이 완전히 멈춰서 그 후로는 아무일도 발생하지 않는다. </script> </body> </html>
18.10.2 이벤트 카테고리
드래그 이벤트 : dragstart, drag, dragend, drop 등의 이벤트를 통해 드래그 앤 드롭 인터페이스를 만들 수 있다.
포커스 이벤트 : 사용자가 폼 필드 같은 편집 가능한 요소를 조작하려 할때 사용 focus : 필드에 들어갈 때 사용 blur : 필드에서 나올 때 사용 change :사용자가 필드의 내용을 바꿀때
폼 이벤트 : 사용자가 전송 버튼을 클릭하거나, 엔터를 눌러서 폼을 전송할 때 submit 이벤트가 발생
입력 장치 이벤트 : 마우스 이벤트 : click외에도 mousedown, move, mouseup, mouseenter, mouseleave, mouseover, mousewheel 이벤트 등 키보드 이벤트 : keydown, keypress ,keyup 터치 이벤트 : 터치 장치의 터치 이벤트는 마우스 이벤트보다 우선
미디어 이벤트 : html 비디오, 오디오 플레이어에 관련한 이벤트로 pause, play 등
진행 이벤트 : 브라우저가 콘텐츠를 불러오는 과정에서 발생 load : 브라우저가 요소와 그에 연관된 자원을 모두 불러올 떄 발생
터치 이벤트 : 이벤트의 touches프로퍼티를 통해 터치를 지원
18.11 Ajax
비동기적 자바스크립트와 XML의 약어 Ajax를 통해 서버와 비동기적으로 통신하면 페이지 전체를 새로 고칠 필요 없이 서버에서 데이터를 받아 올 수 있다.