Shadow DOM 시작하기

Dominic Cooney
Dominic Cooney

소개

웹 구성요소는 일련의 최첨단 표준으로, 다음과 같은 기능을 제공합니다.

  1. 위젯 구축 지원
  2. ...안정적인 재사용이 가능함
  3. ...다음 버전의 구성요소가 구현되어도 페이지가 손상되지 않습니다. 내부 구현 세부정보를 변경합니다.

이것은 HTML/JavaScript를 언제 사용할 것인지와 언제 웹 구성요소를 사용해야 할까요? 아니요 HTML 및 JavaScript를 사용하면 상호작용 시각적 자료를 만들 수 있습니다. 위젯은 대화형 시각적 요소입니다. 그것은 HTML 및 자바스크립트 기술을 활용하는 것이 위젯 개발에 대해 자세히 알아보겠습니다. 웹 구성요소 표준은 그렇게 하면 됩니다.

하지만 한 가지 근본적인 문제가 있는데, HTML 및 JavaScript의 사용이 어려움: 위젯 내부의 DOM 트리는 페이지의 나머지 부분에서 캡슐화됩니다. 이러한 캡슐화 부족은 문서 스타일시트가 실수로 특정 부분에 찾을 수 있습니다. JavaScript에서 실수로 특정 부분을 찾을 수 있습니다. ID가 위젯 내부의 ID와 겹칠 수 있습니다. 등등.

웹 구성요소는 다음 세 부분으로 구성됩니다.

  1. 템플릿
  2. Shadow DOM
  3. 맞춤 요소

Shadow DOM은 DOM 트리 캡슐화 문제를 해결합니다. 이 웹 구성요소의 네 부분은 함께 작동하도록 설계되어 있지만 은 웹 구성요소의 어떤 부분을 사용할지 직접 선택할 수도 있습니다. 이 튜토리얼을 통해 Shadow DOM을 사용하는 방법을 확인할 수 있습니다.

섀도우 월드를 만나보세요

Shadow DOM을 사용하면 요소가 있습니다. 이 새로운 종류의 노드를 섀도우 루트라고 합니다. 섀도우 루트가 연결된 요소를 섀도우(shadow) 루트라고 합니다. 있습니다 섀도 호스트의 콘텐츠는 렌더링되지 않습니다. 의 콘텐츠 대신 섀도우 루트가 렌더링됩니다.

예를 들어 다음과 같은 마크업이 있는 경우:

<button>Hello, world!</button>
<script>
var host = document.querySelector('button');
var root = host.createShadowRoot();
root.textContent = 'こんにちは、影の世界!';
</script>

그런 다음

<button id="ex1a">Hello, world!</button>
<script>
function remove(selector) {
  Array.prototype.forEach.call(
      document.querySelectorAll(selector),
      function (node) { node.parentNode.removeChild(node); });
}

if (!HTMLElement.prototype.createShadowRoot) {
  remove('#ex1a');
  document.write('<img src="SS1.png" alt="Screenshot of a button with \'Hello, world!\' on it.">');
}
</script>

이렇게 보이게 하면

<button id="ex1b">Hello, world!</button>
<script>
(function () {
  if (!HTMLElement.prototype.createShadowRoot) {
    remove('#ex1b');
    document.write('<img src="SS2.png" alt="Screenshot of a button with \'Hello, shadow world!\' in Japanese on it.">');
    return;
  }
  var host = document.querySelector('#ex1b');
  var root = host.createShadowRoot();
  root.textContent = 'こんにちは、影の世界!';
})();
</script>

뿐만 아니라 페이지의 JavaScript가 textContent는 'こんにちは、影の世界!'! 하지만 'Hello, world!' DOM 하위 트리가 섀도우 루트 아래에 캡슐화됩니다

프레젠테이션에서 콘텐츠 분리

이제 Shadow DOM을 사용하여 콘텐츠를 분리하는 방법을 살펴보겠습니다. 살펴보겠습니다 다음과 같은 이름 태그가 있다고 가정해 보겠습니다.

<style>
.ex2a.outer {
  border: 2px solid brown;
  border-radius: 1em;
  background: red;
  font-size: 20pt;
  width: 12em;
  height: 7em;
  text-align: center;
}
.ex2a .boilerplate {
  color: white;
  font-family: sans-serif;
  padding: 0.5em;
}
.ex2a .name {
  color: black;
  background: white;
  font-family: "Marker Felt", cursive;
  font-size: 45pt;
  padding-top: 0.2em;
}
</style>
<div class="ex2a outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    Bob
  </div>
</div>

다음은 마크업입니다. 오늘 작성할 내용입니다. 그렇지 않습니다. Shadow DOM을 사용합니다.

<style>
.outer {
  border: 2px solid brown;
  border-radius: 1em;
  background: red;
  font-size: 20pt;
  width: 12em;
  height: 7em;
  text-align: center;
}
.boilerplate {
  color: white;
  font-family: sans-serif;
  padding: 0.5em;
}
.name {
  color: black;
  background: white;
  font-family: "Marker Felt", cursive;
  font-size: 45pt;
  padding-top: 0.2em;
}
</style>
<div class="outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    Bob
  </div>
</div>

DOM 트리에는 캡슐화가 없기 때문에 문서에 노출됩니다 페이지의 다른 요소가 실수로 동일한 클래스 이름을 사용하는 경우에도 힘든 시간을 보낼 수 있을 거예요.

힘든 시간을 피할 수 있습니다.

1단계: 프레젠테이션 세부정보 숨기기

의미상으로는 다음에만 신경 쓰면 됩니다.

  • 이름 태그입니다.
  • 이름은 '밥'이라고 합니다.

먼저 우리가 원하는 실제 의미 체계에 더 가까운 마크업을 작성합니다.

<div id="nameTag">Bob</div>

그런 다음 프레젠테이션에 사용된 모든 스타일과 div를 <template> 요소:

