개발 R.I.P.

6.03 Dev.Feedback(DOM)

편행 2021. 6. 3. 13:16
반응형

DOM

더보기
더보기

DOM 이란

 

Document Object Model의 약자로, HTML(Document)에 접근하여 Object(JavaScript Object)처럼 HTML을 조작(Manipulation)할 수 있는 Model이라는 의미 만약 자바스크립트를 사용하는 방법을 알고 있으면 DOM을 활용하여 HTML을 조작할 수 있다. 즉 DOM 은 HTML 의 아주 작은 부분까지 접근하여 조작할 수 있도록 웹페이지를 철저히 분석하여 준비된 구조로, 자바스트립트는 이 구조를 활용하여 HTML 로 구성된 웹 페이지를 작동할 수 있게 만들어 준다.

DOM의 구조를 조회할 때 쓰는 명령어 console.dir() ==> tag의 구조를 객체 형태로 Return 해준다.

 

DOM CRUD

document 객체를 이용하여 HTML의 요소를 Create(만들고), Read(조회하고), Update(업데이트하고), Delete(지우고)

createElement - CREATE
querySelector, querySelectorAll - READ
textContent, id, classList, setAttribute - UPDATE
remove, removeChild, innerHTML = "" , textContent = "" - DELETE
appendChild - APPEND
innerHTML과 textContent의 차이

Create

const tweetDiv = document.createElement('div')

createElement를 사용하여 element를 생성할 수 있지만, create.DocumentFragment를 활용하는 것이 더 효율적이다.

 

https://stackoverflow.com/questions/3397161/should-i-use-document-createdocumentfragment-or-document-createelement

 

아래에서 그 이유를 확인할 수 있는데,

 

When you create an element and append it to the DOM, the element is appended to the DOM, as well as the children.

With a document fragment, only the children are appended.

 

예를 들어 parent내부에 다수의 child를 묶어서 넣어줄 때 createElement는 불필요한 tag를 사용하여 넣어줘야 하지만,

document fargment를 사용하면,

1. 따로 tag를 사용하여 묶어주지 않아도 documentfragment 자체가 가상의 부모 역할을 하여 묶어주고,

2. element를 DOM에 append를 하게 되면 묶여 있던 자식 노드들만 추가가 된다는 것이다.

 

var frag = document.createDocumentFragment();
var textNode = frag.appendChild(document.createTextNode("Some text"));
var br = frag.appendChild(document.createElement("br"));
var body = document.body;
body.appendChild(frag);
alert(body.lastChild.tagName); // "BR"
alert(body.lastChild.previousSibling.data); // "Some text"
alert(frag.hasChildNodes()); // false

stackover flow에서 createDocumentFragment의 장점에 대해 예시로 들어준 코드이다.

 

Append

element를 만들고, append를 하여 추가해야 HTML 및 DOM tree에 직접 추가가 된다.

위의 코드 중 var body 부분을 보면 영역을 보면 먼저 html 요소중 body 요소를 body라는 변수에 할당해주고,

그 body에 frag라는 요소를 넣어준 것을 볼 수 있다.

var body = document.body;
body.appendChild(frag);

다른 예시를 들어보면 

위에서 생성한 

const tweetDiv = document.createElement('div')
document.body.append(tweetDiv)

바디라는 태그에 append를 추가하면,

아래 이미지처럼 body tag 내부에 새롭게 생성한 div가 추가된 것이 보인다.

 

append vs prepend

 

append는 Parent.Node의 마지막에 추가하고,

prepend는 Parent.Node의 맨 앞에 추가한다.

 

append vs appendChild

 

append는 한 번에 여러개의 요소를 추가할 수 있고,

appendChild는 한 번에 한 개의 요소만 추가한다.

append는 DOMString objects 도 추가할 수 있다. 다시 말해, append 를 쓰면 string 도 추가할 수 있다.

appendChild는 Node object 만 추가 가능. 

 

Read : querySelector, querySelectorAll

 

자바 스크립트에서는 변수값을 조회하기 위해 그냥 변수의 이름을 사용한다.
ex) 배열은 index, 객체는 key 등

 

HTML 은 tag를 요소들로 찾을 수도 있지만, CSS 의 selector 를 통해 좀 더 쉽게 찾아볼 수 있다. 

querySelector 를 사용하여, (querySelector는 한글로 셀렉터를 기반으로 한 질문을 한다, 쿼리를 날린다라는 의미) '.tweet' 을 인자로 넣으면 tweet을 클래스 이름으로 가진 자식 요소들 중 첫 번째 요소를 조회할 수 있다.

querySelectorAll을 사용하면, tweet을 클래스 이름으로 가진 모든 요소를 조회할 수 있다.

const oneTweet = document.querySelector('.tweet') ///자식 엘리먼트들 중 첫 번째 자식을 조회
const tweets = document.querySelectorAll('.tweet') ///다 조회
const getOneTweet = document.getElementById('container')
const queryOneTweet = document.querySelector('#container')
console.log(getOneTweet === queryOneTweet) // true

