이 번역은 완료되지 않았습니다. 이 문서를 번역해 주세요.

One of the key features of the Web Components standard is the ability to create custom elements that encapsulate your functionality on an HTML page, rather than having to make do with a long, nested batch of elements that together provide a custom page feature. This article introduces the use of HTML custom elements.

Note: Custom elements are supported by default in Chrome and Opera. Firefox  is very close; they are currently available if you set the preferences dom.webcomponents.shadowdom.enabled and dom.webcomponents.customelements.enabled to true. Firefox's implementation is planned to be enabled by default in version 60/61. Safari so far supports only autonomous custom elements, and Edge is working on an implementation as well.

High-level view

The controller of custom elements on a web document is the CustomElementRegistry object — this object allows you to register a custom element on the page, return information on what custom elements are registered, etc. 

웹 컴포넌트에서 커스텀 엘리멘트의 컨트롤러는 CustomElementRegistry object 이다. — 이 객체는 커스텀 엘리멘트를 페이지에 등록하도록 허용하고, 등록된 커스텀엘레멘트의 정보 등을 리턴한다.

To register a custom element on the page, you use the CustomElementRegistry.define() method. This takes as its arguments:

커스텀 엘리멘트를 페이지에 등록하기 위해서, CustomElementRegistry.define() method 를 사용하라. 이것은 다름과 같은 인자들을 사용한다: 

  • A DOMString representing the name you are giving to the element. Note that custom element names require a dash to be used in them; they can't be single words.
  • DOMString 은 엘리멘트에게 이름을 주는것을 나타낸다.주의하라 커스텀 엘리멘트는 이름들은 dash('-')가 사용되어야 한다; 이것은 한단어가 될수 없다. (예 : custom-my-elment)
  • A class object that defines the behaviour of the element.
  • 엘리멘트의 행위가 정의된  class 객체.
  • Optionally, an options object containing an extends property, which specifies the built-in element your element inherits from if any.
  • 선택적으로, 옵션 객체는 extends 속성을 포함한다, 만약 내장 엘리멘트로 상커스텀 엘리멘트가 상속받는것을 지정한다면.

So for example, we can define a custom word-count element like this:

좀 더 예를 들자면 , 우리는  커스텀  word-count 엘리멘트 아래와 같이 정의 할수 있다 :

customElements.define('word-count', WordCount, { extends: 'p' });

The element is called word-count, its class object is WordCount, and it extends the <p> element.

이 엘리멘트는  word-count로  호출되게 된다 , 이것의 class object 는 WordCount이다,  그리고  이것은 <p> 엘리멘트를 extends 했다.

A custom element's class object is written using standard ES 2015 class syntax. For example, WordCount is structured like so: 

커스텀 엘리멘트의 class object 는 표준 ES 2015 class 문법으로 작성하고 있다. 예를들면 , WordCount 는 아래와 같은 구조이다:

class WordCount extends HTMLParagraphElement {
  constructor() {
    // 항상 생성자에서 super는 처음으로 호출된다
    super();

    // 엘리멘트의 기능들은 여기에 작성한다

    ...
  }
}

This is just a simple example, but there is more you can do here. It is possible to define specific lifecycle callbacks inside the constructor, which run at specific points in the element's lifecycle. For example, connectedCallback is invoked when the custom element is first connected to the document's DOM, while attributeChangedCallback is invoked when one of the custom element's attributes is added, removed, or changed.

You'll learn more about these in the Using the lifecycle callbacks section below.

There are two types of custom elements:

  • Autonomous custom elements 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 <popup-info>, or document.createElement("popup-info").
  • Customized built-in elements 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 is attribute (or property). For example <p is="word-count">, or document.createElement("p", { is: "word-count" }).

Working through some simple examples

At this point, let's go through some more simple examples to show you how custom elements are created in more detail.

Autonomous custom elements

