Skip to content

Instantly share code, notes, and snippets.

@sjmiles
Last active August 29, 2015 13:56
Show Gist options
  • Save sjmiles/9282062 to your computer and use it in GitHub Desktop.
Save sjmiles/9282062 to your computer and use it in GitHub Desktop.
One History of `document.registerElement`

As I understand it, the original Custom Elements concept had a crystalline form: attach a class to a tag-name.

Using the yet-to-be ES6 syntax, it would look something like this:

  class MyElement extends HTMLElement {
    constructor() {
    }
    doStuff() {
    }
  }
  document.registerElement('my-element', MyElement);

Unfortunately, we as yet have neither ES6 nor the ability to extend HTMLElement, so the design got a little bit tricky.

ES5 classes are only a little different than above, so one should be able to do:

  MyElement = function() {};
  MyElement.prototype = Object.create(HTMLElement.prototype);
  MyElement.prototype.doStuff = function() {};
  document.registerElement('my-element', MyElement);

However, we cannot actually construct a MyElement defined this way, which is to say, you cannot produce an element via:

  var broken = new MyElement();

for the same reason that new HTMLElement() throws: we are bumping into the C++ edge of Blink (which is essentially a bug, and common to other implementations, apparently).

Today this problem is avoided by having registerElement return a blessed function which can actually instantiate an element instance. That brings us here:

  MyElement = function() {
    // code here never executes
  }
  MyElement.prototype = Object.create(HTMLElement.prototype);
  MyElement.prototype.createdCallback = function() {}; // like a constructor
  MyElement = document.registerElement('my-element', MyElement);

Someday, the constructor 'bug' will be fixed, and we can just use the author's version of MyElement, but today, the author's MyElement is replaced at the end, and the original MyElement never executes.

Because of potential confusion about // code here never executes, the spec writers defined registerElement to take as an argument (paraphrased) a dictionary containing a property named prototype. This is why you see this official construction:

  MyElementPrototype = Object.create(HTMLElement.prototype);
  MyElementPrototype.createdCallback = function() {}; // like a constructor
  MyElement = document.registerElement('my-element', {
    prototype: MyElementPrototype
  });

Some of us who know all this history sometimes like to use an alternate construction, namely:

  MyElement = function() {};
  MyElement.prototype = Object.create(HTMLElement.prototype);
  MyElement.prototype.createdCallback = MyElement; // constructor switcheroo
  MyElement = document.registerElement('my-element', MyElement);

Generally this matches the original concept more closely and can feel more elegant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment