Skip to content

Instantly share code, notes, and snippets.

@nfeldman
Last active April 27, 2016 04:58
Show Gist options
  • Save nfeldman/b619b9f4edec4964f1b0d28c932542e7 to your computer and use it in GitHub Desktop.
Save nfeldman/b619b9f4edec4964f1b0d28c932542e7 to your computer and use it in GitHub Desktop.
notes on custom elements

There are two types of custom elements.

  1. custom tag
  2. type extension

Tag names must have a hyphen and must start with [a-z]. Plus some other rules.

Tag names must not be any one of:

  • annotation-xml
  • color-profile
  • font-face
  • font-face-src
  • font-face-uri
  • font-face-format
  • font-face-name
  • missing-glyph

Tags are added to a global registry via document.registerElement(name, options)

Custom elements have 4 optional lifecycle methods you may choose to implement.

  1. createdCallback
  2. attachedCallback
  3. detachedCallback
  4. attributeChangedCallback

Custom Tag

In this approach, you simply come up with a name, e.g. 'ex-ample' that conforms to the naming requirements. If registered as-is, you'll get a custom element that extends HTMLElement and isn't especially useful. One of the options available is to provide your own prototype object for the custom element. The prototype should extend HTMLElement.prototype.

var ExAmpleProto = Object.create(HTMLElement.prototype);

ExAmpleProto.createdCallback = function () {
};

ExAmpleProto.attachedCallback = function () {
};

ExAmpleProto.detachedCallback = function () {
};

ExAmpleProto.attributeChangedCallback = function (attrName, oldVal, newVal) {
};

// add other prototype methods, as needed

Register the new element.

var ExAmple = document.registerElement('ex-ample', {
    prototype: ExAmpleProto
});

Use the new element in HTML:

<ex-ample>This is an example.</ex-ample>

Or create the new element programatically:

// registerElement returned a constructor function
var example1 = new ExAmple();
document.body.appendChild(example1);
// or using createElement
var example2 = document.createElement('ex-ample');
document.body.appendChild(example2);

Type Extension

Some native elements have a lot of complex behavior associated with them, much of it invisible to the typical user. For example, inputs and buttons have accessibility states, are in the tab ordering, and are removed from the tab ordering when disabled. Rather than recreating all of this behavior with a mixture of JavaScript and aria attributes, you can extend an existing element.

Extending an element is almost identical to the example above. The differences are:

  1. Rather than using HTMLElement.prototype, use the prototype of the element you are extending as your element's prototype's prototype. This is still optional.
  2. use the 'extends' option
  3. Rather than using the new tag name in markup, set it as the value of an "is" attribute on the element you're extending.

Putting it all together gets us:

document.registerElement('ex-input', {
    prototype: Object.create(HTMLInputElement.prototype, {
        createdCallback: {
            value: function() {
                console.log(this.outerHTML);
            }
        }
    }),
	extends: 'input'
});

Which we use:

var x1 = document.createElement('ex-input');
// createdCallback executes only when inserted into the dom
document.body.appendChild(x1);
// console will show <ex-input></ex-input>

or

var x2 = document.createElement('input');
x2.setAttribute('is', 'ex-input');

// createdCallback executes only when inserted into the dom
document.body.appendChild(x2);
// console will show <input is="ex-input">

When used in HTML, the element must be written:

<input is="ex-input">
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment