08.11 TIL - DOM

이형우·2020년 8월 11일

TIL 이라고 부르던데?

목록 보기
12/12

DOM
학습 목표

  • DOM의 의미
  • HTML 조작하기 (DOM을 활용한 HTML Element CRUD)
  • HTML과 Javascript의 해석(parse) 시점
  • event 객체 이해
  • 유효성 검사
  • 트위틀러 함수 작성

DOM 이란..?
Document Object Model의 약자로, HTML(Document)에 접근하여 Object(JavaScript Object)처럼 HTML을 조작(Manipulation)할 수 있는 Model이라는 의미 만약 자바스크립트를 사용하는 방법을 알고 있으면 DOM을 활용하여 HTML을 조작할 수 있다. 즉 DOM 은 HTML 의 아주 작은 부분까지 접근하여 조작할 수 있도록 웹페이지를 철저히 분석하여 준비된 구조로, 자바스트립트는 이 구조를 활용하여 HTML 로 구성된 웹 페이지를 작동할 수 있게 만들어 준다.
MDN에서 Introduction to the DOM 검색해 보기!!
DOM은 HTML 문서의 프로그래밍 interface 이다. 구조화된 nodes, property, method 를 갖고 있는 objects 문서를 표현한다. 이들은 웹 페이지를 스크립트 또는 프로그래밍 언어들에서 사용될 수 있게 연결시켜주는 역할을 담당한다.
페이지 컨텐츠는 DOM에 저장되고, 자바스크립트를 통해 접근하거나 조작할 수 있다.
API(web or XML page) = DOM + JS(scripting language)

CRUD
Document 객체를 통해서 HTML 엘리먼트를
C : Create 만들고
R : read 조회하고
U : Update 갱신하고
D : Delete 삭제하고

HTML 조작하기
학습 목표
DOM을 JavaScript로 조작하여 HTML Element를 추가하거나 삭제, 혹은 내용을 변경할 수 있다.

  • createElement - CREATE
  • querySelector, querySelectorAll - READ
  • textContent, id, classList, setAttribute - UPDATE
  • remove, removeChild, innerHTML = "" , textContent = "" - DELETE
  • appendChild - APPEND
  • innerHTML과 textContent의 차이
    **찾아서 읽어볼 것들
  • MDN : Document.createDocumentFragment() 찾아서 읽어보기 -> DOM 제어하는 방법
    찾아본 내용
  • 새로운 빈 DocumentFragment 를 생성하라는 뜻. DocumentFragments 는 DOM 노드들 임. 이것들은 메인 DOM 트리의 일부가 되지 않는다. 일반적인 Use case는 다큐먼트 조각을 생성하고, 엘리먼트들을 다큐먼트 조각에 추가하고 그 다큐먼트 조각을 DOM 트리에 추가하는 것임. DOM 트리 내에서 다큐먼트 조각은 그것의 모든 자식들로 대체된다.
  • MDN : <template>: The Content Template element
    찾아본 내용 : HTML <template> 요소는 페이지를 불러온 순간 즉시 그려지지는 않지만, 이후 JavaScript를 사용해 인스턴스를 생성할 수 있는 HTML 코드를 담을 방법을 제공합니다.
    템플릿은 콘텐츠 조각을 나중에 사용하기 위해 담아놓는 컨테이너로 생각하세요.

Create : createElement

  • HTML을 제어하는 가장 기초 방법인 element 만들기
  • element 를 만들되, 자바스크립트에서 이용할 수 있도록 만들어야 함. 그렇다면
const tweetDiv = document.createElement('div')

하지만 이 상태는 공중에 떠 있는 것처럼 아무것도 연결되어 있는 것이 없다. 이 엘리먼트를 append 해 주어야 웹 상에서도 구동 가능하다.

Append : append, appendChild
아래 명령어를 통해 tweeDiv를 body 안에 넣기.

document.body.append(tweetDiv)

콘솔창에 가보면 div 가 새로 생긴것을 확인할 수 있다.

혼자 공부하기

  • MDN : append와 prepend에 대해서 검색하고, 차이에 대해서 공부하기. (difference between append and prepend in javascript dom)
    -> 혼자 공부한 내용
    prepend : ParentNode.prepend() 메소드는 Node 객체 또는DOMString 객체를 ParentNode의 첫 자식노드 앞에 삽입한다. DOMString 객체는 Text 노드와 동일하게 삽입된다.
    prepend : 자체가 '앞에 추가' 란 뜻임.
    ex)
