DOM (Document Object Model) 이해하기
DOM은 프로그래머 관점에서 바라본 HTML이다.
HTML 요소를 Object(JavaScript Object)처럼 조작(Manipulation)할 수 있는 모델이다.
JavaScript를 사용할 수 있으면 DOM으로 HTML을 조작할 수 있다 는 말이다.
쉽게 얘기하자면, JavaScript를 이용해서 엘리먼트의 속성값을 얻어내거나 변경하는 방법이라고 이해하면 된다.
하지만 DOM을 JavaScript라고 이해하면 안된다. 단지 JavaScript를 통해 접근하는 것 뿐이다.
이 DOM을 만들기 위해 뛰어난 웹 개발자들이 모여서 HTML을 철저히 분석했다.
분석한 내용으로 HTML의 아주 작은 부분까지 접근할 수 있는 구조(Model / Structure)를 만들어냈다.
이렇게 만들어진 구조를 이용해서 HTML로 구성 페이지를 동적으로 움직이게 만들 수 있게 되었다.
이러한 DOM을 활용하면 웹 페이지를 조금 더 다이나믹하게 만들 수 있다.
HTML에서 JavaScript가 적용되는 방식
HTML에 JavaScript를 적용하기 위해서는 script 태그를 이용한다.
아래의 경우 파일과 같은 디렉토리에 있는 js 파일을 불러온다.
<script src="mySimpleJS.js"></script>
웹 브라우저가 작성된 코드를 해석하는 과정에서 위와 같은 script 요소를 만나면 웹 브라우저는 HTML 해석을 잠시 멈춘다.
그 다음 script 요소를 먼저 실행하게 된다.
여기서 반드시 명심해야 할 점은 script 요소는 등장과 함께 실행된다는 사실 이다.
script 태그를 추가하는 두 가지 대표적인 방법이 있다.
하나는 head 태그에 추가하는 방법이고, 다른 하나는 body 태그가 끝나기 전에 추가하는 방법이다.
그 중, body 태그가 끝나기 전에 추가하는 방법을 활용해야 모든 기능이 정상 작동한다.
자식 엘리먼트 찾기
브라우저에서 작동되는 JavaScript 코드에서는 어디에서나 documnet 객체를 조회할 수 있다.
DOM 구조를 조회할 때에는 console.dir이 유용하다.
왜냐하면 cosole.log와 달리 console.dir은 DOM을 객체의 모습으로 출력하기 때문이다.
console.dir(document.body)와 같이 사용할 수 있다.
여기서 document는 DOM을 대표하는 객체이다.
부모 엘리먼트 찾기
부모 엘리먼트를 조회하려면 자식 엘리먼트의 첫 번째 엘리먼트를 조회하면 된다.
console.dir(documnet.body.children[1]);
따로 변수 선언을 해서 정보를 저장해두면 주소를 참조하기 때문에 언제든지 접근할 수 있다.
let newContents = document.body.children[1];
console.dir 객체의 속성들
console.dir을 통해서 객체 형식으로 출력하면 객체의 속성들이 상당히 많은 것을 확인할 수 있다.
아래의 표를 통해서 특별히 기억해두면 좋을 속성들을 정리하였다.
속성 이름 | 속성 |
---|---|
tagName | 태그 이름 |
id | id |
classList | class 목록 |
className | class 문자열 |
attributes | 속성 객체 |
style | 스타일 객체 |
innerHTML, innrText, innerContent | 엘리먼트에 담긴 내용 |
value | form 입력 값 |
children | 자식 엘리먼트 |
parentElement | 부모 엘리먼트 |
childNodes | 자식 노드 |
dataset | data-* 속성에 담긴 값 |
onclick, onmouseover, onkeyup 등 | 이벤트 |
offsetTop, offsetLeft,scrollTop, scrollLeft,clientTop, clientLeft | 좌표 정보 (기준점) |
offsetWidth, offsetHeight,scrollWidth, scrollHeight,clientWidth, clientHeight | 크기 정보 (기준점) |
- attributes는 id와 class 등 태그 안의 속성들을 배열 형태로 보여준다.
- innerHTML은 태그를 포함해서 보여주고, textContent는 태그 안의 문자열만 보여주며, 이 둘은 안의 내용을 바꿀 수 있다.
- value를 통해 입력값을 받는 양식에서 편하게 입력값을 확인할 수 있다.
- children은 자식의 태그들만을 볼 수 있지만, childNodes는 안에 포함된 태그 이외의 요소들도 같이 보여준다.
- dataset은 화면에는 보이지 않는 데이터를 담아두고 싶을 때 사용할 수 있다.
<div data-user="steve" data-role="moderator" data-user-id="1">
Steve Lee
</div>
추가적인 DOM의 개념들
- Node는 Element의 상위 개념이다. DOM 관련 객체는 대부분 Node에서 파생되었다고 봐도 과언이 아니다. Node의 기능을 적용(implemets)한 객체는 여러 타입이 있는데, 그 중 가장 많이 사용되는 타입이 Element이다.
- DOM은 document 객체를 통해 HTML에 접근하며, BOM(Browser Object Model)는 window 객체를 통해 브라우저에 접근한다.
- 다른 언어도 DOM을 가지고 있지만 JavaScript의 DOM이 전통적으로 오래 쓰여왔으며 안정적이다.
DOM으로 HTML 조작하기
DOM으로 엘리먼트 선택하기
- 태그를 이용 : getElementsByTagName
- id를 이용 : getElementById
- class를 이용 : getElementsByClassName
- selector를 이용 : querySelector / querySelectorAll
=> selector를 이용해서 찾는 방법을 많이 사용하게 될 것이지만, 다른 사람의 코드를 이해하기 위해서 다른 방법들도 알아두어야 한다.
※ querySelectorAll과 같은 방법으로 조회한 엘리먼트들은 배열과 유사하지만 배열이 아니다. 정식 명칭은 Array-like Object이다.
DOM으로 생성하기
<div id="target">변경 전</div>
let target = document.querySelector("#target");
let newSpan = document.createElement("SPAN");
newSpan.className = "test";
// newSpan.classList.add("test"); 의 방법도 이용 가능
newSpan.innerHTML = "변경 후";
target.appendChild(newSpan);
<div id="target">
변경 전
<span class="test">변경 후</span>
</div>
DOM으로 덮어쓰기
innerHTML
<div id="target">변경 전</div>
let target = document.querySelector("#target");
target.innerHTML = `
<span>변경 후</span>
`;
<div id="target">
<span>변경 후</span>
</div>
가장 쓰기 쉬운 속성이지만 느리고 보안 위험이 있다. textContent가 더 안전하다.
DOM으로 삭제하기
innerHTML
let target = document.querySelector("#target");
target.innerHTML = "";
메소드를 이용한 엘리먼트 삭제
let target = document.querySelector("#target");
target.remove();
동일한 클래스 이름이면 삭제
const tweets = document.querySelectorAll(".tweet");
tweets.forEach(function (tweet) {
tweet.remove();
});
추가적인 메소드
setAttribute()
let aElement = document.createElement("a");
aElement.setAttibute("id", "javascipt");
aElement.textContent = "awesome";
위와 같이 setAttribute 메소드를 사용하면 aElement의 id 속성에 'javascript'라는 값을 넣어줄 수 있다.
removeChild()
document.querySelector("#world").remove();
document.querySelector("#world").remove(aElement);
위의 2개의 코드는 괄호 안의 값과 상관 없이 world라는 id를 가진 엘리먼트 전부를 지우게 된다.
document.querySelector("#world").removeChild(aElement);
이렇게 하면 world의 나머지 값들은 보존하고 자식 중에서 aElement만 지울 수 있게 된다.
버튼 클릭
function displayAlert() {
alert("코드스테이츠에 오신 것을 환영합니다");
}
document.querySelector("#apply").onclick = displayAlert;
document.querySelector("#apply").addEventListener("click", function () {
alert("코드스테이츠에 오신 것을 환영합니다");
});
이벤트 발생 시 작동하는 함수를 할당하는 방법은 두 가지가 있다.
DOM 객체에 onclick을 직접 지정하는 방법과 addEventListener라는 메소드를 사용해서 할당하는 방법이다.
주의해야 할 점은 함수를 할당할 시에 함수의 실행값을 할당하면 안되고 함수 그 자체를 할당해야 한다는 점이다.
function displayAlert() {
alert("코드스테이츠에 오신 것을 환영합니다");
}
document.querySelector("#apply").onclick = displayAlert();
이렇게 사용하면 정상적으로 작동하지 않는다.
DOM의 이벤트 객체
DOM을 통한 이벤트 호출 방법
직접 호출
let testInput = document.querySelector("#test-input");
testButton.onkeyup = validFunction;
function validFunction() {
console.log("키를 눌렀다 떼었습니다.");
}
이벤트 리스너를 통한 호출
let testInput = document.querySelector("#test-input");
testInput.addEventListener("keyup", function () {
console.log("키를 눌렀다 떼었습니다.");
});
//or
testInput.addEventListener("keyup", () => {
console.log("키를 눌렀다 떼었습니다.");
});
//or
testInput.addEventListener("keyup", sampleFunc);
function sampleFunc() {
console.log("키를 눌렀다 떼었습니다.");
}
DOM의 이벤트들
keyup
- 키를 놓을 때 이벤트 발생
- 이벤트 핸들러 속성 : onkeyup
keydown
- 키를 눌렀을 때 이벤트 발생
- 이벤트 핸들러 속성 : onkeydown
change
- 값이 변경된 후 포커스도 벗어났을 때 이벤트 발생
- 이벤트 핸들러 속성 : onchange
focus
- 포커스가 해당 엘리먼트에 갔을 때 이벤트 발생
- 이벤트 핸들러 속성 : onfocus
blur
- 포커스가 해당 엘리먼트에서 벗어났을 때 이벤트 발생
- 이벤트 핸들러 속성 : onblur
submit
- form 태그의 action이 발생하기 직전에 이벤트 발생
- 이벤트 핸들러 속성 : onsubmit
mouseover
- 해당 엘리먼트에 마우스가 올라왔을 때 이벤트 발생
- 이벤트 핸들러 속성 : onmouseover
mouseout
- 해당 엘리먼트에서 마우스가 나갔을 때 이벤트 발생
- 이벤트 핸들러 속성 : onmouseout
onload
- 브라우저가 페이지 load를 끝냈을 때 이벤트 발생
event.preventDefault
- 이벤트를 취소할 경우, 이벤트의 전파를 막지 않고 이벤트를 취소
function checkName(evt) {
var charCode = evt.charCode;
if (charCode != 0) {
if (charCode < 97 || charCode > 122) {
evt.preventDefault();
alert(
"소문자만 입력할 수 있습니다." + "\n" + "charCode: " + charCode + "\n"
);
}
}
}
DOM 추가 정리 (append / prepend)
- append는 이미 있는 자식 요소들의 뒤쪽에 추가
- prepend는 이미 있는 자식 요소들의 앞쪽에 추가
- 부모.append(자식)
- 자식.appendTo(부모)
★ 면접을 위해 - DOM이란?
=> HTML 문서를 객체의 형식으로 가공하기 위해 만든 멘탈 모델 / HTML의 트리 구조와 JavaScript 객체의 트리구조가 멘탈 모델이 같게 함
childNodes
자식들 중 엘리먼트와 엘리먼트가 아닌 텍스트 및 빈 공간들까지 같이 반환해준다.
children 메소드를 사용하면 자식들 중 엘리먼트만 반환된다.
document.documentElement.scrollTop
현재 스크롤의 위치를 알 수 있다.
document.addEventListener("scroll", handleScroll);
위와 같이 이벤트 핸들러와 사용할 수도 있다.
잘 만든 웹 사이트들을 구경해보면 스크롤을 내릴 때마다 화려한 화면 전환 효과를 볼 수 있는데, 바로 이 이벤트를 활용한 것이다.
addEventListener의 특징 한 가지
이벤트가 누적된다.
button.onclick = func; 같은 식으로 클릭 함수를 지정할 경우에는 한 가지 이벤트만 할당된다.
removeEventListener를 통해 겹친 이벤트 중 특정 이벤트를 지울 수도 있다.
event.preventDefault()
버튼 클릭과 같은 이벤트의 경우에 어떤 동작도 하지 않았으면서 웹 페이지를 새로 갱신하는 것을 방지한다.
이렇게 되면 기존에 하려던 것을 못하고 화면이 넘어가 버릴 수 있다.
'JavaScript > Vanilla' 카테고리의 다른 글
class, 그리고 JS의 객체 지향 (0) | 2021.07.27 |
---|---|
고차함수 (0) | 2021.07.27 |
객체의 얕은 복사와 깊은 복사 (0) | 2021.07.24 |
Hoisting (0) | 2021.07.24 |
Spread와 Rest 문법 (0) | 2021.07.24 |