Let's have a look at an example of an autonomous custom element — <popup-info-box> (see a live example). 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, the JavaScript file defines a class called PopUpInfo, which extends HTMLElement. Autonomous custom elements nearly always extend HTMLElement.

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

    // write element functionality in here

    ...
  }
}

The preceding code snippet contains the constructor definition for the class, which always starts by calling super() so that the correct prototype chain is established.

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.

// 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);

Finally, we register our custom element on the CustomElementRegistry using the define() method we mentioned earlier — in the parameters we specify the element name, and then the class name that defines its functionality:

customElements.define('popup-info', PopUpInfo);

It is now available to use on our page. Over in our HTML, we use it like so:

<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.">

Note: You can see the full JavaScript source code here.

Customized built-in elements

Now let's have a look at another customized built in element example — expanding-list (see it live also). This turns any unordered list into an expanding/collapsing menu.

First of all, we define our element's class, in the same manner as before:

class ExpandingList extends HTMLUListElement {
  constructor() {
    // Always call super first in constructor
    super();

    // write element functionality in here

    ...
  }
}

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 HTMLUListElement interface, and not HTMLElement. So it has all the characteristics of a <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.

Next, we register the element using the define() method as before, except that this time it also includes an options object that details what element our custom element inherits from:

customElements.define('expanding-list', ExpandingList, { extends: "ul" });

Using the built-in element in a web document also looks somewhat different:

<ul is="expanding-list">

  ...

</ul>

You use a <ul> element as normal, but specify the name of the custom element inside the is attribute.

Note: Again, you can see the full JavaScript source code here.

Using the lifecycle callbacks

You can define several different callbacks inside a custom element's constructor, which fire at different points in the element's lifecycle:

  • connectedCallback: Invoked when the custom element is first connected to the document's DOM.
  • disconnectedCallback: Invoked when the custom element is disconnected from the document's DOM.
  • adoptedCallback: Invoked when the custom element is moved to a new document.
  • attributeChangedCallback: Invoked when one of the custom element's attributes is added, removed, or changed.

Let's look at an example of these in use. The code below is taken from the life-cycle-callbacks example (see it running live). This is a trivial example that simply generates a colored square of a fixed size on the page. The custom element looks like this:

<custom-square l="100" c="red"></custom-square>

The class constructor is really simple — here we attach a shadow DOM to the element, then attach empty <div> and <style> elements to the shadow root:

var shadow = this.attachShadow({mode: 'open'});

var div = document.createElement('div');
var style = document.createElement('style');
shadow.appendChild(style);
shadow.appendChild(div);

The key function in this example is updateStyle() — this takes an element, gets its shadow root, finds its <style> element, and adds width, height, and background-color to the style.

function updateStyle(elem) {
  var shadow = elem.shadowRoot;
  var childNodes = shadow.childNodes;
  for(var i = 0; i < 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');
    }
  }
}

The actual updates are all handled by the life cycle callbacks, which are placed inside the constructor. The connectedCallback() runs when the element is added to the DOM — here we run the updateStyle() function to make sure the square is styled as defined in its attributes:

connectedCallback() {
  console.log('Custom square element added to page.');
  updateStyle(this);
}

the disconnectedCallback() and adoptedCallback() 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:

disconnectedCallback() {
  console.log('Custom square element removed from page.');
}

adoptedCallback() {
  console.log('Custom square element moved to new page.');
}

The attributeChangedCallback() 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 updateStyle() function again to make sure that the square's style is updated as per the new values:

attributeChangedCallback(name, oldValue, newValue) {
  console.log('Custom square element attributes changed.');
  updateStyle(this);
}

Note that to get the attributeChangedCallback() callback to fire when an attribute changes, you have to observe the attributes. This is done by calling the observedAttributes() getter inside the constructor, including inside it a return statement that returns an array containing the names of the attributes you want to observe:

static get observedAttributes() {return ['w', 'l']; }

This is placed right at the top of the constructor, in our example.

Note: Find the full JavaScript source here.

문서 태그 및 공헌자

이 페이지의 공헌자: ByeongGi
최종 변경자: ByeongGi,