let parent = document.createElement("div");
let p = document.createElement("p");
let span = document.createElement("span");
parent.append(p);
parent.prepend(span);

console.log(parent.childNodes); // NodeList [ <span>, <p> ]

append : ParentNode.append() 메서드는 ParentNode의 마지막 자식 뒤에 Node 객체 또는 DOMString 객체를 삽입한다. DOMString 객체는 Text 노드처럼 삽입한다.

Node.appendChild()와 다른 점:

-> ParentNode.append()는 DOMString 객체도 추가할 수 있다. 한편 Node.appendChild()는 오직 Node 객체만 허용한다.
-> ParentNode.append()는 반환하는 값이 없다. 한편 Node.appendChild()는 추가한 Node 객체를 반환한다.
-> ParentNode.append()는 여러 개 노드와 문자를 추가할 수 있다. 한편 Node.appendChild()는 오직 노드 하나만 추가할 수 있다.

  • MDN : appendChild와 append에 대해서 검색하고, 차이에 대해서 공부하기. (difference between appendChild and append in javascript dom)
    -> The main difference is that appendChild is a DOM function meanwhile append is a JavaScript function. 위 차이가 무엇을 의미하느냐? appendChild function 은 “element” 가 필요하다 like a parameter. 때문에
document.getElementById("yourId").appendChild("<p></p>"); // 이렇게는 안됨

var p = document.createElement("p");

document.getElementById("yourId").appendChild(p); //이렇게 해야 함

결론은 appendChild 를 쓸 수 있는 모든 상황에서는 append도 사용할 수 있지만, 그 반대는 안됨.

  • 싸이트 indepth.dev : Here is why appendChild moves a DOM node between parents
    -> appendChild 는 부모의 제일 끝자락 자식으로 만들어 줌. 만약에 appendChild 를 다른데서 받으면 그냥 이동 됨(not cloned)
    -> 클론이 안 되는 이유 : 굉장히 깊이 있는 자식인 경우 목적이 불 분명해 질 수 있으며, 효율면에서도 비효율적이 될 수 있음. 클론되면서 ID까지 복제해 버릴 수 있음.
    -> 그렇다면 왜 node 는 두 부모의 한 자식이 될수 없냐? 이것은 Tree구조이고, 그렇기 때문에 부모는 one or zero 둘중 하나. 때문에 node 하나를 옮기고 싶다면, 원래 있던거는 지우고 옮겨야 한다. 왜냐하면 부모는 only one!

Read : querySelector, querySelectorAll

  • 자바 스크립트에서는 변수값을 조회하기 위해 그냥 변수의 이름을 사용.
    ex) 배열은 index, 객체는 key 등
  • HTML 은 tag, id, class 라는 것들을 가지고 있었고, CSS 의 selector 를 통해 좀 더 쉽게 찾아볼 수 있다. querySelector 를 사용하도록 하며, (querySelector는 한글로 셀렉터를 기반으로 한 질문을 한다, 쿼리를 날린다라는 의미) '.tweet' 을 첫 번째 인자로 넣으면 tweet을 클레스 이름으로 가진 모든 HTML 엘리먼트를 조회할 수 있다.
const oneTweet = document.querySelector('.tweet') ///하나만 조회
const tweets = document.querySelectorAll('.tweet') ///다 조회

All을 덧붙여야 tweet이란 클래스를 가진 모든 요소를 조회할 수 있으며, 이렇게 조회해온 HTML엘리먼트들은 배열과 유사하게 for 문을 사용할 수 있다. 이런 '배열 아닌 배열'은 유사배열, 배열형 객체 등 다양한 이름으로 불리며, MDN에서 Working with array-like objects 검색해 보기.
검색결과 배열 처름 행동하기는 하지만 모든 method 가 먹히는 건 아니라고 함.

아래와 같이 get 으로 시작하는 것들은 옛날 익스플로러 방식

const getOneTweet = document.getElementById('container')
const queryOneTweet = document.querySelector('#container')
console.log(getOneTweet === queryOneTweet) // true

아래와 같은 명령어를 통해 위에서 생성한 tweet을 추가할 수 있다.

const container = document.querySelector('#container')
const tweetDiv = document.createElement('div')
container.append(tweetDiv)

MDN에서 querySelector 검색해서 아래 공부해 보기

  • querySelector의 첫번째 인자에 'div'를 넣으면 어떻게 될까?
    -> 제일 처음으로 검색되는 div를 뱉거나, div 가 없으면 null 을 리턴
  • querySelector를 통해서 더 복잡한 작업을 할 수 있을까?
    -> querySelector 는 파워풀해서 복잡한 것도 찾는다.
  • querySelector의 부모는 꼭 document 객체여야만 할까?
    -> Document method라고 하고, Document 안에서 찾는다고 하니까 그럴껄...?

UPDATE - textContent, classList.add
div 를 하나 만들었고,

const oneDiv = document.createElement('div')
console.log(oneDiv) // <div></div>

text를 넣어보고

oneDiv.textContent = 'dev';
console.log(oneDiv) // <div>dev</div>

class 를 지정

oneDiv.classList.add('tweet')
console.log(oneDiv) // <div class="tweet">dev</div>

append로 container에 붙이기

const container = document.querySelector('#container')
container.append(oneDiv)

-> console 창에 차례대로 실습해 보기

혼자 공부하기

  • MDN에서 setAttribute 라는 메소드를 검색해보기
    -> 새로운 속성 세팅하기
    -> 만약 the attribute가 존재하면, 새로 받는 걸로 업데이트
    -> 없는 속성이었다면 추가함
var b = document.querySelector("button"); 

b.setAttribute("name", "helloButton"); // 기존 '버튼' 이던걸 '헬로버튼'으로 이름을 바꿈
b.setAttribute("disabled", ""); // 조금이라도 속성이 존재한다면 boolean 에서 true 로 간주. 근데 이번 예시는 무슨 뜻인지 모르게뜸...
  • textContentinnerHTML의 차이 (difference between textContent and innerHTML)
    -> textContent
    --> Node 인터페이스의 textContent 속성은 노드와 그 자손의 텍스트 콘텐츠를 표현합니다.
    --> 노드가 document 또는 Doctype이면 null을 반환합니다.
    --> 다른 노드 유형에 대해서는 주석과 처리 명령을 제외한 모든 자식 노드의 textContent를 병합한 결과를 반환합니다. 자식이 없는 경우 빈 문자열입니다.
    --> 노드의 textContent를 설정하면, 노드의 모든 자식을 주어진 문자열로 이루어진 하나의 텍스트 노드로 대치합니다.
    -> innerHTML
    --> Element.innerHTML는 이름 그대로 HTML을 반환합니다. 간혹 innerHTML을 사용해 요소의 텍스트를 가져오거나 쓰는 경우가 있지만, HTML로 분석할 필요가 없다는 점에서 textContent의 성능이 더 좋습니다. 이에 더해, textContent는 XSS 공격의 위험이 없습니다.
  • innerHTML의 보안상 단점 (mdn innerHTML security issue)
    -> 뭔 소린지는 정확하게 모르겠지만, 대략 innerHTML을 사용하게 되면,, innerHTML 값을 설정할 때 지정한 값이 HTML or XML 로 파싱될 때 요소의 내용이 DocumentFragment 로 대체되는데 이때 제어할 수 없는 문자열을 설정할 수 있거나 보안 점검을 거치는 프로젝트인 경우 코드가 거부될 가능성이 있다. 암튼 그래서 innerHTML 말고 Node.textContent 를 쓰는것이 좋다.

DELETE - remove, removeChild
삭제하는 방법도 여러가지가 있다.
우선 해당 엘리먼트가 어디 있는지 알고 있는 경우

const container = document.querySelector('#container')
const tweetDiv = document.createElement('div')
container.append(tweetDiv)
tweetDiv.remove() // 이렇게 append 했던 엘리먼트를 삭제할 수 있다.

자식 엘리먼트 지우기 : innerHTML을 활용하면 간단하지만, 보안상의 문제가 있다. 상기 혼자 공부하기 내용 참조

document.querySelector('#container').innerHTML = '';

