Manipulating documents

김동현·2023년 3월 12일
0

자바스크립트

목록 보기
17/22

The important parts of a web browser

웹 브라우저는 움직이는 부분이 매우 많은 복잡한 소프트웨어이며, 그 중 많은 부분은 웹 개발자가 자바스크립트로 조작할 수 없다.

이러한 제한이 나쁘다고 생각할 수 있지만, 대부분 보안문제를 방지하고자 잠겨있다.

웹 사이트에 저장된 암호나 기타 중요한 정보에 액세스하여 타인이 마치 본인처럼 웹 사이트에 로그인할 수 있다고 생각해보면 이해가 갈 것이다.

이러한 제한들이 있지만, 웹 API는 여전히 우리에게 웹 페이지로 많은 것을 할 수 있게 해주는 기능들을 제공한다.

  • window는 웹 페이지가 로드되는 브라우저 탭이며, 자바스크립트에서 window 객체로 표시된다.
    이 객체에서 사용할 수 있는 메서드를 통해 window의 크기를 조작하고 (window.innerWidth , window.innerHeight ) 페이지에 해당하는 데이터를 클라이언트 측에 저장하고, 이벤트 핸들러를 현재 창에 등록하는 ( addEventListener()EventTarget 객체의 메서드인데, window 객체는 EventTarget 객체를 상속한다) 등의 작업을 수행할 수 있다.

  • navigator는 브라우저( 즉, user agent )의 상태와 ID를 나타낸다.
    JavaScript에서는 Navigatior 객체로 표현된다.
    이 객체를 이용하여 사용자의 국가별 언어, 웹 캠의 미디어 스트림 등을 검색할 수 있다.

  • document는 window에 로드된 실제 페이지이며, JavaScript에서 Document 로 표현된다.
    이 객체를 사용하여 문서를 구성하는 HTML, CSS에 대한 정보를 반환하고 조작할 수 있다.
    예를 들어 DOM의 엘리먼트에 대한 참조를 가져오거나, 텍스트 내용을 변경하거나, 새 스타일을 적용하거나, 새 엘리먼트를 만들고 하위로 현재 엘리먼트에 추가하거나 문서를 모두 삭제할 수 있다.


The document object model

브라우저의 각 탭에 현재 로드된 문서는 DOM으로 표시된다.

DOM은 자바스크립트로 HTML구조에 보다 쉽게 접근할 수 있게 브라우저에 의해 만들어진 "트리 구조" 이다.

아래의 예제는 HTML문서와 그에 매칭되는 DOM구조이다.

<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <title>Simple DOM example</title>
  </head>
  <body>
    <section>
      <img
        src="dinosaur.png"
        alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth." />
      <p>
        Here we will add a link to the
        <a href="https://www.mozilla.org/">Mozilla homepage</a>
      </p>
    </section>
  </body>
</html>

DOM 구조

DOM 트리 다이어그램을 보고 싶다면 Ian Hickson의 Live DOM viewer➡️에서 확인하자.

트리의 각 항목을 노드라고 부른다.

