문서 객체 모델 DOM!
HTML,CSS,JavaScript가 결합하는 약속의 땅!
웹 페이지를 구성하는 JavaScript 객체들의 집합.
HTML은 모두 연결되어있다. body안에 h1,ul, ul안에 li 등 이렇게 아래로 연결되어있는 구조를 "트리 구조"라고 한다. 그 트리 구조의 최상위에 있는 가장 중요한 요소이자 가장 중요한 객체는 문서(DOCUMENT)다.
문서도 하나의 객체다. 다른 점은 브라우저에서 자동으로 생성한다는 점!
새로운 페이지를 열 때마다 해당 페이지의 콘텐츠를 이용한 문서 객체 모델이 생성된다.
클릭이나 드래그에 반응하는 유용한 메서드와 특성도 포함되어있다.
컴퓨터 사이언스에서 나무의 뿌리 역할을 하는 것이다.
window가 브라우저에서 사용되는 특별한 객체인 것 처럼. Document도 마찬가지다.
(콘솔창에 console.dir(document)를 입력하면 JavaScript 객체들을 볼 수 있다.)
console.dir(document)를 보면 하나 하나가 다 객체라는걸 알 수 있다.
그렇기 때문에
// document에서 10번째 요소가 b태그의 글이 들어있다면
document.all[10].innerText = "SLICKIE";
HTML을 건드리지 않고 문서 객체 모델을 이용해서 HTML 요소를 바꿀수있다.
생소하겠지만 document 객체가 모든 게 담겨 있는 시작점이란 것! 그게 중요하다.
그 안의 메서드나 특성을 이용해 문서를 변경할 수 있다!!
'Element'는 반환되는 객체에 해당하는데
하나의 HTML 요소를 나타내는 모든 특성(프로퍼티)을 지닌 객체이다!
선택하기와 이용하기 중 선택하기에 해당하는 getElementById()!
특정 ID를 가진 Element를 선택하는 메서드.
실제 요소를 JavaScript가 Element를 객체로 생성한다.
일치하는 ID가 없으면 null이 뜬다.
document.getElementById('chicken');
이름처럼 get Element's' By TagName이다.
한 가지 요소가 아니라 여러개의 요소를 선택한다.
document.getElementsByTagName('img');
// HTMLCollection 반환
위와 같이 HTMLCollection을 반환한다. 배열처럼 생겼지만 배열은 아니다.
const allImg = document.getElementsByTagName('img'); // 변수저장
console.dir(allImg[1]); // 2번째 img 요소를 선택, 객체가 반환된다
for(let img of allImg){
console.log(img.src); // src는 객체의 프로퍼티(특성) 중 하나다
img.src = 'https://.../.../'; // 이미지 소스를 조작도 가능하다.
} // 이미지 출처가 콘솔창에 하나 하나 입력된다
allImg.map // undefined 배열이 아니기 때문에 안된다
인덱스나 for..of문은 사용 가능하다.
반복 가능한 집합이지만 배열은 아니기 때문에 map과 같은 것은 사용 불가능하다.
이름처럼 class 명으로 여러개의 Element를 선택하는 방법이다.
TagName과 같이 HTMLCollection을 반환한다. 배열처럼 생겼지만 배열은 아니다.
존재하지 않는 클래스를 선택하면 getElementById 와 같이 null이 아니라
빈 배열 같은 빈 집합이 나온다. 이것은 getElementsByTagName도 같다.
(ID는 페이지에 한 개만 존재하기 때문에 ID를 지닌 Element의 집합은 없기 때문.)
새로운 선택 방법은 querySelector라는 단 하나의 메서드를 사용해서
ID,클래스,요소타입,속성,css 스타일 뭐든 원하는 선택자를 이용해서 선택 가능하다!!
querySelector은 첫 번째로 일치하는 요소를 반환한다.
document.querySelector('p'); // 타입
document.querySelector('#chicken'); // ID
document.querySelector('.square'); // class
document.querySelector('img:nth-of-type(2)'); // pseudo-class
document.querySelector('a[title="Java"]'); // 타입과 속성
똑같은 원리인데, 일치하는 첫 번째 요소 대신에 일치하는 모든 요소를 반환한다.
document.querySelectorAll('p');
// NodeList(2) [p, p] 이런 식으로 반환된다
document.querySelector('a');
// NodeList(24) [a,...]
document.querySelector('p a'); // p 안에 있는 앵커태그
// NodeList(17) [a,...]
자! 선택 방법들은 끝이다!
이제 나올 방법들은 너무나 방대하다. 그렇기 때문에 모든 걸 마스터하려는 마인드를
조금 내려놓자. 찾을 수 있으면 된다. MDN 등 정보는 많다.
필요한 정보에 엑세스 할 수 있는 것에 집중하자.
innerText와 textContent는 요소 안의 내용,항목을 제어할 수 있다.
textContent는 요소 안에 있는 모든 콘텐츠가 나타난다. display:none 처리한 것도 모두 보인다. innerText는 현재 보이는 내용에 따라 달라진다.
const links = document.querySelectorAll('a');
for(let link of links){
link.innerText = 'I AM A LINK!!!!';
} // 모든 a 링크의 내용을 바꾼다!
위와 같이 다소 웃긴 제어를 해볼 수 있다.
innerHTML은 포함된 모든 태그까지 나온다.
그래서 어떤 컨텐츠에 태그를 사용해서 제어를 하려면 innerHTML이 필요하다.
document.querySelector('h1').innerText = '<i>hello</i>'
// 화면에 <i>hello</i> 그대로 출력된다
document.querySelector('h1').innerHTML = '<i>hello</i>'
// 원했던대로 이탤릭체가 추가된다
document.querySelector('h1').innerHTML += '<sup>asdf</sup>'
// +=으로 기존 내용에 추가할 수 도있다.
document.querySelector('#banner').id = 'whoops';
document.querySelector('#banner').src
document.querySelector('a').href
document.querySelector('a').title
const firstLink = document.querySelector('a');
firstLink.href // file:///.../...
// 둘은 동일한 기능을 하지만 차이가 있다
firstLink.getAttribute('href') // /.../...
firstLink.setAtrribute('href','http://www.google.com')
둘은 속성을 가져오는데, 차이점이 있다.
요소의 특성에 직접 액세스하는 .href는 JavaScript를 거쳐야한다. 계산된 값이다.
setAttribute로 조작할 수 있다.
document.querySelectorAll('input')[1]; // 인덱스 순서로
const input = document.querySelector('input[type="text"]')
// 속성으로
input.type = 'password'
input.type = 'color'
input.setAttribute('type', 'text')
동일한 작업을 여러 방법으로 할 수 있다. 하지만 다른 결과가 나오는 경우도 있는데
그건 다음에 알아보자!!
1. JavaScript 객체에 직접 액세스하기 -> 객체.특성
2. 마크업에 있는 속성 자체를 이용하기 -> setAttribute()
원래했던 것 처럼 먼저 요소를 선택하고
const h1 = document.querySelector('h1');
h1.style // 객체가 엄청 나온다
style 객체를 열어보면 특이한 점은 모두 카멜케이스 라는 점이다.
하이픈. 대시를 사용하지 않는다.
예로 css에서 font-size난 fontSize로 처리해야한다.
h1.style.color = 'green'
h1.style.fontSize = '3em'
h1.style.border = '2px solid pink'
숫자도 다 문자열로 처리해야한다.
인라인은 아주 구체적이고 특정하게 적용되기 때문에 스타일을 인라인으로 넣는 건 지양!
const h2 = document.querySelector('h2');
h2.setAttribute('class','purple'); // purple 클래스 적용
h2.setAttribute('class','border'); // purple 클래스에 border 덮음
// 그럼 이런 식으로 해야겠지?
let currentClasses = h2.getAttribute('class'); // border
h2.setAttribute('class',`${currentClasses} purple`);
...
...
이렇게 할 수는 있지만 귀찮지않은가? 그래서 쓸수있는것이
바로 'classList'다.
h2.classList.add('purple')
h2.classList.add('border') // 추가
h2.classList.remove('border') // 제거
h2.classList.contains('border') // false
h2.classList.contains('purple') // true
h2.classList.toggle('purple') // true / false 토글 왔다갔다!
이렇게 classList의 여러가지 메서드로 여라가지 기능을 쓸 수있다. !!!!
토글 기능처럼 아주 유용한 기능도 있다. class가 적용됐다가 제거하는 방법은,
토글로 켰다가 끄면서 어떤 요소를 여닫는 등의 기능을 추가할 때 정말 유용하다.
const h1 = document.querySelector('h1');
window.getComputedStyle(h1).color // rgb(129, 128, 0) 등
window.getComputedStyle(h1).fontSize // 32px 등
인라인으로 적혀있는 스타일이 아니면 자바스크립트에서 스타일이 보이지 않는데,
이 때 윈도우 메서드로 확인할 수 있다. 많이 쓰진않는다.
요소의 부모 요소를 반환한다.
const firstBold = document.querySelector('b')
firstBold // <b>---</b>
firstBold.parentElement // <p>---</p>
firstBold.parentElement.parentElement // <body>---</body>
const firstBold = document.querySelector('b')
const paragraph = firstBold.parentElement // <p>---</p>
paragraph.childElementCount // 8
paragraph.children // HTMLCollection(8)
paragraph.children[0] // b
childElementCount는 자식 요소의 개수를 알려준다.
children은 HTMLCollection으로 반환해준다.
해당하는 노드를 출력한다.
형제인 실제 요소가 출력한다. 보통은 이전 형제나 다음 형제 요소를 보려면 이걸 쓴다.
const newImg = document.createElement('img'); // 객체 생성
newImg.src = ``; // src가 비어있다 추가하면된다
처음 img객체를 생성하면 출처가 없다. src를 추가하자.
그리고 이제 페이지에 추가를 해줘야한다!!
두 가지 방법이 있다.
document.body.appendChild(newImg); // body의 마지막 자식으로 추가
const newH3 = document.createElement('h3');
newH3.innerText = 'I am new!';
document.body.appendChild(newH3);
조금 더 유연한 최신 메서드다.
텍스트나 문단도 전달 가능하다. 한 번에 한 개 이상을 삽입 가능하다.
const p = document.querySelector('p');
p.append('i am new text yaaaayyyy!!!'); // 가능하다!
p.append('i am new text yaaaayyyy!!!','abcdefghijk'); // 가능하다!
맨 뒤에 추가되었다. prepend로 앞쪽에 추가할 수도 있다.
const newB = document.createElement('b')
newB.append('Hi!')
p.prepend(newB)
const h2 = document.createElement('h2') // 생성
h2.append('Are adrable chickends') // 텍스트 추가
// 특정 요소 뒤에 삽입하기 위해서 요소 선택
const h1 = document.querySelector('h1')
h1.insertAdjacentElement('afterend', h2) // 이런 식으로 작동
첫번째 인수에 들어오는 것
'beforebegin' , 'afterbegin', 'beforeend', 'afterend' 가 있다.
const h3 = document.createElement('h3');
h3.innerText = 'I am h3';
h1.after(h3)
선택한 요소를 제거하는 것이 아니라 선택한 요소의 자식을 제거하는 방식이다.
즉, 제거하려는 요소의 부모에 메서드를 호출해야 한다.
const firstLi = document.querySelector('li')
firstLi.removeChild // x 이게 아니라
const ul = firstLi.parentElement // ul
ul.removeChild(firstLi)
const b = document.querySelector('b')
b.parentElement.removeChild(b) // 이런식으로 사용할 수도 있다
선택한 요소를 삭제한다!!
const img = document.querySelector('img')
img.remove()
끝!