aboutsummaryrefslogtreecommitdiff
path: root/files/ko/web/web_components/using_shadow_dom/index.html
blob: 7d82820b76ee598cadc0dc0fea8c9f563a66ca39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
---
title: Using shadow DOM
slug: Web/Web_Components/Using_shadow_DOM
tags:
  - API
  - DOM
  - Guide
  - Web Components
  - shadow
  - shadow dom
  - 쉐도우 돔
  - 웹 컴포넌트
  - 웹컴포넌트
translation_of: Web/Web_Components/Using_shadow_DOM
---
<div>{{DefaultAPISidebar("Web Components")}}</div>

<p class="summary">웹 컴포넌트의 중요한 측면은 캡슐화입니다. 마크업 구조, 스타일 그리고 동작을 페이지 내의 다른 코드와<br>
 분리하고 숨긴채로 유지하여 서로 충돌하지 않으며, 코드가 좋고 깨끗하게 되도록 하는 중요한 측면입니다.<br>
 Shadow DOM API 는 이러한 캡슐화의 핵심이며, 숨겨지고 분리된 DOM 을 엘리먼트에 달 수 있는<br>
 방법입니다. 현재 문서는 Shadow DOM 의 기본적인 사용을 다루고 있습니다.</p>

<div class="note">
<p><strong>Note</strong>: Shadow DOM 은 Firefox (63 and onwards), Chrome, Opera, and Safari 에서 기본으로 지원되고 있습니다.  새로운 Chromium 기반의 Edge (75 and onwards) 또한 지원하고 있습니다. 그러나 구버전의 Edge 는 지원하지 않습니다.</p>
</div>

<h2 id="High-level_view">High-level view</h2>

<p>This article assumes you are already familiar with the concept of the <a href="/en-US/docs/Web/API/Document_Object_Model/Introduction">DOM (Document Object Model)</a> — a tree-like structure of connected nodes that represents the different elements and strings of text appearing in a markup document (usually an HTML document in the case of web documents). As an example, consider the following HTML fragment:</p>

<pre class="brush: html notranslate">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;title&gt;Simple DOM example&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
      &lt;section&gt;
        &lt;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."&gt;
        &lt;p&gt;Here we will add a link to the &lt;a href="https://www.mozilla.org/"&gt;Mozilla homepage&lt;/a&gt;&lt;/p&gt;
      &lt;/section&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre>

<p>This fragment produces the following DOM structure:</p>

<p><img alt="" src="https://mdn.mozillademos.org/files/14559/dom-screenshot.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>

<p><em>Shadow</em> DOM allows hidden DOM trees to be attached to elements in the regular DOM tree — this shadow DOM tree starts with a shadow root, underneath which can be attached to any elements you want, in the same way as the normal DOM.</p>

<p><img alt="" src="https://mdn.mozillademos.org/files/15788/shadow-dom.png" style="height: 543px; width: 1138px;"></p>

<p>There are some bits of shadow DOM terminology to be aware of:</p>

<ul>
 <li><strong>Shadow host</strong>: The regular DOM node that the shadow DOM is attached to.</li>
 <li><strong>Shadow tree</strong>: The DOM tree inside the shadow DOM.</li>
 <li><strong>Shadow boundary</strong>: the place where the shadow DOM ends, and the regular DOM begins.</li>
 <li><strong>Shadow root</strong>: The root node of the shadow tree.</li>
</ul>

<p>You can affect the nodes in the shadow DOM in exactly the same way as non-shadow nodes — for example appending children or setting attributes, styling individual nodes using element.style.foo, or adding style to the entire shadow DOM tree inside a {{htmlelement("style")}} element. The difference is that none of the code inside a shadow DOM can affect anything outside it, allowing for handy encapsulation.</p>

<p>Note that the shadow DOM is not a new thing by any means — browsers have used it for a long time to encapsulate the inner structure of an element. Think for example of a {{htmlelement("video")}} element, with the default browser controls exposed. All you see in the DOM is the <code>&lt;video&gt;</code> element, but it contains a series of buttons and other controls inside its shadow DOM. The shadow DOM spec has made it so that you are allowed to actually manipulate the shadow DOM of your own custom elements.</p>

<h2 id="Basic_usage">Basic usage</h2>

<p>You can attach a shadow root to any element using the {{domxref("Element.attachShadow()")}} method. This takes as its parameter an options object that contains one option — <code>mode</code> — with a value of <code>open</code> or <code>closed</code>:</p>

<pre class="brush: js notranslate">let shadow = elementRef.attachShadow({mode: 'open'});
let shadow = elementRef.attachShadow({mode: 'closed'});</pre>

<p><code>open</code> means that you can access the shadow DOM using JavaScript written in the main page context, for example using the {{domxref("Element.shadowRoot")}} property:</p>

<pre class="brush: js notranslate">let myShadowDom = myCustomElem.shadowRoot;</pre>

<p>If you attach a shadow root to a custom element with <code>mode: closed</code> set, you won't be able to access the shadow DOM from the outside — <code>myCustomElem.shadowRoot</code> returns <code>null</code>. This is the case with built in elements that contain shadow DOMs, such as <code>&lt;video&gt;</code>.</p>

<div class="note">
<p><strong>Note</strong>: As <a href="https://blog.revillweb.com/open-vs-closed-shadow-dom-9f3d7427d1af">this blog post shows</a>, it is actually fairly easy to work around closed shadow DOMs, and the hassle to completely hide them is often more than it's worth.</p>
</div>

