본문 바로가기

① 기본

Shadow Dom 이란?

728x90

웹 컴포넌트란 ?

 

 - Custom Element

 - Shadow Dom

 - HTML Template

 - ES Module

 

이 네 가지 명세를 결합해서

 - 스타일링이 캡슐화되고  (Shadow Dom),

 - 스탬프를 찍어내듯이 여러번 사용 가능한 (HTML Template),

 - 고유한 태그를 (Custom Element)

 - 일관된 방식으로 통합하여 사용 (ES module)

 

가능하도록 만든 컴포넌트이다.

 

 


 

 

웹 컴포넌트에 있어서 Shadow DOM의 역할은?

 

DOM API는 자체적으로 캡슐화를 지원하지 않는다. 캡슐화를 지원하지 않는 다는 것은 스타일 정보가 다른 트리 요소로 누출될 수 있기 때문에 커스텀 요소를 개발하기 어렵고, id가 다른 요소간에 겹칠 수 있음을 의미한다.

캡슐화는 웹 컴포넌트의 중요한 개념이다. Shadow DOM은 마크업 구조, 스타일 및 동작을 숨겨서, 페이지의 다른 코드와 충돌하지 않도록 코드를 깨끗하게 유지해 준다. 

 

 

 

Shadow DOM 이란?

 

Shadow DOM을 사용하면 일반적인 DOM 트리에 서브 DOM 트리를 붙일 수 있다.

 

 

 - Shadow host: Shadow DOM이 붙어 있는 일반적인 DOM 노드

 - Shadow tree: Shadow DOM 안에 있는 DOM 트리

 - Shadow root: Shadow tree의 루트 노드

 - Shadow boundary: Shadow DOM이 끝나고 일반 DOM이 시작되는 곳

 

Shadow DOM 은 일반적인 DOM과 조작 방법(자식 요소 추가나 스타일을 넣는 등)에서 차이가 없지만, shadow DOM 내부의 어떤 코드도 외부에 영향을 줄 수 없는 캡슐화를 허용한다는 차이점이 있다. 

 

shadow DOM은 새로운 개념이 아니다. 브라우저는 오래 전부터 캡슐화를 하기 위해 custom element와 shadow DOM을 이용했다. video 태그가 바로 그것이다.

 

 


 

기본 사용법

 

Element.attachShadow() 메서드를 이용해, 엘레멘트에 shadow root를 붙일 수 있다.

 

let shadow = element.attachShadow({mode: 'open'});
let shadow = element.attachShadow({mode: 'closed'});

 

open: 바깥에서 작성된 javascript를 사용하여 shadow dom에 접근을 허용한다.

const $myWebComponent = document.querySelector("my-web-component"); 
$myWebComponent.shadowRoot.querySelector("p").innerText = "수정됨!";

 

 

 

closed: 바깥에서 작성된 javascript를 사용하여 shadow dom에 접근할 수 없다.

 

let $element = document.createElement("div"); 
$element.attachShadow({ mode: "closed" }); 
$element.shadowRoot // null

 


 

 

예제

1. HTMLElement를 상속받는 클래스를 만든다.

 

class PopUpInfo extends HTMLElement {
  constructor() {
    // Always call super first in constructor
    super();

    // write element functionality in here

    ...
  }
}

 

 

2. shadow root를 만들어 붙인다.

 

// Create a shadow root
let shadow = this.attachShadow({mode: 'open'});

 

 

3. shadow dom 구조를 만든다. 

 

// Create spans
let wrapper = document.createElement('span');
wrapper.setAttribute('class', 'wrapper');
let icon = document.createElement('span');
icon.setAttribute('class', 'icon');
icon.setAttribute('tabindex', 0);
let info = document.createElement('span');
info.setAttribute('class', 'info');

// Take attribute content and put it inside the info span
let text = this.getAttribute('data-text');
info.textContent = text;

// Insert icon
let imgUrl;
if(this.hasAttribute('img')) {
  imgUrl = this.getAttribute('img');
} else {
  imgUrl = 'img/default.png';
}
let img = document.createElement('img');
img.src = imgUrl;
icon.appendChild(img);

 

 

4. shadow DOM을 스타일링한다.

 

// Create some CSS to apply to the shadow dom
let style = document.createElement('style');

style.textContent = `
.wrapper {
  position: relative;
}

.info {
  font-size: 0.8rem;
  width: 200px;
  display: inline-block;
  border: 1px solid black;
  padding: 10px;
  background: white;
  border-radius: 10px;
  opacity: 0;
  transition: 0.6s all;
  position: absolute;
  bottom: 20px;
  left: 10px;
  z-index: 3;
}

img {
  width: 1.2rem;
}

.icon:hover + .info, .icon:focus + .info {
  opacity: 1;
}`;

 

 

5. shadow root에 shadow DOM을 붙인다.

 

// attach the created elements to the shadow dom
shadow.appendChild(style);
shadow.appendChild(wrapper);
wrapper.appendChild(icon);
wrapper.appendChild(info);

 

 

6. 커스텀 엘레멘트 사용하기

 

// Define the new element
customElements.define('popup-info', PopUpInfo);
<popup-info img="img/alt.png" data-text="Your card validation code (CVC)">

 

 

7. 외부의 스타일 참조하기

 

<link> 태그로 외부 스타일시트를 참조하면 가능하다.

 

// Apply external styles to the shadow dom
const linkElem = document.createElement('link');
linkElem.setAttribute('rel', 'stylesheet');
linkElem.setAttribute('href', 'style.css');

// Attach the created element to the shadow dom
shadow.appendChild(linkElem);

 

 


 

Shadow DOM을 붙일 수 있는 요소

 

- 보안상의 이유로 shadow DOM을 가질 수 없는 태그가 있다. (예: <a>)

 

 

다음은 shadow root를 연결할 수 있는 요소 목록이다.

- custom element

- article

- aside

- blockquote

- body

- div

- footer

- h1 ~ h6

- header

- main

- nav

- p

- section

- span

 

Q. button 태그는?

 

 

참고 사항

- Shadow DOM 은 FireFox, Chorme, Opera, Safari 등 모든 브라우저에서 기본으로 지원한다. 크로미움 기반 엣지도 지원하지만 구 버전 엣지에서는 지원하지 않는다.

- <link>요소는 shadow root의 paint를 차단하지 않으므로, 스타일시트가 로드되는 동안 스타일이 지정되지 않은 콘텐츠(FOUC)가 깜박일 수 있다.

 

 

참고

https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM

https://www.webcomponents.org/specs

728x90