<div id="nameTag">Bob</div>
<template id="nameTagTemplate">
<span class="unchanged"><style>
.outer {
  border: 2px solid brown;

  … same as above …

</style>
<div class="outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    Bob
  </div>
</div></span>
</template>

이 시점에서 'Bob'은 렌더링되는 유일한 항목입니다. 왜냐하면 표시 DOM 요소를 내부로 이동하여 <template> 요소의 경우 렌더링되지 않지만 JavaScript에서 액세스할 수 있습니다. 이제 이 작업을 수행하여 섀도우 루트를 채웁니다.

<script>
var shadow = document.querySelector('#nameTag').createShadowRoot();
var template = document.querySelector('#nameTagTemplate');
var clone = document.importNode(template.content, true);
shadow.appendChild(clone);
드림

이제 섀도우 루트를 설정했으므로 이름 태그가 다시 시도합니다. 이름 태그를 마우스 오른쪽 버튼으로 클릭하고 요소를 사용하면 재미있고 의미 있는 마크업이 됩니다.

<div id="nameTag">Bob</div>

이는 Shadow DOM을 사용하여 문서 이름 태그의 프레젠테이션 세부정보를 가져옵니다. 이 프레젠테이션 세부정보는 Shadow DOM에 캡슐화됩니다.

2단계: 프레젠테이션에서 콘텐츠 분리

이제 이름 태그가 페이지에서 프레젠테이션 세부정보를 숨기지만 실제로는 콘텐츠와 구분되지 않습니다. 왜냐하면 페이지에 있는 콘텐츠('Bob')가 섀도우 루트로 복사한 것입니다 인코더-디코더 아키텍처를 이름을 바꾸려면 두 곳에 추가해야 하며 동기화에서 벗어납니다

HTML 요소는 합성입니다. 즉, 표 안에 버튼을 배치하거나 예로 들 수 있습니다. 여기서 컴포지션이 필요합니다. 이름 태그는 '안녕하세요!'라는 빨간색 배경의 텍스트, 콘텐츠 찾을 수 있습니다.

구성요소 작성자는 컴포지션이 구성요소와 어떻게 작동하는지 <content>라는 새 요소를 사용하여 위젯을 만들 수 있습니다. 이 위젯의 프레젠테이션에 삽입 지점을 만듭니다. 삽입 지점이 섀도 호스트에서 콘텐츠를 선별하고 사용할 수 있습니다.

Shadow DOM의 마크업을 다음과 같이 변경하면

<span class="unchanged"><template id="nameTagTemplate">
<style>
  …
</style></span>
<div class="outer">
  <div class="boilerplate">
    Hi! My name is
  </div>
  <div class="name">
    <content></content>
  </div>
</div>
<span class="unchanged"></template></span>

이름 태그가 렌더링될 때 섀도우 호스트의 콘텐츠는 <content> 요소가 있는 위치에 투영됨 표시됩니다.

이제 이름이 문서라는 한 곳에 모입니다. 페이지에서 사용자는 다음과 같이 작성하기만 하면 됩니다.

document.querySelector('#nameTag').textContent = 'Shellie';

이게 다입니다. 이름 태그의 렌더링이 자동으로 업데이트됩니다. 이는 브라우저의 콘텐츠를 투영하기 때문입니다. 이름 태그를 <content>를 사용하여 제자리에 삽입합니다.

<div id="ex2b">

이제 콘텐츠와 프레젠테이션이 분리되었습니다. 문서에 콘텐츠가 있는 경우 Shadow DOM에 있습니다 때가 되면 브라우저에서 자동으로 동기화 상태를 유지합니다. 뭔가를 렌더링할 수 있습니다.

3단계: 수익

콘텐츠와 프레젠테이션을 분리함으로써 콘텐츠를 조작하는 코드(예: 이름 태그)에서 코드 내에서만 고유하도록 하는 여러 개가 아닌 하나의 <div>.

이제 프레젠테이션을 변경하면 있습니다!

예를 들어 이름 태그를 현지화하려고 한다고 가정해 보겠습니다. 여전히 이름입니다 태그의 시맨틱 콘텐츠가 변경되지 않습니다.

<div id="nameTag">Bob</div>

섀도우 루트 설정 코드는 동일하게 유지됩니다. 텍스트에 넣는 것은 섀도 루트 변경사항:

<template id="nameTagTemplate">
<style>
.outer {
  border: 2px solid pink;
  border-radius: 1em;
  background: url(sakura.jpg);
  font-size: 20pt;
  width: 12em;
  height: 7em;
  text-align: center;
  font-family: sans-serif;
  font-weight: bold;
}
.name {
  font-size: 45pt;
  font-weight: normal;
  margin-top: 0.8em;
  padding-top: 0.2em;
}
</style>
<div class="outer">
  <div class="name">
    <content></content>
  </div>
  と申します。
</div>
</template>
드림

이것은 오늘날의 웹 상황에 비해 크게 개선된 것인데, 왜냐하면 이름 업데이트 코드는 구성요소도 있습니다. 이름 업데이트 코드는 있습니다 렌더링되는 내용을 고려하면 이름이 두 번째는 영어로 'Hi! 제 이름은)이지만 일본어로는 처음입니다 (이전 'tos申masす' 전) 이 구분은 의미론적으로 무의미하고 표시되는 이름을 업데이트하는 관점에서 따라서 이름 업데이트 코드는 해당 세부 사항에 대해 알 필요가 없습니다.

추가 크레딧: 고급 프로젝션

위의 예에서 <content> 요소는 섀도 호스트에서 모든 콘텐츠를 선별합니다. 이 select 속성을 통해 살펴보겠습니다. 또한 여러 콘텐츠를 사용하여 요소

예를 들어 다음이 포함된 문서가 있는 경우:

<div id="nameTag">
  <div class="first">Bob</div>
  <div>B. Love</div>
  <div class="email">bob@</div>
</div>

그리고 CSS 선택자를 사용하여 특정 콘텐츠를 선택하는 섀도우 루트가 있습니다.

<div style="background: purple; padding: 1em;">
  <div style="color: red;">
    <content **select=".first"**></content>
  </div>
  <div style="color: yellow;">
    <content **select="div"**></content>
  </div>
  <div style="color: blue;">
    <content **select=".email">**</content>
  </div>
</div>
드림

<div class="email"> 요소가 둘 다 일치함 <content select="div"><content select=".email"> 요소 철수씨는 몇 번이나 이메일을 어떤 색상으로 표시할까요?

대답은 Bob의 이메일 주소가 한 번 표시되고 노란색으로 표시된다는 것입니다.

그 이유는 Shadow DOM을 해킹하는 사람들이 알고 있듯이 화면에 실제로 렌더링되는 항목의 트리를 구성하는 것은 즐거운 시간이 되었길 바랍니다. 콘텐츠 요소는 초대를 통해 문서의 콘텐츠를 백스테이지 Shadow DOM 렌더링으로 있습니다. 이러한 초대장은 순서대로 전달됩니다. 누가 초대 받는 사람 (즉, select 속성) 콘텐츠(한 번) 초대를 받은 경우 항상 초대를 수락하고 (수락하지 않을 사람) 있습니다. 이후 해당 주소로 초대가 다시 전송되면 집에 아무도 없고 파티에 오지 않는 것 같습니다.

위의 예에서 <div class="email">는 다음과 일치합니다. div 선택기와 .email 모두 선택기에 있기 때문입니다. 하지만 div가 있는 콘텐츠 요소가 선택기는 문서의 앞부분에 있으므로 <div class="email">이(가) 옐로 파티에 갑니다. 파란 파티에 아무도 올 수 없습니다. (그렇다면 파란색인지. 고통은 회사를 사랑하지만, 알 수 없습니다.)

당사자 없음에 초대받은 경우 전혀 렌더링되지 않습니다. 지금까지 'Hello, world' 텍스트가 첫 번째 예입니다. 이는 목표를 달성하려고 할 때 완전히 다른 렌더링을 구현할 수 있습니다. 문서로서 페이지의 스크립트에 액세스할 수 있지만 다른 클라우드 스토리지에 Shadow DOM의 Shadow DOM 렌더링 모델을 사용합니다.

예를 들어 HTML에는 유용한 날짜 선택 도구가 있습니다. <input type="date">를 쓰면 깔끔한 팝업 캘린더가 표시됩니다. 하지만 만약 사용자가 디저트로 기간을 선택할 수 있게 하려는 경우 (예... 붉은 덩굴로 만들어진 해먹이 있음) 나 문서를 설정할 수 있습니다.

<div class="dateRangePicker">
  <label for="start">Start:</label>
  <input type="date" name="startDate" id="start">
  <br>
  <label for="end">End:</label>
  <input type="date" name="endDate" id="end">
</div>

테이블을 사용하여 세련된 캘린더를 만드는 Shadow DOM을 만듭니다. 날짜 범위 등이 강조 표시됩니다. 사용자가 구성요소가 캘린더의 상태를 startDate 및 endDate 입력; 사용자가 양식을 제출하면 이러한 입력 요소의 값이 제출됩니다.

문서에 라벨을 포함하지 않는데 왜 무엇인가요? 이는 사용자가 브라우저에서 양식을 보는 경우 Shadow DOM을 지원하지 않는 경우 형식은 여전히 사용할 수 있지만 예쁘다. 사용자에게 다음과 같이 표시됩니다.

<div class="dateRangePicker">
  <label for="start">Start:</label>
  <input type="date" name="startDate" id="start">
  <br>
  <label for="end">End:</label>
  <input type="date" name="endDate" id="end">
</div>

Shadow DOM 전달 101

이것이 Shadow DOM의 기본 사항이므로 Shadow DOM 101을 통과했습니다. 다음과 같은 작업을 할 수 있습니다. Shadow DOM으로 더 많은 작업을 수행할 수 있습니다. 예를 들어 하나의 섀도 호스트, 또는 캡슐화를 위한 중첩된 섀도우 또는 아키텍처 모델 기반 뷰 (MDV) 및 Shadow DOM을 사용하여 페이지를 만듭니다. 웹 구성요소는 Shadow DOM 그 이상입니다.

이후 게시물에서 이에 대해 설명합니다.