1 | # Custom Elements with Builtin Extends
|
2 |
|
3 | [![Build Status](https://travis-ci.com/ungap/custom-elements-builtin.svg?branch=master)](https://travis-ci.com/ungap/custom-elements-builtin) ![WebReflection status](https://offline.report/status/webreflection.svg)
|
4 |
|
5 | Brings builtin extends to browsers that already have `customElements` (i.e. Safari).
|
6 |
|
7 | See [document-register-element](https://github.com/WebReflection/document-register-element) to polyfill upfront all other legacy browsers too.
|
8 |
|
9 | ```js
|
10 | customElements.define(
|
11 | 'my-button',
|
12 | class MyButton extends HTMLButtonElement {
|
13 | static get observedAttributes() { return ['color']; }
|
14 | attributeChangedCallback(name, oldValue, newValue, nsValue) {
|
15 | this.style.color = newValue;
|
16 | }
|
17 | connectedCallback() {
|
18 | this.addEventListener('click', this);
|
19 | }
|
20 | disconnectedCallback() {
|
21 | this.removeEventListener('click', this);
|
22 | }
|
23 | handleEvent(event) {
|
24 | const next = this.nextElementSibling ||
|
25 | this.parentNode.appendChild(
|
26 | document.createElement('div')
|
27 | );
|
28 | next.textContent = `${event.type} @ ${new Date}`;
|
29 | }
|
30 | },
|
31 | {'extends': 'button'}
|
32 | );
|
33 | ```
|
34 |
|
35 | * CDN via https://unpkg.com/@ungap/custom-elements-builtin
|
36 | * ESM via `import iterator from '@ungap/custom-elements-builtin'`
|
37 | * CJS via `const iterator = require('@ungap/custom-elements-builtin')`
|
38 |
|
39 | [Live test](https://ungap.github.io/custom-elements-builtin/test/)
|
40 |
|
41 | ## Constructor Caveat
|
42 |
|
43 | You cannot use the `constructor` in any meaningful way if you want to ensure API consistency.
|
44 |
|
45 | Create new elements via `document.createElement('button', {is: 'my-button'})` but do not use `new MyButton` or incompatible browsers will throw right away because they made `HTMLButtonElement` and all others not usable as classes.
|
46 |
|
47 | If you need a reliable entry point to setup your custom builtins use the `connectedCallback` method instead of the `constructor` so you're also sure all attributes are eventually already known and you'll have full control.
|
48 |
|
49 | Alternatively, use a `WeakSet` to optionally invoke a setup.
|
50 |
|
51 | ```js
|
52 | const initialized = new WeakSet;
|
53 | const setup = node => {
|
54 | initialized.add(node);
|
55 | node.live = true;
|
56 | };
|
57 | class MyButton extends HTMLButtonElement {
|
58 | connectedCallback() {
|
59 | if (!initialized.has(this))
|
60 | setup(this);
|
61 | // anything else
|
62 | }
|
63 | }
|
64 | ```
|
65 |
|
66 | You can do the same at the beginning of `attributeChangedCallback`.
|
67 |
|
68 | ### Compatible with ...
|
69 |
|
70 | Any engine that supports genuine ES2015 syntax and the following features:
|
71 |
|
72 | * global `MutationObserver`, `customElements`, and `Promise`
|
73 | * `assign`, `create`, `defineProperties`, and `setPrototypeOf` from the `Object`
|