🔸 HTML 요소를 Object(JavaScript Object)처럼 조작(Manipulation)할 수 있는 Model. 즉 HTML로 구성된 웹 페이지를 동적으로 움직이게 만들 수 있음.
🔸 <script>
: HTML에 JavaScript를 적용함.
<script src="myScriptFile.js"></script>
// HTML 파일과 같은 디렉토리에 존재하는 myScriptFile.js을 불러옴.
🔸 웹 브라우저가 작성된 코드를 해석하는 과정에서 <script>
요소를 만나면, 웹 브라우저는 HTML 해석을 잠시 멈추고, <script>
요소를 먼저 실행 (<script>
요소는 등장과 함께 실행).
<script>
요소를 추가하는 위치🔸 <head>
안쪽에 삽입하는 경우
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
<!-- script 요소 삽입 위치 -->
<script src="myScriptFile.js"></script>
</head>
<body>
<div id="msg">Hello JavaScript!</div>
</body>
</html>
🔸 <body>
요소가 끝나기 전에 삽입하는 경우
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<div id="msg">Hello JavaScript!</div>
<!-- script 요소 삽입 위치 -->
<script src="myScriptFile.js"></script>
</body>
</html>
// myScriptFile.js
console.log('welcome JavaScript'); // 출력
let msgElement = document.querySelector('#msg');
console.log(msgElement); // 출력실패
🔸 두 방식 모두 myScriptFile.js
내의 첫 번째 console.log
를 출력하지만, 두 번째 console.log
의 경우 1번 예시에서는 null이 출력됨.
script
요소가 head
요소 안쪽에 위치하므로, body
요소가 아직 완전히 로드되기 전에 myScriptFile.js
파일이 실행됨.myScriptFile.js
파일 안의 두 번째 console.log
문장은 body
요소 안에 있는 div
요소가 아직 로드되지 않아 해당 요소를 찾을 수 없기 때문에 null
이 출력. script
요소가 body
요소 안쪽에 위치하므로, body
요소의 모든 내용이 로드된 후 myScriptFile.js
파일이 실행됨. myScriptFile.js
파일 안의 두 번째 console.log
문장은 div
요소가 로드된 후 실행되므로 Hello JavaScript!
이 출력.🔸 따라서 HTML 요소를 이용하려면 body 요소 안쪽에 script 요소를 위치시키는 방법을 이용해야 함. 이 방법은 모든 요소가 로드된 후 자바스크립트 파일이 실행되므로, 자바스크립트 파일에서 HTML 요소를 조작하려는 경우 예기치 않은 동작을 방지.
<html>
<body>
<div id="nav">
<div class="logo"></div>
<div class="menu-wrapper">
<div class="menu"></div>
<div class="menu"></div>
<div class="menu"></div>
<div class="profile-photo"></div>
</div>
</div>
<div id="news-contents">
<div class="news-content-wrapper">
<div class="news-picture"></div>
<div class="news-title"></div>
<div class="news-description"></div>
</div>
</div>
<div id="footer"></div>
</body>
</html>
✔️ 3; id = nav
, news-contents
, footer
🔸 자바스크립트에서 DOM은 document
객체에 구현됨. 브라우저에서 작동되는 자바스크립트 코드에서는 어디에서나 document
객체를 조회가능.
🔸 console.dir()
: 주어진 JavaScript 객체의 모든 속성을 콘솔에서 볼 수 있는 방법.
console.dir(document.body);
console.dir(document.body.children); // body에서 자식 찾기
✔️ <body></body>
🔸 id가 news-contents
인 엘리먼트를 조회하려면, document.body.children
의 첫 번째 요소를 조회 (document.body.children[1]
).
🔸 document.body
의 children을 조회할 때마다, 매번 document.body
로부터 찾아가는 일은 정말 번거롭기 때문에 따로 변수 선언을 해서 이 정보를 저장해 두면, 주소를 참조하기 때문에 언제든지 접근할 수 있음.
let newsContents = document.body.children[1]
console.log(newsContents); // div#news-contents
🔸 특정 자식 요소의 부모 요소를 반환하는 데 사용되는 메서드
parentElement
: 현재 노드의 부모 요소 반환. 부모가 없거나 부모 노드가 DOM 요소가 아닌 경우 null
을 반환. (반환값은 언제나 DOM 요소
이거나 null
)parentNode
: DOM 트리에서 특정한 노드(주로 현재 다루고 있는 노드)의 부모 노드를 반환. 평상시(부모 노드가 엘리먼트인 경우)에는 거의 parentElement
와 똑같은 값을 반환하나, 특수한 경우(예를 들어 현재 노드가 documentElement
인 경우) 둘의 반환값이 달라짐. Document
, DocumentFragment
는 부모를 가질 수 없으므로 parentNode
는 항상를 null
을 반환. 또한 노드가 방금 생성되었고 아직 트리에 연결되지 않은 경우에도 null
을 반환. 🔸 document.getElementById('id')
: 해당 아이디 이름을 가진 모든 요소를 가져옴.
🔸 document.getElementsByTagName('tag')
: 해당 태그 이름을 가진 모든 요소를 가져옴. (매개변수 tag에 "*"
이 들어가면, 모든 태그가 검색)
🔸 document.getElementsByClassName('className')
: 해당 클래스 이름을 가진 모든 요소를 가져옴.
🔸 document.getElementsByName('name')
: name
속성값을 가진 요소들 중 해당 이름을 가진 요소들을 가져옴.
🔸 element.querySelectorAll('selector')
: element의 자식 요소 중 주어진 css 선택자에 대응하는 컬렉션를 모두 반환. :hover
, :active
같은 가상 클래스도 사용 가능. 정적인 컬렉션을 반환하므로 컬렉션이 한 번 확정되면 더는 늘어나지 않음.
🔸 element.querySelector('selector')
: 주어진 css 선택자에 대응하는 요소 중 첫 번째 요소를 반환.
🔸 element.matches('selector')
: 주어진 css 선택자와 일치하는지 여부를 판단, 일치한다면 true, 아니라면 false를 반환 (요소가 담겨있는 배열 등을 순회해 원하는 요소만 걸러내고자 할 때 유용).
🔸 element.closest('selector')
: 자기 자신을 포함해 css 선택자와 일치하는 가장 가까운 조상 요소를 찾음 (조상 요소가 아닐경우 null
반환).
<div>
요소를 포함해서, 모든 자식 요소의 class 이름을 console.log를 사용하여 확인하는 방법은 무엇인지 의사코드로 작성하세요.
🔸 이런 자료 구조를 컴퓨터 공학에서는 트리 구조라고 함.
🔸 부모가 자식을 여러 개 가지고, 부모가 하나인 구조가 반복되는 점. 부모가 가진 하나 또는 여러 개의 자식 엘리먼트를 조회하는 코드를 작성한다면, 여러 번 반복해서 실행하는 코드가 필요.
🔸 document.createElement('tagName')
: 태그 이름을 인자로 전달해 요소 노드 생성.
🔸 document.createTextNode('text')
: 텍스트를 인자로 전달해 텍스트 노드 생성.
🔸 Element.before('tagName or text')
: 선택한 노드의 앞에 추가.
🔸 Element.after('tagName or text')
: 선택한 노드의 뒤에 추가.
🔸 Element.prepend('tagName or text')
: 선택한 엘리먼트의 첫 번째 자식 엘리먼트 앞에 추가.
🔸 Element.append('tagName or text')
: 선택한 엘리먼트의 마지막 자식 엘리먼트 뒤에 추가.
💡
createTextNode()
,createElement()
이용한 노드 생성 및 추가 과정을before()
,after()
,prepend()
,append()
1개의 메서드로 줄여서 사용할 수 있기 때문에 코드 최적화 및 가독성 향상에 유리.
💡 가급적before()
,after()
메서드를 사용해 DOM을 제어하는 것을 추천.
JavaScript에서 어떤 작업의 결과를 담으려면 변수를 선언하고 어떤 작업의 결과를 변수에 할당.
const tweetDiv = document.createElement('div')
- 현재
<div>
는 현재 아무것도 연결되어 있지 않은 노드이므로, 실제 웹 페이지 상에도 보이지 않음. 따라서 APPEND 해서 트리구조에 연결 해야함.
🔸 새로 생성한 요소를 트리 구조와 연결.
🔸 Element.prepend('tagName or text')
: 현재 엘리먼트의 첫번째 자식 엘리먼트 앞에 추가.
🔸 Element.append('tagName or text')
: 현재 엘리먼트의 자식 엘리먼트 중 마지막 엘리먼트 뒤에 추가.
변수
tweetDiv
를<body>
에 연결.document.body.append(tweetDiv)
🔸 Element.onwerDocument
: 해당 요소가 속한 문서의 document
객체를 반환. 이를 이용해 Document 객체의 메서드를 요소에 사용할 수 있음.
🔸 querySelector('css selector')
: DOM으로 선택자 조회하는 방법으로 해당 선택자 중 첫 번째 요소를 조회. (선택자로는 HTML 요소("div"
), id("#tweetList"
), class(".tweet"
)가 가장 많이 사용)
querySelector
로 클래스 이름이tweet
인 HTML 엘리먼트 중 첫 번째 엘리먼트를 조회.const oneTweet = document.querySelector('.tweet')
🔸 여러 개의 요소를 한 번에 가져오기 위해서는, querySelectorAll
을 사용. 이렇게 조회한 HTML 요소들은 배열처럼 for문을 사용 가능.
💡
querySelectorAll
로 조회한 HTML 요소들은 배열이 아님‼️ 이런 '배열 아닌 배열'을 유사 배열, 배열형 객체 등 다양한 이름으로 부름.
🔸 정식 명칭은 Array-like Object
🔸get
으로 시작하는 DOM 조회 메서드들은querySelector
와 비슷한 역할을 하는 오래된 방식임.const getOneTweet = document.getElementById('container') const queryOneTweet = document.querySelector('#container') console.log(getOneTweet === queryOneTweet) // true
🔸 textContent
: 엘리먼트에 텍스트 노드 추가.
div
요소에'dev'
텍스트 추가const oneDiv = document.createElement('div'); oneDiv.textContent = 'dev'; console.log(oneDiv) // `<div>dev</div>`
🔸 Element.classList.add('class')
: 지정된 요소에 클래스를 추가. 이미 클래스가 존재하다면 클래스 무시.
🔸 Element.classList.remove('class')
: 지정된 요소에 클래스를 제거. 클래스가 존재하지 않더라도 에러를 발생시키지 않음.
🔸 Element.classList.item( Number )
: Number
(인덱스)를 이용해 클래스 값을 반환
🔸 Element.classList.contains( 'String' )
: 지정한 클래스 값이 엘리먼트의 class
속성에 존재하는지 확인
🔸 Element.classList.replace( 'oldClass', 'newClass' )
: 존재하는 클래스를 새로운 클래스로 교체.
🔸 Element.classList.toggle( 'String', force )
: 하나의 인수만 있을때, 클래스가 존재하면 제거하고 false
반환, 존재하지 않으면 클래스를 추가하고 true
를 반환. 두번째 인수가 있을때, 두번째 인수가 true
로 평가되면 지정한 클래스 값을 추가하고 false
로 평가되면 제거.
div
요소에tweet
클래스 추가.oneDiv.classList.add('tweet') console.log(oneDiv) // <div class="tweet">dev</div>
🔸 Element.setAttribute(name, value)
: 지정된 요소의 속성 값을 설정. 속성이 이미 있으면 값은 업데이트되고, 속성이 없다면 지정된 이름과 값으로 새 속성이 추가.
name
: 값을 설정할 속성의 이름을 지정하는 문자열. (속성 이름은 HTML 문서의 HTML 요소에서 setAttribute()
가 호출될 때 모두 자동으로 소문자로 변환됨)value
: 속성에 할당할 값이 포함된 문자열. 지정된 문자열이 아닌 값은 자동으로 문자열로 변환.🔸 Element.getAttribute()
: 속성의 현재 값 반환
🔸 Element.removeAttribute()
: 속성 제거
🔸 .remove()
: 삭제하려는 요소의 위치를 알고 있는 경우 사용.
append 했던 요소 삭제
container.append(oneDiv) oneDiv.remove()
🔸 여러 개의 자식 요소를 지우려면 innerHTML
을 이용, 모든 자식 요소를 지울 수 있음.
컨테이너의 모든 자식 요소를 지우기
document.querySelector('#container').innerHTML = '';
innerHTML
은 보안에서 몇 가지 문제를 가지고 있음.
- HTML 삽입 공격(XSS; 크로스 사이트 스크립팅) : 사용자로부터 입력을 받아 동적으로 페이지에 추가할 때, 사용자가 악의적인 스크립트를 주입할 수 있음.
innerHTML
에 스크립트 코드를 직접 삽입하면, 해당 코드가 실행될 수 있음.- 대신에,
Node.textContent
혹은Element.setHTML()
사용.
🔸 removeChild
: 자식 요소를 지정해서 삭제하는 메서드. 모든 자식 요소를 삭제하기 위해, 반복문(while, for, etc.)을 활용할 수 있음.
자식 요소가 남아있지 않을 때까지, 첫 번째 자식 요소를 삭제.
const container = document.querySelector('#container'); while (container.firstChild) { container.removeChild(container.firstChild); }
removeChild
와while
을 이용해 자식 요소를 삭제하면, 제목에 해당하는 H2"Tweet List"
까지 삭제됨. 이를 방지하기 위한 방법들- 자식 요소가 1개만 남을 때까지, 마지막 자식 요소 제거
const container = document.querySelector('#container'); while (container.children.length > 1) { container.removeChild(container.lastChild); }
- 클래스 이름이
tweet
인 요소만 찾아서 지우는 방법.const tweets = document.querySelectorAll('.tweet') tweets.forEach(function(tweet){ tweet.remove(); }) // or for (let tweet of tweets){ tweet.remove() }
🔸
Array.forEach(func)
: 각 배열 요소에 대해 제공된 함수를 한 번씩 실행.
🔸 event 객체 : 해당 이벤트가 발생한 HTML 요소의 정보와 발생한 이벤트에 대한 정보 (예를 들어, 클릭된 요소의 위치, 마우스 버튼, 클릭된 시간 등을 포함).
🔸 이벤트가 발생되면 event 객체는 동적으로 생성되며 이벤트를 처리할 수 있는 이벤트 핸들러에 인자로 전달됨.
🔸 Event.target
: 실제로 이벤트를 발생시킨 요소 (버블링에 인해 다른 타겟이 인식될 수 있음). ex) 클릭한 버튼 요소
🔸 Event.currentTarget
: 코드에서 이벤트에 바인딩된 DOM 요소. addEventListener
앞에 기술된 객체. 이벤트 핸들러 함수 내에서 currentTarget
과 this
는 항상 일치함.
🔸 이벤트가 발생하기 전에는 실행되지 않다가 이벤트가 발생되면 실행되는 함수.
🔸 이벤트를 이벤트 대상의 태그 속성으로 지정하는 것.
버튼을 클릭했을 때 Hello world를 경고창으로 출력
<input type="button" onclick="alert('Hello world');" value="button" />
🔸 이벤트가 발생한 대상을 필요로하는 경우 this를 통해서 참조할 수 있음.
버튼을 클릭했을 때 Hello world button를 경고창으로 출력
<input type="button" onclick="alert('Hello world, ' + this.value);" value="button" />
🔸 script탭에서 함수를 만들고, 태그에 지정하는 방식
버튼을 클릭했을 때 Hello world button를 경고창으로 출력
html<button onclick="myHandler1(); myHandler2();">Click me</button> <script> function myHandler1() { alert('myHandler1'); } function myHandler2() { alert('myHandler2'); } </script>
🔸 인라인 이벤트 핸들러의 경우, 이벤트 핸들러는 일반 함수로서 호출되므로 이벤트 핸들러 내부의 this
는 전역 객체 window
를 가리킴.
🔸 이벤트 대상에 해당하는 객체의 프로퍼티로 이벤트를 등록하는 방식.
🔸 오직 하나의 이벤트 핸들러만을 바인딩할 수 있음.
<button class="btn">Click me</button>
<script>
const btn = document.querySelector('.btn');
// 이벤트 핸들러 프로퍼티 방식은 이벤트에 하나의 이벤트 핸들러만을 바인딩할 수 있다
// 첫번째 바인딩된 이벤트 핸들러 => 실행되지 않는다.
btn.onclick = function() {
alert('① Button clicked 1');
};
// 두번째 바인딩된 이벤트 핸들러
btn.onclick = function() {
alert('① Button clicked 2'); // << 실행결과
};
</script>
💡 인라인 방식에 비해 HTML과 JS를 분리할 수 있다는 점에서 선호되지만,
addEventListener
방식이 더 추천됨.
🔸 이벤트 핸들러 프로퍼티 방식의 경우, 이벤트 핸들러는 메소드이므로 이벤트 핸들러 내부의 this
는 이벤트에 바인딩된 요소를 가리킴. (이벤트 객체의 currentTarget 프로퍼티와 같음)
🔸 대상 DOM 요소에 이벤트를 바인딩하고 해당 이벤트가 발생했을 때 실행된 콜백함수 (이벤트 핸들러)를 지정함.
🔸 하나의 이벤트에 대해 하나 이상의 이벤트 핸들러를 추가할 수 있음.
🔸 캡처링과 버블링을 지원함.
🔸 HTML 요소뿐만아니라 모든 DOM 요소 (HTML, XML, SVG)에 대해 동작함. 브라우저는 웹 문서(HTML, XML, SVG)를 로드한 후, 파싱하여 DOM을 생성함.
🔸 이벤트 핸들러를 선언할 때, event 객체를 전달받을 첫번째 매개변수를 명시적으로 선언하여야 함.
<input type="button" id="target1" value="button1" />
<input type="button" id="target2" value="button2" />
<script>
var t1 = document.getElementById('target1');
var t2 = document.getElementById('target2');
function btn_listener(event){ // event 객체를 전달받을 첫번째 매개변수 "event"
switch(event.target.id){
case 'target1':
alert(1);
break;
case 'target2':
alert(2);
break;
}
}
t1.addEventListener('click', btn_listener); // btn_listener()가 아니다.
t2.addEventListener('click', "btn_listener()"); // btn_listener()를 쓰려면 문자열로 감싼다.
</script>
💡 이벤트리스너 두번째 인수로 함수를 넣어주는데,
함수()
로 쓰게 되면 리턴값을 넣게되므로, 함수 그 자체를 입력하거나"함수()"
처럼 함수를 문자열로 감싸면 됨.
🔸 addEventListener 방식의 경우, 이벤트 핸들러는 콜백 함수이지만 이벤트 핸들러 내부의 this
는 이벤트에 바인딩된 요소(currentTarget)를 가리킴.
📍
onclick
vsaddEventListener
onclick
는 하나의 이벤트 핸들러만을 할당할 수 있으며, 추가할 경우 새롭게 덮여쓰여짐.- 반면에
addEventListener
는 여러 개의 이벤트 핸들러를 등록할 수 있음.removeEventListener
를 사용하여 특정 이벤트 핸들러를 제거할 수 있음.
🔸 JavaScript는 프로그래밍 언어 중 하나로, 웹 브라우저에서 실행되는 스크립트 언어. 웹 페이지의 동적인 기능을 구현하고 사용자와 상호작용할 수 있음.
🔸 DOM(Document Object Model)은 HTML, XML 또는 XHTML 문서를 나타내는 표준 객체 모델.
🔸 웹 브라우저는 HTML 문서를 렌더링하는 과정에서 DOM을 생성하며, JavaScript는 이 DOM을 사용하여 HTML 요소를 선택하고 조작가능.
🔸 즉, JavaScript는 DOM을 통해 HTML 문서에 접근하고 조작할 수 있는 인터페이스를 제공. 따라서 JavaScript는 프로그래밍 언어이며, DOM은 HTML 문서를 나타내는 표준 객체 모델, JavaScript를 사용하여 DOM을 조작하여 HTML 문서에 대한 동적인 기능을 구현가능.
🔸 Document.createDocumentFragment()
: 메모리 상에 존재하는 비어있는 문서 조각(Document Fragment)을 생성하는 DOM 메서드.
🔸 Document Fragment는 실제 DOM에 추가되기 전에 메모리에서 조작 및 수정할 수 있는 가벼운 컨테이너 역할을 함.
🔸 즉, 여러 개의 DOM 조작을 하나의 작업으로 묶어서 처리할 수 있어서 렌더 트리에 대한 갱신 횟수를 줄일 수 있음. 이로 인해 브라우저 성능이 향상됨.
function createFragment() {
const fragment = document.createDocumentFragment();
for (let i = 1; i <= 5; i++) {
const newElement = document.createElement('p');
newElement.textContent = `Paragraph ${i}`;
fragment.appendChild(newElement);
}
return fragment;
}
// container에 document fragment 추가
const container = document.getElementById('container');
container.appendChild(createFragment()); // 실제 DOM 조작이 일어나는 시점
🔸 모두 DOM에서 사용되는 객체, Node
는 DOM에서 모든 노드 유형의 기본 클래스이며, Document, Element, Text, Comment 등 모든 노드 타입은 Node를 상속. Element
는 Node의 하위 클래스이며, Element 객체는 요소 노드를 나타냄.
🔸 Element 객체는 Node의 하위 클래스이므로 Node의 모든 속성과 메서드를 상속하며, 추가적으로 Element에는 요소 노드의 태그 이름, 속성 등과 같은 특수한 속성과 메서드가 있음.
🔸 children
: 부모 요소의 자식 요소 노드들을 담고 있는 요소 노드의 컬렉션(collection)을 반환. 자식 노드 중에서 요소 노드만을 가져옴 (텍스트 노드, 주석 노드 등은 제외, HTML 코드에서 공백이나 줄바꿈 같은 요소 노드가 아닌 것들은 반환 안됨).
🔸 childNodes
의 차이: 부모 요소의 모든 자식 노드들을 담고 있는 노드의 컬렉션을 반환. 자식 노드 중에서 요소 노드 뿐만 아니라 텍스트 노드, 주석 노드 등 모든 노드를 가져옴. HTML 코드에서 공백이나 줄바꿈 같은 요소 노드가 아닌 것들도 반환.
🔸 remove()
: 삭제할 요소를 직접 선택하여 삭제.
🔸 parent.removeChild(child)
: 부모 노드를 선택하여 그 하위 요소를 삭제.
🔸 요소가 이미 다른 부모에 속한 상태에서 다른 부모에게 다시 추가되면, 그 요소는 이전 위치에서 제거되어 새로운 위치로 이동.
🔸 offsetTop
: 상위 요소에서 현재 요소의 Y축 위치를 반환
🔸 offsetLeft
: 상위 요소에서 현재 요소의 X축 위치를 반환
🔸 offsetWidth
: 요소의 가로폭(Width)을 반환 (보더/패딩 포함).
🔸 offsetHeight
: 요소의 높이(Height)를 반환 (보더/패딩 포함).