웹브라우저는 HTML 문서를 해석하고, 화면을 통해 해석된 결과를 보여준다. 해석한 HTML를 화면을 통해 보여주는 과정을 렌더링
이라 한다.
브라우저는 HTML코드를 해석해서 요소들을 트리 형태
로 구조화해 표현하는 문서(데이터)를 생성한다. 이를 DOM(Document Object Model)
이라 한다.
브라우저는 DOM
을 통해 화면에 웹 콘텐츠들을 렌더링
한다.
DOM
은 자바스크립트를 사용해서 웹 화면의 콘텐츠를 추가,수정,삭제하거나 이벤트를 처리할 수 있도록 프로그래밍 인터페이스
를 제공한다.
HTML에 JavaScript를 적용하기 위해서는 <script>
태그를 이용합니다. 아래의 경우 HTML 파일과 같은 디렉토리에 존재하는 myScriptFile.js
을 불러옵니다.
<script src="myScriptFile.js"></script>
웹 브라우저가 작성된 코드를 해석하는 과정에서 <script>
요소를 만나면, 웹 브라우저는 HTML 해석을 잠시 멈춥니다. HTML 해석을 잠시 멈춘 웹 브라우저는 <script>
요소를 먼저 실행합니다.
<script>
요소를 추가하는 두 가지 대표적인 사례가 존재합니다.
<head>
요소에 추가하는 방법</body>
가 끝나기 전에 추가하는 방법입니다.<head>
에 있으면 그 지점에서 html 해석을 멈춤으로 <body>
에 있는 id나 class를 제대로 끌고 올 수가 없고
<body>
에 모든 HTML의 해석이 끝난뒤에 실행됨으로 문제가 안 생긴다.
HTML을 지탱하는 것은 태그(tag)
입니다.
문서 객체 모델(DOM)에 따르면, 모든 HTML 태그
는 객체
입니다. 태그 하나가 감싸고 있는 ‘자식’ 태그는 중첩 태그(nested tag)
라고 부릅니다. 태그 내의 문자(text) 역시 객체
입니다.
이런 모든 객체는 자바스크립트를 통해 접근할 수 있고, 페이지를 조작할 때 이 객체를 사용합니다.
document.body
는 <body>
태그를 객체로 나타낸 것이죠.
HTML에서 객체의 정의란??
<!DOCTYPE HTML>
<html>
<head>
<title>사슴에 관하여</title>
</head>
<body>
사슴에 관한 진실.
</body>
</html>
DOM은 HTML을 태그 트리 구조로 표현한다.
트리에 있는 노드(node)는 모두 객체
이다.
태그는 요소 노드(element node)(혹은 그냥 요소)
이고, 트리 구조를 구성한다.
<html>은 루트 노드
가 되고,
<head>와 <body>는 루트 노드의 자식
이 된다.
요소 내의 문자는 텍스트(text) 노드
가 된다. 위 그림에서 #text를 확인해봐라. 텍스트 노드
는 문자열만 담는다. 자식 노드를 가질 수 없고, 트리의 끝에서 잎 노드(leaf node)
가 된다.
자동교정
기형적인 HTML을 만나면 브라우저는 DOM 생성과정에서 HTML을 자동으로 교정한다.<p>안녕하세요 <li>엄마 <li>그리고 <li>아빠
이렇게 닫는 태그가 없어도, 자동으로 빠진 부분을 채워넣어준다.
노드 타입은 총 열두 가지 인데, 실무에선 주로 다음 네 가지 노드를 다룬다.
문서 노드(document node)
요소 노드(element node)
★★★텍스트 노드(text node)
주석 노드(comment node)
주석(comment) 노드
js에서 주석처리를 해도 DOM에 나타난다. 이를 주석노드라고 한다.
물론 화면에는 나타나지 않는다.
Live DOM Viewer 에 들어가면 실시간으로 DOM구조를 볼 수 있다.
또는 개발자도구를 통해서 DOM구조를 확인 할 수 있다.
DOM에 수행하는 모든 연산은 document 객체에서 시작합니다. document 객체는 DOM에 접근하기 위한 '진입점’이죠. 진입점을 통과하면 어떤 노드에도 접근할 수 있습니다.
아래 그림은 DOM 노드 탐색이 어떤 관계를 통해 이루어지는지를 보여줍니다.
<html>
= document.documentElement
document를 제외하고 DOM 트리 꼭대기에 있는 문서 노드는 <html> 태그에 해당하는 document.documentElement입니다.
<body>
= document.body
document.body는 <body> 요소에 해당하는 DOM 노드로, 자주 쓰이는 노드 중 하나입니다.
<head>
= document.head
<head> 태그는 document.head로 접근할 수 있습니다.
childNodes
컬렉션은 텍스트 노드를 포함한 모든 자식 노드
를 담고 있습니다.childNodes 주의사항 (Array-like Object)
childNodes는 마치 배열 같아 보입니다. 하지만 childNodes는 배열이 아닌 반복 가능한(iterable, 이터러블) 유사 배열 객체인 컬렉션(collection) 입니다.
특징
1. for..of를 사용할 수 있습니다.
2. 배열이 아니기 때문에 배열 메서드를 쓸 수 없습니다.
만약 진짜 배열로 만들고 싶다면 Array.from을 사용하면 된다.
children
자식노드 중 요소 노드만 담고있다.
firstChild
와 lastChild
프로퍼티를 이용하면 첫 번째, 마지막 자식 노드에 빠르게 접근할 수 있습니다.
firstElementChild
와 lastElementChild
프로퍼티는 각각 첫 번째 자식 요소 노드
와 마지막 자식 요소 노드
를 가리킵니다.
elem.hasChildNodes()
자식 노드의 존재 여부를 검사할 때 사용
null
DOM에서null
값은'존재하지 않음’
이나'해당하는 노드가 없음’
을 의미합니다.
같은 부모를 가진 노드는 형제(sibling) 노드
라고 부릅니다.
<head>와 <body>는 대표적인 형제 노드입니다.
<html>
<head>...</head><body>...</body>
</html>
‘다음(next)’
혹은 '우측(right)'
에 있는 형제 노드입니다.‘이전(previous)’
혹은 '좌측(left)'
에 있는 형제 노드입니다.parentNode
는 부모 노드
를 담아준다.// <body>의 부모 노드는 <html>입니다
alert( document.body.parentNode === document.documentElement ); // true
// <head>의 다음 형제 노드는 <body>입니다.
alert( document.head.nextSibling ); // HTMLBodyElement
// <body>의 이전 형제 노드는 <head>입니다.
alert( document.body.previousSibling ); // HTMLHeadElement
parentNode
프로퍼티는 '종류에 상관없이 부모 노드’
를 반환합니다. parentElement
는 부모 요소 노드
를 가리킵니다.
previousElementSibling
과 nextElementSibling
은 형제 요소 노드
를 가리킵니다.
createElement()
document.createElement('div') // <div></div>
const tweetDiv = document.createElement('div')
변수명을 정하고 createElement
프로퍼티를 실행하면 변수명이 있는 하나의 element를 만들어진다. 이 element는 어디에 속해있지않고 떠다니는중이다.
떠다니는 element를 <body>안에 넣으려면 어떻게 해야할까?
식별자 tweetDiv로 지정된 <div></div>를 body의 자식요소가 되도록 만든다.
const tweetDiv = document.createElement('div')
document.body.append(tweetDiv)
DOM에서 특정한 class, id를 선택하려면 어떻게해야할까?
querySelector 에 '.tweet' 을 첫 번째 인자로 넣으면, 클래스 이름이 tweet 인 HTML 엘리먼트 중 첫 번째 요소
를 조회할 수 있습니다.
const oneTweet = document.querySelector('.tweet')
여러 개의 요소를 한 번에 가져오기 위해서는, querySelectorAll 을 사용한다. 이렇게 조회한 HTML 요소들은 배열처럼 for문을 사용하실 수 있습니다. (배열이 아닌것을 기억해두자!! 정식명칭 Array-like Object)
const oneTweet = document.querySelectorAll('.tweet')
querySelectorAll 주의사항
(Array-like Object)
위의childNodes 주의사항
과 같다.
childNodes는 마치 배열 같아 보입니다. 하지만 childNodes는 배열이 아닌반복 가능한(iterable, 이터러블) 유사 배열 객체인 컬렉션(collection)
입니다.
특징
1. for..of를 사용할 수 있습니다.
2. 배열이 아니기 때문에 배열 메서드를 쓸 수 없습니다.
만약 진짜 배열로 만들고 싶다면 Array.from을 사용하면 된다.
get으로 시작하는 메서드
get으로 시작하는 DOM 조회 메서드를 볼 수도 있다. 이런 메서드는 querySelector 와 비슷한 역할을 하는 오래된 방식이라고만 이해하면 됩니다.const getOneTweet = document.getElementById('container') const queryOneTweet = document.querySelector('#container') console.log(getOneTweet === queryOneTweet) // true
CREATE에서 생성한 div 요소를 container에 넣을 준비를 마쳤습니다. 다음 코드를 입력하면, container의 맨 마지막 자식 요소로 tweetDiv를 추가합니다.
const container = document.querySelector('#container')
const tweetDiv = document.createElement('div')'
container.append(tweetDiv)
태그에 class, id를 넣어주거나 문자열을 입력시키기 위해서 어떻게 해야할까?
우선 oneDiv라는 div태그를 만들고 (이 태그는 어디에 속해있지않고 떠다니는 중)
const oneDiv = document.createElement('div');
console.log(oneDiv) // <div></div>
onDiv태그안에 문자열을 입력해보자.
textContent
사용마지막 노드라면 이렇게 해도된다.
innerHTML = '텍스트 값'
oneDiv.textContent = 'dev';
console.log(oneDiv) // <div>dev</div>
oneDiv에 class를 설정해준다.
classList.add()
사용oneDiv.classList.add('tweet')
console.log(oneDiv) // <div class="tweet">dev</div>
클래스 지우려면
- classList.remove()
class를 지워준다.
oneDiv를 #container의 자식 요소로 추가한다.
append
사용
const container = document.querySelector('#container')
container.append(oneDiv)
지금까지 힘들게 만들어본 태그를 삭제해보자.
remove()
remove()
는 삭제하려는 요소의 위치를 알고 있을 때 사용한다.
변수명을 알고있을때??
const container = document.querySelector('#container')
const tweetDiv = document.createElement('div')
container.append(tweetDiv)
tweetDiv.remove() // 이렇게 append 했던 요소를 삭제할 수 있다.
class인 hide를 지워서 나타나게 할 수 있다.
JS Bin on jsbin.com
여러개를 지우려면 어떻게 해야할까?
removeChild()
removeChild 는 자식 요소를 지정해서 삭제하는 메서드입니다. 모든 자식 요소를 삭제하기 위해, 반복문(while, for, etc.)을 활용할 수 있습니다.
이 코드는 #container의 자식 요소가 남아있지 않을 때까지, 첫 번째 자식 요소를 삭제하는 코드이다.
const container = document.querySelector('#container');
while (container.firstChild) {
container.removeChild(container.firstChild);
}
innerHTML
자식요소를 모두 지워준다.
오류가 생길 수 있어 사용하지 말자.document.querySelector('#container').innerHTML = '';
removeChild 와 while 을 이용해 자식 요소를 삭제하면, 제목에 해당하는 H2 "Tweet List"까지 삭제됩니다.
이를 방지하기 위한 방법은 여러 가지가 있습니다.
1. 자식 요소가 담고 있는 문자열을 비교해 "Tweet List"만 남긴다.
2. 새로운 변수를 생성하고 Tweet List를 할당해뒀다가 반복문이 끝난 뒤에 새롭게 추가할 수도 있다.
3. 자식 요소를 하나만 남기게 할 수도 있다.
container의 자식 요소가 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()
}
사이트에서 원하는 조건에 맞게 반드시 형식을 맞춰 입력해야 하는 경우가 생깁니다.
이를 유효성검사
라고 한다.
DOM만을 이용해서 style을 바꿀 수 있지만 CSS 클래스를 이용해서 간접적으로 바꾸는 것을 권장한다.. 그 이유는 무엇일까?
이를 한마디로 표현하면 "관심사 분리 때문이다"라고 말할 수 있습니다.
// [유효성 검증 함수]: 영어 또는 숫자만 가능
function onlyNumberAndEnglish(str) {
return /^[A-Za-z][A-Za-z0-9]*$/.test(str);
}
// [유효성 검증 함수]: 최소 8자 이상하면서, 알파벳과 숫자 및 특수문자(@$!%*#?&) 는 하나 이상 포함
function strongPassword(str) {
return /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/.test(str);
}
출처
https://ko.javascript.info/searching-elements-dom
https://www.youtube.com/watch?v=zyz1eJJjsNE
코드스테이츠