querySelector 가 등장하기 전에는 get 메소드가 있었다.

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

위의 코드를 통해 container에 div를 추가할 수 있다.

 

UPDATE - textContent, classList.add

textContent를 이용해 문자열을 입력하는 방법

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

위에 추가했던 div태그에 'tweet'이라는 클래스를 추가해주는 방법

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

container라는 아이디를 가진 요소를 선택하여 oneDiv의 변수에 할당된 class 명이 tweet인 div 태그를 자식으로 추가해주는 방법

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

***

Set attribute

 

Sets the value of an attribute on the specified element. If the attribute already exists, the value is updated; otherwise a new attribute is added with the specified name and value.

 

Syntax

Element.setAttribute(name, value);
속성을 부여하려는 엘리먼트.setAttribute(부여하려는 이름, 부여하려는 값);

만일 이미 name과 value가 존재하는 경우에
name을 기존 name과 동일하게 설정을 하고, value값을 변경을 하면 value 값만 변경이 된다.
name을 다르게 설정을 하면, 새롭게 name과 value값이 더해져 그 엘리먼트에 추가가 된다.

Parameter를 변경했을 때,

name : 변경하면 자동적으로 소문자로 변경이 된다.

value : 변경하면 자동적으로 문자열로 변경이 된다.

사용 예시

<button>Hello World</button>
let b = document.querySelector("button");

b.setAttribute("name", "helloButton");
// <button name = "helloButton">Hello World</button>

b.setAttribute("love", "consolebabe");
// <button name = "helloButton" love = "consolebabe" >Hello World</button>

b.setAttribute("Love", "beloved")
// <button name="helloButton" love="beloved">Hello World</button>

 

textContent와 innerHTML의 차이 (difference between textContent and innerHTML) 추가적으로 innerText까지

 

  • textContents is all text contained by an element and all its children that are for formatting purposes only.
  • innerText returns all text contained by an element and all its child elements.
  • innerHtml returns all text, including html tags, that is contained by an element.
<div id="whatHappens">
	What happened <b> when we use textContents</b>:
    <ul>
    	<li><a href = "x"> innerText <b> lol </b> </a></li>
        <li><a href = "y"> innerHtml </a></li>
    </ul>
</div>
browser.DomElement("//div[@id='whatHappens']").GetProperty("textContents")

//What happened when we use textContents

browser.DomElement("//div[@id='whatHappens']").GetProperty("innerText")

//What happened when we use textContents:innerText lol innerHtml

browser.DomElement("//div[@id='whatHappens']").GetProperty("innerHtml")

//
	What happened <b> when we use textContents</b>:
    <ul>
    	<li><a href = "x"> innerText <b> lol </b> </a></li>
        <li><a href = "y"> innerHtml </a></li>
    </ul>

위의 예시를 보면 textContents는 그 tag의 콘텐츠만, innerText는 그 tag의 자식 엘리먼트들의 콘텐츠까지, innerHtml은 HTML의 tag를 전부 포함하여 다 출력하는 것을 볼 수 있다.

DELETE - remove, removeChild

삭제하는 방법도 여러가지가 있다.

 

먼저 삭제하는 엘리먼트의 위치를 알고 있는 경우

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

한 번에 모든 자식 엘리먼트를 지우는 방법 .innerHTML

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

하지만, innerHTML은 보안상의 문제점을 갖고 있다.

더보기
더보기

innerHTML 보안상의 문제점

해커가 script를 주입해서 사용자의 모든 정보를 가져가게 된다.

그리하여 대체할 수 있는 방법이 있는데 바로 removeChild를 사용하면 가능하다.

removeChild는 자식 엘리먼트를 지정해서 삭제할 수 있는 메소드인데, 전체를 지우려면 반복문을 사용해야만 한다.

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

위 코드는 첫 번째 자식요소가 남지 않을 때까지 첫 번째 자식요소를 계속 지우는 반복문 ==> 전체를 지우게 된다.

하지만, 만일 모든 요소가 아닌 한 개의 특정한 요소 예를 들어 h2 태그인 자식 요소를 지우고 싶다

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()

클래스가 tweet인 요소들을 전부 제거하는 방법이 있다.

 

추가적인 개념 정리

 

node와 element의 차이

 

node

node는 element의 상위 개념이다.

node는 DOM tree를 구성하는 객체의 이름이다.

node를 통해 뭐든지 확인할 수 있다.

https://issabmsangare.medium.com/nodes-vs-elements-in-the-dom-1865885d0b9b

위 표의 value의 숫자는 그 노드의 타입을 알려준다

 

element

element는 node의 한 유형이다.

정확히 HTML의 tag를 가리키며, id 혹은 class처럼 속성을 가질 수 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형