<p>If you are attaching a shadow DOM to a custom element as part of its constructor (by far the most useful application of the shadow DOM), you would use something like this:</p>

<pre class="brush: js notranslate">let shadow = this.attachShadow({mode: 'open'});</pre>

<p>When you've attached a shadow DOM to an element, manipulating it is a matter of just using the same DOM APIs as you use for the regular DOM manipulation:</p>

<pre class="brush: js notranslate">var para = document.createElement('p');
shadow.appendChild(para);
// etc.</pre>

<h2 id="Working_through_a_simple_example">Working through a simple example</h2>

<p>Now let's walk through a simple example to demonstrate the shadow DOM in action inside a custom element — <code><a href="https://github.com/mdn/web-components-examples/tree/master/popup-info-box-web-component">&lt;popup-info-box&gt;</a></code> (see a <a href="https://mdn.github.io/web-components-examples/popup-info-box-web-component/">live example</a> also). This takes an image icon and a text string, and embeds the icon into the page. When the icon is focused, it displays the text in a pop up information box to provide further in-context information. To begin with, in our JavaScript file we define a class called <code>PopUpInfo</code>, which extends <code>HTMLElement</code>:</p>

<pre class="brush: js notranslate">class PopUpInfo extends HTMLElement {
  constructor() {
    // Always call super first in constructor
    super();

    // write element functionality in here

    ...
  }
}</pre>

<p>Inside the class definition we define the element's constructor, which defines all the functionality the element will have when an instance of it is instantiated.</p>

<h3 id="Creating_the_shadow_root">Creating the shadow root</h3>

<p>We first attach a shadow root to the custom element:</p>

<pre class="brush: js notranslate">// Create a shadow root
var shadow = this.attachShadow({mode: 'open'});</pre>

<h3 class="brush: js" id="Creating_the_shadow_DOM_structure">Creating the shadow DOM structure</h3>

<p class="brush: js">Next, we use some DOM manipulation to create the element's internal shadow DOM structure:</p>

<pre class="brush: js notranslate">// Create spans
var wrapper = document.createElement('span');
wrapper.setAttribute('class','wrapper');
var icon = document.createElement('span');
icon.setAttribute('class','icon');
icon.setAttribute('tabindex', 0);
var info = document.createElement('span');
info.setAttribute('class','info');

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

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

<h3 class="brush: js" id="Styling_the_shadow_DOM">Styling the shadow DOM</h3>

<p class="brush: js">After that we create a {{htmlelement("style")}} element and populate it with some CSS to style it:</p>

<pre class="brush: js notranslate">// Create some CSS to apply to the shadow dom
var 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;
}`;

</pre>

<h3 id="Attaching_the_shadow_DOM_to_the_shadow_root">Attaching the shadow DOM to the shadow root</h3>

<p>The final step is to attach all the created elements to the shadow root:</p>

<pre class="brush: js notranslate">// attach the created elements to the shadow dom
shadow.appendChild(style);
shadow.appendChild(wrapper);
wrapper.appendChild(icon);
wrapper.appendChild(info);</pre>

<h3 id="Using_our_custom_element">Using our custom element</h3>

<p>Once the class is defined, using the element is as simple as defining it, and putting it on the page, as explained in <a href="/en-US/docs/Web/Web_Components/Using_custom_elements">Using custom elements</a>:</p>

<pre class="brush: js notranslate">// Define the new element
customElements.define('popup-info', PopUpInfo);</pre>

<pre class="brush: html notranslate">&lt;<span class="pl-ent">popup-info</span> <span class="pl-e">img</span>=<span class="pl-s"><span class="pl-pds">"</span>img/alt.png<span class="pl-pds">"</span></span> <span class="pl-e">text</span>=<span class="pl-s"><span class="pl-pds">"</span>Your card validation code (CVC) is an extra
                                    security feature — it is the last 3 or 4
                                    numbers on the back of your card.<span class="pl-pds">"</span></span>&gt;</pre>

<div>
<h3 id="Internal_versus_external_styles">Internal versus external styles</h3>

<p>In the above example we apply style to the Shadow DOM using a {{htmlelement("style")}} element, but it is perfectly possible to do it by referencing an external stylesheet from a {{htmlelement("link")}} element instead.</p>

<p>For example, take a look at this code from our <a href="https://mdn.github.io/web-components-examples/popup-info-box-external-stylesheet/">popup-info-box-external-stylesheet</a> example (see the <a href="https://github.com/mdn/web-components-examples/blob/master/popup-info-box-external-stylesheet/main.js">source code</a>):</p>

<pre class="brush: js notranslate">// 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);</pre>

<p>Note that {{htmlelement("link")}} elements do not block paint of the shadow root, so there may be a flash of unstyled content (FOUC) while the stylesheet loads.</p>

<p>Many modern browsers implement an optimization for {{htmlelement("style")}} tags either cloned from a common node or that have identical text, to allow them to share a single backing stylesheet. With this optimization the performance of external and internal styles should be similar.</p>

<h2 id="See_also">See also</h2>

<ul>
 <li><a href="/en-US/docs/Web/Web_Components/Using_custom_elements">Using custom elements</a></li>
 <li><a href="/en-US/docs/Web/Web_Components/Using_templates_and_slots">Using templates and slots</a></li>
</ul>
</div>