LifeCycle Callbacks in Custom Elements

I was going through the HTML5 Google Community posts a few days ago and saw Max Waterman had asked a question about  Custom Elements. I thought I could easily answer this one, so I did. Interestingly enough I learned a bit more as I answered the question. When I was going through code in the console to test I noticed a few things I hadn’t before.

// namespace for custom elements
var customElements = {};

// create a JavaScript object based on HTMLElement
var registeredElementProto = Object.create(HTMLElement);

//add a function to registeredElementProto
registeredElementProto.foo = function(){ return 'foo'; };

//add a property to registeredElementProto
registeredElementProto.bar  = 'bar';

//add a createdCallback
registeredElementProto.createdCallback = function(newBar){
this.bar = (newBar || this.bar);
console.log(this.foo() + this.bar);
}

// add our element to the DOM registry
customElements.fooBar = document.registerElement('foo-bar', { prototype: registeredElementProto });

Alright, this looks good so now I will add a instance of my element to the DOM using it’s constructor. Should be simple enough, right?

var myConstructor = new customElements.fooBar("Baz");
constructor error from custom elements

constructor error from custom elements

I spin the wheel and I can hear the sound of Pat Sayjak laughing at me as the spinner lands on a ‘Bankrupt’ space. What just happened here? Let’s take a look at the Lifecycle Callbacks a DOM Element. These are functions that are fired internally by native code but can optionally be redefined.

HTML Rocks description of Custom Elements

HTML Rocks description of Custom Elements

One thing that was unclear to me before was whether or not you can override these callbacks to take parameters from the element constructor.  Keeping in mind that “createdCallback” is not the actual element constructor I would guess that it is most likely just a simple callback fired by the containing element constructor. I would hope something similar to this might be the case.

//...
constructor (arg1, arg2,...){
// main constructor logic
this.createdCallback.apply(this, arguments);
}
//...

If I update the first code example so that I inspect the arguments of a redefined ‘createdCallback’ I see that no arguments exist. Notice I am adding an argument reference as well in ‘fooBarOne’ and no arguments in ‘fooBarTwo’.

// ...

//add a createdCallback
registeredElementProto.createdCallback = function(newBar){
console.log("args",  arguments, newBar);
}
// ...

var fooBarOne = customElements.fooBar('myArg');
//-> outputs TypeError: This constructor should be called without arguments.

var fooBarTwo = customElements.fooBar();
//-> outputs to console "args [] undefined"

I can see now that ‘fooBarOne’ outputs a type error “TypeError: This constructor should be called without arguments.” and as I would expect in ‘fooBarTwo’ the output in the console is an empty arguments object and the ‘newBar’ argument is ‘undefined’ even though I tried to override in the callback signature. Based on this, I would bet that when we define custom elements, the native code constructor will not recognize any arguments passed in and therefore even if redefining my callbacks with parameters and my assumption that callbacks would apply constructor arguments, the constructor never allows arguments to pass into the HTML Element anyway so nothing like this could work.

I guess it makes sense though since custom elements can also be constructed using ‘document.createElement’ which takes only only parameter which is the name of the element which happens automatically when calling “new ElementName();”. I’m a bit naive to browser development but I would guess browser developers would have to either redefine ‘document.createElement‘ and HTML Element constructors AND make it all backwards compatible, or they would have to further clutter DOM manipulation with a new function ‘document.createCustomElement’ & change the internals in the JavaScript ‘new’ constructor for HTML Elements as well.

Advertisements