웹 컴포넌트란 ?
- 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 host: Shadow DOM이 붙어 있는 일반적인 DOM 노드
- Shadow tree: Shadow DOM 안에 있는 DOM 트리
- Shadow root: Shadow tree의 루트 노드
- Shadow boundary: Shadow DOM이 끝나고 일반 DOM이 시작되는 곳
Shadow DOM 은 일반적인 DOM과 조작 방법(자식 요소 추가나 스타일을 넣는 등)에서 차이가 없지만, shadow DOM 내부의 어떤 코드도 외부에 영향을 줄 수 없는 캡슐화를 허용한다는 차이점이 있다.
기본 사용법
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
'① 기본' 카테고리의 다른 글
Shadow DOM 스타일링하는 방법 (0) | 2022.03.28 |
---|---|
Web Component Introduction 번역 - 웹 컴포넌트 소개 (3) | 2022.03.22 |