그래서 메소드를 활용. remoeChild는 자식 엘리먼트를 지정해서 삭제하는 방식이고, 모두 삭제하고 싶다면 while 문을 활용할 수 있다. 자식 엘리먼트가 하나도 없을때 까지 삭제한다는 의미.

const container = document.querySelector('#container');
while (container.firstChild) {
  container.removeChild(container.firstChild);
}

이렇게 지우면 H2 Tweet list까지 싹 지워지기 때문에, 마지막 자식을 하나 남기거나, 직접 일일히 찾아서 지울수 도 있다.

const container = document.querySelector('#container');
while (container.children.length > 1) {
  container.removeChild(container.lastChild);
} // 한놈만 남기기
const tweets = document.querySelectorAll('.tweet')
tweets.forEach(function(tweet){
    tweet.remove();
})
// or
for (let tweet of tweets){
    tweet.remove()
} // 일일히 다 지우기

또 혼자 공부하기. 그냥 다 혼자 공부하기

  • element와 node의 차이 (difference between element and node in javascript dom)
    -> Node 는 DOM 계층에 있는 오브젝트 타입의 어떤 일반적인 이름이다. 노드는 DOM 요소의 뭐라도 될 수 있다. such as document, document.body, textnode, HTML tag가 될 수도 있고, 또 다른 element 안의 text block 도 될수 있고. 뭐 졸라 많네.. 무튼 간단히 말하면 노드는 뭐 어떤 DOM object 임.
    반면, element 는 노드 안에서 어떤 특정 타입밖에 될 수 없음. 이런 DOM 은 부모가 되기도 하고, 자식이 되기도 하고, 앞에도 있고 뒤에도 있는 이런 노드 계층으로 구성되어 있음. 그래서 나무 계층 구조라고 하는 거임. element 는 어떤 특정 타입의 노드를 가리키는 거임. 그래서 노드가 엘리먼트보다 더 큰 범주.

  • children과 childNodes의 차이 (difference between children and childNodes in javascript dom)
    -> childNodes : childNodes 는 노드의 속성을 지니고 있는데, 이것은 nodelist 를 리턴하고, 이 node list 는 string 이 아닌 onject 임. 따라서 인덱스 넘버로 엑세스 할 수 있음.
    -> children은 엘리먼드의 속성을 지니고 있음.
    -> 이 둘의 main defference 는 children 은 오직 엘리먼트의 속성만 지니고 있는 반면, childNode는 텍스트나 comment node 처럼 non-element 적인 노드도 포함하고 있음

  • removeChild와 remove의 차이 (difference between removeChild and remove in javascript dom)
    -> 일단 기본적으로 remove 라고 하면 진짜 제거하는 줄 알고 있는데 그게 아니다.
    -> 근데 사실은 remove의 컨셉은 진짜 없애버리는게 아니라, 부모 자식간의 관계를 끊어버리는 거다.
    -> 이 둘을 썻을 때 결과는 동일하지만, remove 는 자식만 reference 하고, removeChild는 부모 자식 모두를 reference 한다.

  • tweets에 forEach는 되는데, reduce는 안되는 이유 (why array method is not working on nodelist)
    -> forEach & reduce 모두 array 에 사용하는 method 인데, 이 둘의 성격을 정확히 모르겠으므로 검색해 봐도 모르게따...

  • tweets를 유사 배열에서 배열로 바꾸는 방법 (how to convert nodelist into javascript array)
    -> 일단 바꾸는 방법을 확인하기에 앞서,
    -> 노드 리스트는 DOM element 에 접근하고 다룰 수 있는 어떤 노드 collection 임. 반면 자바스크립트에서의 array는 한번에 한개 이상의 value를 가질 수 있음. 노드 리스트 & 어레이 둘다 스스로의 prototype, method, property 를 가지고 있음.
    -> 노드 리스를 어레이 인 자바스크립트로 바꾸는 가장 쉬운 방법은 Array.from()을 쓰는 거임. 그러나 옛날 브라우저에서는 안 먹힘
    -> spread syntax 도 됨. const divsArr = [...divs]; 처럼. 근데 이것도 옛날 브라우저는 안 먹힘
    -> 그래서 Array.prototype.slice()를 쓰면 됨. 이건 옛날 브라우저도 먹힘.

profile
완전 완전 초짜

0개의 댓글