aboutsummaryrefslogtreecommitdiff
path: root/files/ko/web/web_components
diff options
context:
space:
mode:
authorPeter Bengtsson <mail@peterbe.com>2020-12-08 14:42:17 -0500
committerPeter Bengtsson <mail@peterbe.com>2020-12-08 14:42:17 -0500
commitda78a9e329e272dedb2400b79a3bdeebff387d47 (patch)
treee6ef8aa7c43556f55ddfe031a01cf0a8fa271bfe /files/ko/web/web_components
parent1109132f09d75da9a28b649c7677bb6ce07c40c0 (diff)
downloadtranslated-content-da78a9e329e272dedb2400b79a3bdeebff387d47.tar.gz
translated-content-da78a9e329e272dedb2400b79a3bdeebff387d47.tar.bz2
translated-content-da78a9e329e272dedb2400b79a3bdeebff387d47.zip
initial commit
Diffstat (limited to 'files/ko/web/web_components')
-rw-r--r--files/ko/web/web_components/index.html223
-rw-r--r--files/ko/web/web_components/using_custom_elements/index.html248
-rw-r--r--files/ko/web/web_components/using_shadow_dom/index.html230
3 files changed, 701 insertions, 0 deletions
diff --git a/files/ko/web/web_components/index.html b/files/ko/web/web_components/index.html
new file mode 100644
index 0000000000..f097979a1b
--- /dev/null
+++ b/files/ko/web/web_components/index.html
@@ -0,0 +1,223 @@
+---
+title: 웹 컴포넌트
+slug: Web/Web_Components
+tags:
+ - Landing
+ - NeedsTranslation
+ - Template
+ - Web Components
+ - custom elements
+ - slot
+ - 웹 컴포넌트
+translation_of: Web/Web_Components
+---
+<p>{{DefaultAPISidebar("Web Components")}}</p>
+
+<div class="summary">
+<p>웹 컴포넌트는 그 기능을 나머지 코드로부터 캡슐화하여 재사용 가능한 커스텀 엘리먼트를 생성하고 웹 앱에서 활용할 수 있도록 해주는 다양한 기술들의 모음입니다.</p>
+</div>
+
+<h2 id="개념_및_사용">개념 및 사용</h2>
+
+<p>개발자로서 우리 모두는 가능한 한 코드를 재사용하는 것이 좋은 생각이라는 것을 알고 있습니다. 이는 전통적으로 커스텀 마크업 구조에선 쉽지 않았습니다. 커스텀 UI 컨트롤을 렌더링하기위해 작성해야하는 복잡한 HTML (및 관련된 스타일과 스크립트)을 생각해보십시오. 여러번 사용할 때 조심하지 않으면 페이지가 엉망이 될 수 있습니다.</p>
+
+<p>웹 컴포넌트는 다음 문제들을 해결하는 것을 목표로 합니다 — 세 가지 주요 기술들로 구성되며, 재사용을 원하는 어느곳이든 코드 충돌에 대한 걱정이 없는 캡슐화된 기능을 갖춘 다용도의 커스텀 엘리먼트를 생성하기 위해 함께 사용될 수 있습니다.</p>
+
+<p><strong>커스텀 엘리먼트</strong>: 커스텀 엘리먼트와 그 동작을 정의할 수 있도록 해주는 JavaScript API 의 집합으로, 사용자 인터페이스에서 원했던대로 사용될 수 있습니다.</p>
+
+<ul>
+ <li><strong>Custom elements</strong>: 사용자 인터페이스에서 원하는대로 사용할 수있는 사용자 정의 요소 및 해당 동작을 정의 할 수있는 JavaScript API 세트입니다.</li>
+ <li><strong>Shadow DOM</strong>: 캡슐화된 "그림자" DOM 트리를 엘리먼트 — 메인 다큐먼트 DOM 으로부터 독립적으로 렌더링 되는 — 를 추가하고 연관된 기능을 제어하기 위한 JavaScript API 의 집합. 이 방법으로 엘리먼트의 기능을 프라이빗하게 유지할 수 있어, 다큐먼트의 다른 부분과의 충돌에 대한 걱정 없이 스크립트와 스타일을 작성할 수 있습니다.</li>
+ <li><strong>HTML 템플릿</strong>: {{HTMLElement("template")}} 과 {{HTMLElement("slot")}} 엘리먼트는 렌더링된 페이지에 나타나지 않는 마크업 템플릿을 작성할 수 있게 해줍니다. 그 후, 커스텀 엘리먼트의 구조를 기반으로 여러번 재사용할 수 있습니다.</li>
+</ul>
+
+<p>웹 컴포넌트를 구현하기 위한 기본 접근법은 일반적으로 다음과 같습니다.</p>
+
+<ol>
+ <li>ECMAScript 2015 클래스 문법(자세한 내용은 <a href="/ko/docs/Web/JavaScript/Reference/Classes">Classes</a>에서 확인)을 사용해 웹 컴포넌트 기능을 명시하는 클래스를 생성합니다.</li>
+ <li>{{domxref("CustomElementRegistry.define()")}} 메소드를 사용해 새로운 커스텀 엘리먼트를 등록하고, 정의할 엘리먼트 이름, 기능을 명시하고 있는 클래스, (선택적으로) 상속받은 엘리먼트를 전달합니다.</li>
+ <li>필요한 경우, {{domxref("Element.attachShadow()")}} 메소드를 사용해 shadow DOM 을 커스텀 엘리먼트에 추가합니다. 일반적인 DOM 메소드를 사용해 자식 엘리먼트, 이벤트 리스너 등을 shadow DOM 에 추가합니다.</li>
+ <li>필요한 경우, {{htmlelement("template")}} 과 {{htmlelement("slot")}} 을 사용해 HTML 템플릿을 정의합니다. 다시 일반적인 DOM 메소드를 사용해 템플릿을 클론하고 shadow DOM 에 추가합니다.</li>
+ <li>일반적인 HTML 엘리먼트처럼, 페이지의 원하는 어느곳이든 커스텀 엘리먼트를 사용할 수 있습니다.</li>
+</ol>
+
+<h2 id="튜토리얼">튜토리얼</h2>
+
+<dl>
+ <dt><a href="/ko/docs/Web/Web_Components/Using_custom_elements">커스텀 엘리먼트 사용하기</a></dt>
+ <dd>간단한 웹 컴포넌트를 생성하는 커스텀 엘리먼트의 기능을 사용하는 방법과 라이프사이클 콜백 및 그 외 고급 기능들을 보여주는 가이드입니다.</dd>
+ <dt><a href="/ko/docs/Web/Web_Components/Using_shadow_DOM">shadow DOM 사용하기</a></dt>
+ <dd>shadow DOM 기본을 살펴보고, shadow DOM 을 엘리먼트에 추가하고, shadow DOM 트리를 추가하고 스타일링하는 방법 등을 보여주는 가이드입니다.</dd>
+ <dt><a href="/ko/docs/Web/Web_Components/Using_templates_and_slots">템플릿과 슬롯 사용하기</a></dt>
+ <dd>{{htmlelement("template")}} 과 {{htmlelement("slot")}} 엘리먼트를 사용해 재사용가능한 HTML 구조를 정의하는 방법과 웹 컴포넌트 내에서 그 구조를 사용하는 방법을 보여주는 가이드입니다.</dd>
+</dl>
+
+<h2 id="레퍼런스">레퍼런스</h2>
+
+<h3 id="커스텀_엘리먼트">커스텀 엘리먼트</h3>
+
+<dl>
+ <dt>{{domxref("CustomElementRegistry")}}</dt>
+ <dd>커스텀 엘리먼트와 관련된 기능을 포함하며, 새로운 커스텀 엘리먼트를 등록하여 다큐먼트에서 사용할 수 있도록 해주는 {{domxref("CustomElementRegistry.define()")}} 메소드가 가장 주요합니다.</dd>
+ <dt>{{domxref("Window.customElements")}}</dt>
+ <dd><code>CustomElementRegistry</code> 객체에 대한 참조를 반환합니다.</dd>
+ <dt><a href="/ko/docs/Web/Web_Components/Using_custom_elements#Using_the_lifecycle_callbacks">라이프 사이클 콜백</a></dt>
+ <dd>커스텀 엘리먼트의 클래스 정의 내에 정의되어 동작에 영향을 주는 특별한 콜백 함수입니다.
+ <ul>
+ <li><code>connectedCallback</code>: 커스텀 엘리먼트가 처음으로 다큐먼트의 DOM 에 연결되었을 때 호출됩니다.</li>
+ <li><code>disconnectedCallback</code>: 커스텀 엘리먼트가 다큐먼트의 DOM 으로부터 연결 해제되었을 때 호출됩니다.</li>
+ <li><code>adoptedCallback</code>: 커스텀 엘리먼트가 새로운 다큐먼트로 이동되었을 때 호출됩니다.</li>
+ <li><code>attributeChangedCallback</code>: 커스텀 엘리먼트의 어트리뷰트가 추가, 제거 또는 변경되었을  때 호출됩니다.</li>
+ </ul>
+ </dd>
+ <dd>
+ <ul>
+ </ul>
+ </dd>
+</dl>
+
+<dl>
+ <dt>커스텀 내장 엘리먼트 생성을 위한 확장 기능</dt>
+ <dd>
+ <ul>
+ <li>{{htmlattrxref("is")}} 전역 HTML 어트리뷰트: 표준 HTML 엘리먼트가 등록된 커스텀 내장 엘리먼트처럼 동작하도록 지정을 허용합니다.</li>
+ <li>{{domxref("Document.createElement()")}} 메소드의 "is" 옵션: 주어진 등록된 커스텀 내장 엘리먼트처럼 동작하는 표준 HTML 엘리먼트의 인스턴스를 생성하는 것을 허용합니다.</li>
+ </ul>
+ </dd>
+ <dt>CSS 수도(pseudo) 클래스</dt>
+ <dd>커스텀 엘리먼트에 관련된 수도 클래스입니다.
+ <ul>
+ <li>{{cssxref(":defined")}}: 내장 엘리먼트와 <code>CustomElementRegistry.define()</code> 으로 정의된 커스텀 엘리먼트를 포함해, 정의된 모든 엘리먼트와 일치합니다.</li>
+ <li>{{cssxref(":host")}}: 대상 CSS 를 내부에 포함하고 있는 <a href="/ko/docs/Web/Web_Components/Using_shadow_DOM">shadow DOM</a> 의 shadow 호스트를 선택합니다.</li>
+ <li>{{cssxref(":host()")}}: 대상 CSS 를 내부에 포함하고 있는 <a href="/ko/docs/Web/Web_Components/Using_shadow_DOM">shadow DOM</a> 의 shadow 호스트를 선택합니다(따라서 shadow DOM 내부에서 커스텀 엘리먼트를 선택할 수 있습니다) — 함수의 파라미터로써 주어진 셀렉터가 shadow 호스트에 일치하는 경우에만 해당합니다.</li>
+ <li>{{cssxref(":host-context()")}}: 대상 CSS 를 내부에 포함하고 있는 <a href="/ko/docs/Web/Web_Components/Using_shadow_DOM">shadow DOM</a> 의 shadow 호스트를 선택합니다(따라서 shadow DOM 내부에서 커스텀 엘리먼트를 선택할 수 있습니다) — 함수의 파라미터로써 주어진 셀렉터가 DOM 계층 내에 위치한 shadow 호스트의 조상에 일치하는 경우에만 해당합니다.</li>
+ </ul>
+ </dd>
+</dl>
+
+<h3 id="Shadow_DOM">Shadow DOM</h3>
+
+<dl>
+ <dt>{{domxref("ShadowRoot")}}</dt>
+ <dd>shadow DOM 하위 트리의 루트 노드를 나타냅니다.</dd>
+ <dt>{{domxref("DocumentOrShadowRoot")}}</dt>
+ <dd>다큐먼트와 shadow 루트 모두에서 사용가능한 기능을 정의하는 믹스인</dd>
+ <dt>{{domxref("Element")}} 확장 기능</dt>
+ <dd>shadow DOM 에 관련된 <code>Element</code> 인터페이스의 확장 기능입니다.
+ <ul>
+ <li>{{domxref("Element.attachShadow()")}} 메소드는 shadow DOM 트리를 지정된 엘리먼트에 추가합니다.</li>
+ <li>{{domxref("Element.shadowRoot")}} 프로퍼티는 지정된 엘리먼트에 추가된 shadow 루트를 반환합니다. 추가된 shadow 루트가 없을 경우 <code>null</code> 을 반환합니다.</li>
+ </ul>
+ </dd>
+ <dt>{{domxref("Node")}} 추가 관련</dt>
+ <dd>shadow DOM 과 관련된 <code>Node</code> 인터페이스에 추가
+ <ul>
+ <li>{{domxref("Node.getRootNode()")}} 메소드는 사용 가능한 shadow 루트가 있을 경우 선택적으로 이를 포함하는 컨텍스트의 객체의 루트를 반환합니다.</li>
+ <li>{{domxref("Node.isConnected")}} 프로퍼티는 노드가 직접 또는 간접적으로 컨텍스트 객체(예를 들면, 일반 DOM 의 경우 {{domxref("Document")}} 객체, shadow DOM 의 경우 {{domxref("ShadowRoot")}})에 연결되었는지를 나타내는 boolean 을 반환합니다.</li>
+ </ul>
+ </dd>
+ <dt>{{domxref("Event")}} 확장 기능</dt>
+ <dd>shadow DOM 과 관련된 <code>Event</code> 인터페이스의 확장 기능
+ <ul>
+ <li>{{domxref("Event.composed")}}: 이벤트가 shadow DOM 영역을 거쳐 일반 DOM 으로 전파 되는지를 나타내는 {{jsxref("Boolean")}} 을 반환합니다(전파되면 <code>true</code>, 그렇지 않으면 <code>false</code>) </li>
+ <li>{{domxref("Event.composedPath")}}: 이벤트의 패스(이벤트가 호출될 객체)를 반환합니다. shadow 루트가 {{domxref("ShadowRoot.mode")}} closed 로 생성된경우 shadow 트리의 노드를 포함하지 않습니다.</li>
+ </ul>
+ </dd>
+</dl>
+
+<h3 id="HTML_템플릿">HTML 템플릿</h3>
+
+<dl>
+ <dt>{{htmlelement("template")}}</dt>
+ <dd>다큐먼트가 처음 로드될 때 렌더링되지 않지만 JavaScript를 사용하는 런타임에 나타나는 HTML 조각을 포함합니다. 주로 커스텀 엘리먼트 구조의 기본으로 사용됩니다. 관련된 DOM 인터페이스로 {{domxref("HTMLTemplateElement")}} 가 있습니다.</dd>
+ <dt>{{htmlelement("slot")}}</dt>
+ <dd>자신만의 마크업으로 채울 수 있는 웹 컴포넌트 내부의 placeholder 입니다. 별도의 DOM 트리로 생성하고 함께 보여줄 수 있게 해줍니다. 관련된 DOM 인터페이스로 {{domxref("HTMLSlotElement")}} 가 있습니다.</dd>
+ <dt><code><a href="/ko/docs/Web/HTML/Global_attributes/slot">slot</a></code> 전역 HTML 어트리뷰트</dt>
+ <dd>shadow DOM shadow 트리내의 slot을 엘리먼트로 할당합니다.</dd>
+ <dt>{{domxref("Slotable")}}</dt>
+ <dd>{{domxref("Element")}} 와 {{domxref("Text")}} 노드로 구현된 믹스인입니다. {{htmlelement("slot")}} 엘리먼트의 컨텐츠로 만들기 위한 기능들을 정의합니다. 믹스인은 노드가 삽입된 슬롯의 참조를 반환하는 하나의 어트리뷰트 {{domxref("Slotable.assignedSlot")}} 을 정의합니다.</dd>
+</dl>
+
+<dl>
+ <dt>{{domxref("Element")}} 확장 기능</dt>
+ <dd>슬롯과 관련된 <code>Element</code> 인터페이스 확장 기능
+ <ul>
+ <li>{{domxref("Element.slot")}}: 엘리먼트에 추가된 shadow DOM 슬롯의 이름을 반환합니다.</li>
+ </ul>
+ </dd>
+ <dt>CSS 수도(pseudo) 엘리먼트</dt>
+ <dd>슬롯과 관련된 수도 엘리먼트입니다.
+ <ul>
+ <li>{{cssxref("::slotted")}}: 슬롯으로 삽입된 모든 컨텐츠와 일치합니다.</li>
+ </ul>
+ </dd>
+ <dt>{{event("slotchange")}} 이벤트</dt>
+ <dd>슬롯에 포함된 노드가 변경될 때 {{domxref("HTMLSlotElement")}} 인스턴스({{htmlelement("slot")}} 엘리먼트)에서 실행됩니다.</dd>
+</dl>
+
+<h2 id="예제">예제</h2>
+
+<p><a href="https://github.com/mdn/web-components-examples">web-components-examples</a> GitHub 저장소에 많은 예제를 생성하였습니다. 더 많은 예제가 추가될 예정입니다.</p>
+
+<h2 id="명세">명세</h2>
+
+<table>
+ <tbody>
+ <tr>
+ <th scope="col">명세</th>
+ <th scope="col">상태</th>
+ <th scope="col">코멘트</th>
+ </tr>
+ <tr>
+ <td>{{SpecName("HTML WHATWG","scripting.html#the-template-element","&lt;template&gt; element")}}</td>
+ <td>{{Spec2("HTML WHATWG")}}</td>
+ <td>{{HTMLElement("template")}} 의 정의.</td>
+ </tr>
+ <tr>
+ <td>{{SpecName("HTML WHATWG","custom-elements.html#custom-elements","custom elements")}}</td>
+ <td>{{Spec2("HTML WHATWG")}}</td>
+ <td><a href="https://developer.mozilla.org/ko/docs/Web/Web_Components/Using_custom_elements">HTML 커스텀 엘리먼트</a>의 정의.</td>
+ </tr>
+ <tr>
+ <td>{{SpecName("DOM WHATWG","#shadow-trees","shadow trees")}}</td>
+ <td>{{Spec2("DOM WHATWG")}}</td>
+ <td><a href="https://developer.mozilla.org/ko/docs/Web/Web_Components/Using_shadow_DOM">Shadow DOM</a> 의 정의.</td>
+ </tr>
+ <tr>
+ <td>{{SpecName("HTML Imports", "", "")}}</td>
+ <td>{{Spec2("HTML Imports")}}</td>
+ <td>초기 <a href="https://developer.mozilla.org/ko/docs/Web/Web_Components/HTML_Imports">HTML Imports</a> 정의.</td>
+ </tr>
+ <tr>
+ <td>{{SpecName("Shadow DOM", "", "")}}</td>
+ <td>{{Spec2("Shadow DOM")}}</td>
+ <td>초기 <a href="https://developer.mozilla.org/ko/docs/Web/Web_Components/Using_shadow_DOM">Shadow DOM</a> 정의.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="브라우저_호환성">브라우저 호환성</h2>
+
+<p> </p>
+
+<p>일반적인 경우,</p>
+
+<ul>
+ <li>웹 컴포넌트는 기본적으로 Firefox (버전 63), Chrome, 및 Opera 에서 지원됩니다.</li>
+ <li>Safari 는 많은 웹 컴포넌트 기능을 지원하지만 위 브라우저들만큼은 아닙니다.</li>
+ <li>Edge 는 구현 작업중입니다.</li>
+</ul>
+
+<p>특정 기능들의 브라우저 지원에 대한 상세 내용은, 위에 나열된 레퍼런스 페이지들에서 찾아보시기 바랍니다.</p>
+
+<p> </p>
+
+<h2 id="함께_보기">함께 보기</h2>
+
+<ul>
+ <li><a href="https://www.webcomponents.org/">webcomponents.org</a> — 웹 컴포넌트 예제, 튜토리얼 등의 정보를 포함하는 사이트.</li>
+ <li><a href="https://github.com/hybridsjs/hybrids">Hybrids</a> — 오픈 소스 웹 컴포넌트 라이브러리. <code>class</code> 와 <font face="consolas, Liberation Mono, courier, monospace">this</font> 문법보다 일반 객체와 순수 함수를 선호합니다. 커스텀 엘리먼트 생성을 위한 간단한 함수형 API 를 제공합니다.</li>
+ <li><a href="https://www.polymer-project.org/">Polymer</a> — 구글의 웹 컴포넌트 프레임워크 — 폴리필, 확장 및 예제의 집합. 현재 크로스 브라우징 웹 컴포넌트를 사용하는 가장 쉬운 방법.</li>
+ <li><a href="https://github.com/devpunks/snuggsi#readme">Snuggsi.es</a> — 폴리필 포함 ~1kB 크기의 쉬운 웹 컴포넌트 — 브라우저와 HTML, CSS, JavaScript 클래스에 대한 기본 이해만으로 생산성을 높일 수 있습니다.</li>
+ <li><a href="https://github.com/slimjs/slim.js">Slim.js</a> — 오픈 소스 웹 컴포넌트 라이브러리 — 빠르고 쉬운 컴포넌트 작성을 위한 고성능 라이브러리; 확장 가능하며 플러거블하고 크로스 프레임워크와 호환됩니다.</li>
+</ul>
diff --git a/files/ko/web/web_components/using_custom_elements/index.html b/files/ko/web/web_components/using_custom_elements/index.html
new file mode 100644
index 0000000000..c98c6aa275
--- /dev/null
+++ b/files/ko/web/web_components/using_custom_elements/index.html
@@ -0,0 +1,248 @@
+---
+title: 커스텀 엘리먼트 사용하기
+slug: Web/Web_Components/Using_custom_elements
+tags:
+ - Classes
+ - Guide
+ - HTML
+ - Web Components
+translation_of: Web/Web_Components/Using_custom_elements
+---
+<div>{{DefaultAPISidebar("Web Components")}}</div>
+
+<p class="summary">웹 컴포넌트 표준의 주요 기능 중 하나는 HTML 페이지에 커스텀 페이지 기능을 함께 제공하는 엘리먼트의 길고 중첩된 묶음을 만드는 것보다 사용자의 기능이 캡슐화된 커스텀 엘리먼트를 생성하는 기능입니다. 이 문서는 HTML 커스텀 엘리먼트의 사용을 소개합니다.</p>
+
+<div class="note">
+<p><strong>노트</strong>: 커스텀 엘리먼트는 크롬과 오페라에서 기본으로 제공됩니다. 최근에는 사용자가 두 브라우저에서  <code>dom.webcomponents.shadowdom.enabled</code> 과 <code>dom.webcomponents.customelements.enabled</code> 를 <code>true</code> 로 설정하면 사용가능합니다. 파이어폭스는 버전 60/61에서 기본으로 사용할 수 있도록 계획되었습니다. 사파리는 지금까지 <a href="https://w3c.github.io/webcomponents/spec/custom/#autonomous-custom-element">Autonomous custom elements</a>만을 제공하고 엣지 또한 잘 구현되어 있습니다.</p>
+</div>
+
+<h2 id="중요내용_보기">중요내용 보기</h2>
+
+<p>웹 document의 커스텀 엘리먼트의 컨트롤러는 {{domxref("CustomElementRegistry")}} 오브젝트입니다. 이 오브젝트는 사용자가 페이지에서 커스텀 엘리먼트를 등록하도록 하며 등록된 커스텀 컴포넌트의 정보 등을 리턴합니다.</p>
+
+<p>페이지에 커스텀 엘리먼트를 등록하려면 {{domxref("CustomElementRegistry.define()")}} 메소드를 사용하십시오. 이 메소드는 다음과 같은 인자들을 사용합니다. </p>
+
+<ul>
+ <li>{{domxref("DOMString")}} 은 사용자가 엘리먼트에게 전달하려는 이름을 나타냅니다. 커스텀 엘리먼트 이름들은 <a href="https://stackoverflow.com/questions/22545621/do-custom-elements-require-a-dash-in-their-name">dash('-')가 포함된 이름을 사용하도록</a> 주의하십시오. 이 이름들은 한 단어가 될 수 없습니다. (예 : custom-my-element)  </li>
+ <li>엘리먼트의 행위가 정의된 <a href="/en-US/docs/Web/JavaScript/Reference/Classes">class</a> 오브젝트.</li>
+ <li>선택적으로, 커스텀 엘리먼트는 특정 내장 엘리먼트를 상속받도록 지정할 수 있는 옵션 객체   <code>extends</code> 속성을 포함합니다.</li>
+</ul>
+
+<p>예를 들면, 우리는 커스텀 <a href="https://mdn.github.io/web-components-examples/word-count-web-component/">word-count element</a> 를 아래와 같이 정의할 수 있습니다 :</p>
+
+<pre class="brush: js notranslate">customElements.define('word-count', WordCount, { extends: 'p' });</pre>
+
+<p>이 엘리먼트는 <code>word-count</code> 로 불리며 클래스 오브젝트는 <code>WordCount</code> 입니다. 또한 {{htmlelement("p")}} 엘리먼트를 상속받습니다.</p>
+
+<p>커스텀 엘리먼트의 클래스 오브젝트는 표준 ES 2015 클래스 문법으로 작성됩니다. 예를 들면, <code>WordCount</code> 는 다음과 같은 구조입니다 :</p>
+
+<pre class="brush: js notranslate">class WordCount extends HTMLParagraphElement {
+ constructor() {
+ // 항상 생성자에서 super는 처음으로 호출됩니다
+ super();
+
+ // 엘리먼트의 기능들은 여기에 작성합니다.
+
+ ...
+ }
+}</pre>
+
+<p>위의 예제는 매우 간단하지만, 더 많은 것들을 할 수 있습니다. 특정 지점에서 실행되는 생성자 내부에서 특정 지점에서 실행하는 생명주기 콜백을 정의할 수 있습니다. 예를 들면, <code>connectedCallback</code> 은 커스텀 엘리먼트가 document의 DOM에 연결될 때마다 호출되는 반면, <code>attributeChangedCallback</code>은 커스텀 엘리먼트의 애트리뷰트가 추가, 제거 또는 변경될때 호출됩니다.</p>
+
+<p>아래의 {{anch("Using the lifecycle callbacks")}} 부분에서 이러한 부분을 더 배울 수 있습니다.</p>
+
+<p>커스텀 엘리먼트에는 두 종류가 있습니다:</p>
+
+<ul>
+ <li><strong>Autonomous custom elements</strong> are standalone — they don't inherit from standard HTML elements. You use these on a page by literally writing them out as an HTML element. For example <code>&lt;popup-info&gt;</code>, or <code>document.createElement("popup-info")</code>.</li>
+ <li><strong>Customized built-in elements</strong> inherit from basic HTML elements. To create one of these, you have to specify which element they extend (as implied in the examples above), and they are used by writing out the basic element but specifying the name of the custom element in the {{htmlattrxref("is")}} attribute (or property). For example <code>&lt;p is="word-count"&gt;</code>, or <code>document.createElement("p", { is: "word-count" })</code>.</li>
+</ul>
+
+<h2 id="Working_through_some_simple_examples">Working through some simple examples</h2>
+
+<p>At this point, let's go through some more simple examples to show you how custom elements are created in more detail.</p>
+
+<h3 id="Autonomous_custom_elements">Autonomous custom elements</h3>
+
+<p>Let's have a look at an example of an autonomous 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>). 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.</p>
+
+<p>To begin with, the JavaScript file defines a class called <code>PopUpInfo</code>, which extends {{domxref("HTMLElement")}}. Autonomous custom elements nearly always extend <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>The preceding code snippet contains the {{jsxref("Classes.constructor","constructor")}} definition for the class, which always starts by calling <code><a href="/en-US/docs/Web/JavaScript/Reference/Operators/super">super()</a></code> so that the correct prototype chain is established.</p>
+
+<p>Inside the constructor, we define all the functionality the element will have when an instance of it is instantiated. In this case we attach a shadow root to the custom element, use some DOM manipulation to create the element's internal shadow DOM structure — which is then attached to the shadow root — and finally attach some CSS to the shadow root to style it.</p>
+
+<pre class="brush: js notranslate">// Create a shadow root
+var shadow = this.attachShadow({mode: 'open'});
+
+// 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);
+
+// Create some CSS to apply to the shadow dom
+var style = document.createElement('style');
+
+style.textContent = '.wrapper {' +
+// CSS truncated for brevity
+
+// attach the created elements to the shadow dom
+
+shadow.appendChild(style);
+shadow.appendChild(wrapper);
+wrapper.appendChild(icon);
+wrapper.appendChild(info);</pre>
+
+<p>Finally, we register our custom element on the <code>CustomElementRegistry</code> using the <code>define()</code> method we mentioned earlier — in the parameters we specify the element name, and then the class name that defines its functionality:</p>
+
+<pre class="brush: js notranslate">customElements.define('popup-info', PopUpInfo);</pre>
+
+<p>It is now available to use on our page. Over in our HTML, we use it like so:</p>
+
+<pre class="brush: html notranslate">&lt;popup-info img="img/alt.png" text="Your card validation code (CVC)
+ is an extra security feature — it is the last 3 or 4 numbers on the
+ back of your card."&gt;</pre>
+
+<div class="note">
+<p><strong>Note</strong>: You can see the <a href="https://github.com/mdn/web-components-examples/blob/master/popup-info-box-web-component/main.js">full JavaScript source</a> code here.</p>
+</div>
+
+<h3 id="Customized_built-in_elements">Customized built-in elements</h3>
+
+<p>Now let's have a look at another customized built in element example — <a href="https://github.com/mdn/web-components-examples/tree/master/expanding-list-web-component">expanding-list</a> (<a href="https://mdn.github.io/web-components-examples/expanding-list-web-component/">see it live also</a>). This turns any unordered list into an expanding/collapsing menu.</p>
+
+<p>First of all, we define our element's class, in the same manner as before:</p>
+
+<pre class="brush: js notranslate">class ExpandingList extends HTMLUListElement {
+ constructor() {
+ // Always call super first in constructor
+ super();
+
+ // write element functionality in here
+
+ ...
+ }
+}</pre>
+
+<p>We will not explain the element functionality in any detail here, but you can discover how it works by checking out the source code. The only real difference here is that our element is extending the {{domxref("HTMLUListElement")}} interface, and not {{domxref("HTMLElement")}}. So it has all the characteristics of a {{htmlelement("ul")}} element with the functionality we define built on top, rather than being a standalone element. This is what makes it a customized built-in, rather than an autonomous element.</p>
+
+<p>Next, we register the element using the <code>define()</code> method as before, except that this time it also includes an options object that details what element our custom element inherits from:</p>
+
+<pre class="brush: js notranslate">customElements.define('expanding-list', ExpandingList, { extends: "ul" });</pre>
+
+<p>Using the built-in element in a web document also looks somewhat different:</p>
+
+<pre class="brush: html notranslate">&lt;ul is="expanding-list"&gt;
+
+ ...
+
+&lt;/ul&gt;</pre>
+
+<p>You use a <code>&lt;ul&gt;</code> element as normal, but specify the name of the custom element inside the <code>is</code> attribute.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Again, you can see the full <a href="https://github.com/mdn/web-components-examples/blob/master/expanding-list-web-component/main.js">JavaScript source code</a> here.</p>
+</div>
+
+<h2 id="Using_the_lifecycle_callbacks">Using the lifecycle callbacks</h2>
+
+<p>You can define several different callbacks inside a custom element's constructor, which fire at different points in the element's lifecycle:</p>
+
+<ul>
+ <li><code>connectedCallback</code>: Invoked when the custom element is first connected to the document's DOM.</li>
+ <li><code>disconnectedCallback</code>: Invoked when the custom element is disconnected from the document's DOM.</li>
+ <li><code>adoptedCallback</code>: Invoked when the custom element is moved to a new document.</li>
+ <li><code>attributeChangedCallback</code>: Invoked when one of the custom element's attributes is added, removed, or changed.</li>
+</ul>
+
+<p>Let's look at an example of these in use. The code below is taken from the <a href="https://github.com/mdn/web-components-examples/tree/master/life-cycle-callbacks">life-cycle-callbacks</a> example (<a href="https://mdn.github.io/web-components-examples/life-cycle-callbacks/">see it running live</a>). This is a trivial example that simply generates a colored square of a fixed size on the page. The custom element looks like this:</p>
+
+<pre class="brush: html notranslate">&lt;custom-square l="100" c="red"&gt;&lt;/custom-square&gt;</pre>
+
+<p>The class constructor is really simple — here we attach a shadow DOM to the element, then attach empty {{htmlelement("div")}} and {{htmlelement("style")}} elements to the shadow root:</p>
+
+<pre class="brush: js notranslate">var shadow = this.attachShadow({mode: 'open'});
+
+var div = document.createElement('div');
+var style = document.createElement('style');
+shadow.appendChild(style);
+shadow.appendChild(div);</pre>
+
+<p>The key function in this example is <code>updateStyle()</code> — this takes an element, gets its shadow root, finds its <code>&lt;style&gt;</code> element, and adds {{cssxref("width")}}, {{cssxref("height")}}, and {{cssxref("background-color")}} to the style.</p>
+
+<pre class="brush: js notranslate">function updateStyle(elem) {
+ var shadow = elem.shadowRoot;
+ var childNodes = shadow.childNodes;
+ for(var i = 0; i &lt; childNodes.length; i++) {
+ if(childNodes[i].nodeName === 'STYLE') {
+ childNodes[i].textContent = 'div {' +
+ ' width: ' + elem.getAttribute('l') + 'px;' +
+ ' height: ' + elem.getAttribute('l') + 'px;' +
+ ' background-color: ' + elem.getAttribute('c');
+ }
+ }
+}</pre>
+
+<p>The actual updates are all handled by the life cycle callbacks, which are placed inside the constructor. The <code>connectedCallback()</code> runs when the element is added to the DOM — here we run the <code>updateStyle()</code> function to make sure the square is styled as defined in its attributes:</p>
+
+<pre class="brush: js notranslate">connectedCallback() {
+ console.log('Custom square element added to page.');
+ updateStyle(this);
+}</pre>
+
+<p>the <code>disconnectedCallback()</code> and <code>adoptedCallback()</code> callbacks log simple messages to the console to inform us when the element is either removed from the DOM, or moved to a different page:</p>
+
+<pre class="brush: js notranslate">disconnectedCallback() {
+ console.log('Custom square element removed from page.');
+}
+
+adoptedCallback() {
+ console.log('Custom square element moved to new page.');
+}</pre>
+
+<p>The <code>attributeChangedCallback()</code> callback is run whenever one of the element's attributes is changed in some way. As you can see from its properties, it is possible to act on attributes individually, looking at their name, and old and new attribute values. In this case however, we are just running the <code>updateStyle()</code> function again to make sure that the square's style is updated as per the new values:</p>
+
+<pre class="brush: js notranslate">attributeChangedCallback(name, oldValue, newValue) {
+ console.log('Custom square element attributes changed.');
+ updateStyle(this);
+}</pre>
+
+<p>Note that to get the <code>attributeChangedCallback()</code> callback to fire when an attribute changes, you have to observe the attributes. This is done by calling the <code>observedAttributes()</code> getter inside the constructor, including inside it a <code>return</code> statement that returns an array containing the names of the attributes you want to observe:</p>
+
+<pre class="brush: js notranslate">static get observedAttributes() {return ['w', 'l']; }</pre>
+
+<p>This is placed right at the top of the constructor, in our example.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Find the <a href="https://github.com/mdn/web-components-examples/blob/master/life-cycle-callbacks/main.js">full JavaScript source</a> here.</p>
+</div>
diff --git a/files/ko/web/web_components/using_shadow_dom/index.html b/files/ko/web/web_components/using_shadow_dom/index.html
new file mode 100644
index 0000000000..7d82820b76
--- /dev/null
+++ b/files/ko/web/web_components/using_shadow_dom/index.html
@@ -0,0 +1,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>