위의 다이어그램에서 일부 노드는 엘리먼트( HTML , HEAD , META )를 나타내고 또 다른 일부 노드는 텍스트( #text )를 나타내는 것을 볼 수 있다.

더 많은 타입➡️이 있지만 가장 중요한 것은 위의 것들이다.

노드는 트리에서 다른 노드와의 위치로도 참조된다.

  • Root node : 트리의 맨 위 노드이다.
    HTML의 경우 항상 HTML노드이다.
    SVG나 XML과 같은 다른 마크업 언어일 경우엔 다른 Root node를 가진다.

  • Child node : 다른 노드의 바로 밑에 포함된 노드이다.

  • Descendant node : 다른 노드에 포함된 노드이다.
    몇 단계 중첩으로 포함되어 있는지는 중요치 않다.

  • Parent node : 다른 노드를 포함하고 있는 노드이다.

  • Sibling nodes : DOM tree에서 같은 레벨에 있는 노드들이다.

DOM을 사용하기 전에 위의 용어들을 숙지하는 것이 유용하다.

Basic DOM manipulation

<section>
	<img src="dinosaur.png" alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth.">
    <p>Here we will add a link to the <a href="https://www.mozilla.org/">Mozilla homepage</a></p>
</section>

DOM 내부에서 엘리먼트를 조작하려면 먼저 엘리먼트를 선택하고 변수 내부에 참조를 저장해야 한다.

const link = document.querySelector('a');

변수에 엘리먼트의 참조를 저장했으니, 프로퍼티와 메소드를 이용해 조작할 수 있다.

<a> 엘리먼트의 경우, 프로퍼티와 메소드는 HTMLAnchorElement에 정의되어 있다.

HTMLAnchorElement는 HTMLElement, Node 객체와 같이 부모의 인터페이스도 이용할 수 있다.

내부의 텍스트를 변경하기 위해서 Node.textContent 속성을 사용한다.

link.textContent = 'Mozilla Developer Network';

엘리먼트의 어트리뷰트의 변경도 다음과 같이 할 수 있다.

link.href = 'https://developer.mozilla.org';

자바스크립트에는 엘리먼트를 선택하고 변수에 참조를 저장하는 방법이 여러개가 있는 데, Document.querySelector() 는 권장되는 최신 접근 방법이다.

CSS selector를 이용해 엘리먼트를 선택할 수 있어서 편리하다.

매칭 되는 엘리먼트가 여러개일 경우엔, 첫 번째 엘리먼트가 선택된다.

만약 여러개의 엘리먼트를 선택하고 싶다면 Document.querySelectorAll() 를 사용하자.

일치하는 모든 엘리먼트들을 담은 NodeList 라는 유사 배열 객체를 반환한다.

NodeList 는 특이하게 Live NodeLists와 Static NodeLists로 분류된다.
Node.childNodes 에서 반환된 NodeList는 Live 이고 Document.querySelectorAll() 에서 반환된 NodeList는 Static이다.

엘리먼트를 참조하는 레거시 방법으로는 Document.getElementById() , Document.getElementsByTagName() 등이 있다.

Creating and placing new nodes

다시 위의 예제 HTML 코드로 돌아가서, <section> 엘리먼트 안에 새로운 엘리먼트를 추가해보자.

그러기 위해 먼저 <section> 엘리먼트의 참조를 변수에 저장한다.

const sect = document.querySelector('section');

새 엘리먼트를 생성하기 위해서 Document.createElement() 를 사용한다.

const para = document.createElement('p');
para.textContent = 'We hope you enjoyed the ride.';

새로 만든 엘리먼트를 <section> 에 포함시키 위해 Node.appendChild() 를 사용한다.

sect.appendChild(para);

텍스트 노드를 만들고 추가하기 위해서는 다음과 같이 한다.

const text = document.createTextNode(' — the premier source for web development knowledge.');

const linkPara = document.querySelector('p');
linkPara.appendChild(text);

또는 Element.append() 를 사용하면 매개변수로 전달한 텍스트를 자동으로 텍스트 노드로 만들어서 추가해준다.

const linkPara = document.querySelector('p');
linkPara.append(' — the premier source for web development knowledge.');

Moving and removing elements

Node.appendChild() 로 자식 엘리먼트의 순서도 바꿀 수 있다.

<ul>
  <li id="first">first</li>
  <li id="second">second</li>
  <li id="third">third</li>
</ul>
const ul = document.querySelector("ul");
const first = document.querySelector("#first");

ul.appendChild(first);

위의 코드처럼 기존에 있던 자식 엘리먼트를 대상으로 Node.appendChild() 를 하면 다음과 같이 순서가 변경된다.

<ul>
  <li id="second">second</li>
  <li id="third">third</li>
  <li id="first">first</li>
</ul>

이는 복사본을 통해 이루어 지지 않는다.

first 변수는 <li id="first">first</li> 를 유일하게 참조한다.

만약 Node를 복사하고 싶다면 Node.cloneNode() 를 사용해야 한다.

노드를 제거하고 싶다면 Node.removeChild() 또는 Element.remove() 를 사용하자.

부모와 자식 모두 참조하고 있으면 Node.removeChild() 를 사용할 수 있고 자기 자신만을 참조하고 있으면 Element.remove() 를 사용할 수 있다.

단, Element.remove() 는 최신버전의 브라우저에서만 동작하므로 주의해야 한다.

Manipulating styles

자바스크립트를 통해 CSS 스타일을 다양한 방식으로 조작할 수 있다.

Document.stylesheets 를 사용하여 문서에 첨부된 모든 스타일시트의 목록을 얻을 수 있다.

이 목록은 CSSStyleSheet 객체가 담겨있는 유사 배열 객체를 반환한다.

그 후 원하는 대로 스타일을 추가 / 제거할 수 있다.

그러나 이러한 기능은 다소 오래되고 어려운 방법이기 때문에 더 배울 필요가 없다.

훨씬 쉬운 방법들이 있기 때문이다.

첫 번째 방법은 스타일을 지정할 엘리먼트에 동적으로 인라인 스타일을 직접 추가하는 것이다.

이 작업은 문서의 각 엘리먼트에 대한 인라인 스타일 정보를 포함하고 있는 HTMLElement.style 에서 수행된다.

이 객체의 속성을 직접 설정해서 엘리먼트 스타일을 업데이트할 수 있다.

para.style.color = 'white';
para.style.backgroundColor = 'black';
para.style.padding = '10px';
para.style.width = '250px';
para.style.textAlign = 'center';

위와 같이 설정하고 새로고침을 한 뒤에 개발자 도구에서 HTML을 보자.

<p
  style="color: white; background-color: black; padding: 10px; width: 250px; text-align: center;">
  We hope you enjoyed the ride.
</p>

엘리먼트의 스타일 어트리뷰트에 인라인 스타일이 지정되어있는 모습을 확인 할 수 있다.

문서의 스타일을 동적으로 조작할 수 있는 또 다른 일반적인 방법이 있다.

특정한 클래스 selector에 미리 스타일을 지정해 놓고 자바스크립트로 해당 클래스를 설정해주는 방식이다.

.highlight {
  color: white;
  background-color: black;
  padding: 10px;
  width: 250px;
  text-align: center;
}

위와 같이 CSS에 highlight라는 클래스 선택자의 스타일을 설정했다.

para.setAttribute('class', 'highlight');

엘리먼트에 클래스를 할당할 수 있는 여러 방법이 있지만 여기서는 두루두루 사용되는 Element.setAttribute() 를 사용했다.

새로고침을 하면 스타일이 적용되는 걸 확인 할 수 있다.

어떤 방법을 선택하느냐는 개발자 본인에게 달려있다.

둘 다 장단점이 있다.

첫 번째 방법은 간단한 사용에 적합한 반면, 두 번째 방법은 더 "순수"하다.

MDN 문서에서 따로 어떠한 방법을 권장한다라고 나와있지는 않지만 개인적으로 두 번째 방법을 사용하는 것이 옳다고 생각한다.

CSS의 우선순위 때문이다.

CSS는 selector의 specificity와 CSS가 포함된 위치 즉, 인라인, 내부, 외부 등 위치에 따라 적용 우선순위가 달라진다.

최근에는 layer라는 개념도 추가되어서 더욱 우선순위가 헷갈릴 수 있다.

그래서 BEM과 같이 specificity를 동일하게 해주는 명명법 기술도 등장하는데, 이처럼 인라인 스타일을 입히는 방식을 사용하면 마치 !important 를 여기저기 남발하는 것과 같은 느낌이다.

[참고] : MDN

profile
프론트에_가까운_풀스택_개발자

0개의 댓글