In web components, to register an element you simply type:
var XFoo = document.registerElement('x-foo', {
prototype: Object.create(HTMLElement.prototype)
});
To create an element you can do one of these:
<x-foo></x-foo>
var xFoo = new XFoo();
document.body.appendChild(xFoo);
var xFoo = document.createElement( 'x-foo')
document.body.appendChild(xFoo);
This is all fine and dandy. The issues start when you are talking about extending existing elements.
var XFooButton = document.registerElement('x-foo-button', {
prototype: Object.create(HTMLButtonElement.prototype),
extends: 'button'
});
Question 1: Why the duplication? Here, 'button' should suffice (especially since it's easy enough to work out the element's prototype with Object.getPrototypeOf(document.createElement(tag));
Question 2: How is that information used internally? What happens if you for example have prototype: Object.create(HTMLFormElement.prototype and extends: 'button' (where what's after extends doesn't match the prototype passed)
To create one you can do one of these:
<button is="x-foo-button"></button>
var xFooButton = new XFooButton();
document.body.appendChild(xFoo);
var xFooButton = document.createElement('button', 'x-foo-button');
document.body.appendChild(xFooButton);
Question 3: since it's clear that x-foo-button extends button, why do we have to specify both of them when we use document.createElement()? I suspect that's because document.createElement() simply creates a tag with syntax <button is="x-foo-button"></button>, which brings me to the next question:
Question 4: What's the point of the is syntax? What is the actual difference between doing this:
var XFooButton = document.registerElement('x-foo-button', {
prototype: Object.create(HTMLButtonElement.prototype),
extends: 'button'
});
And this:
var XFooButton = document.registerElement('x-foo-button', {
prototype: Object.create(HTMLButtonElement.prototype),
});
Other than 1) The first syntax will need <button is="x-foo-button"></button> to create an instance in the document 2) The second syntax can be used on any element, not just an extension of the custom ones?
React and Web Components are built to solve different problems. Web Components provide strong encapsulation for reusable components, while React provides a declarative library that keeps the DOM in sync with your data. The two goals are complementary.
Web Components consist of three separate technologies that are used together: Custom Elements. Quite simply, these are fully-valid HTML elements with custom templates, behaviors and tag names (e.g. <one-dialog> ) made with a set of JavaScript APIs. Custom Elements are defined in the HTML Living Standard specification.
Custom elements allow web developers to define new HTML tags, extend existing ones, and create reusable web components.
Answer 1
The apparent duplication is because your example is very simple. In the real virtual life, you would provide a different prototype to registerElement.
Example with a custom button that will display a popup when clicked:
//Custom method
function callback ()
{
console.log( this + " {created}" )
this.onclick = function ( event )
{
alert( this.id + " " + this.value )
}
}
//Type Extension
var newProto = Object.create( HTMLButtonElement.prototype )
newProto.createdCallback = callback
var XFooButtonExt = document.registerElement( 'x-foo-button', {
prototype: newProto,
extends: 'button'
} )
newProto is different than HTMLButtonElement's prototype.
With the following HTML code:
<button is="x-foo-button" id="Hello" value="World"> Hello </button>
A click on it will display "Hello World" in a popup.
Answer 2
The extends: 'button' is a semantic indication that tells the browser that the new prototype provided implements the HTMLButtonElement interface. That's why it's easier to start with an object that inherits from HTMLButtonElement. Instead you could start with an HTMLFormElement prototype but you would have to reimplement all the properties and methods of the HTMLButtonElement interface.
If not, the element behaviour will be incorrect. In the above example, if you replace a line by:
var newProto = Object.create( HTMLFormElement.prototype )
... the click on it will fail because the property value is not implemented in a <form> element.
The property id is always correct because it is provided by the HTMLElement interface, implemented by every elements (including <form>).
Note that you could add the missing properties, and link them to their attribute in the attributeChangedCallback method.
Answer 3
You are correct. This maintains backward compatibility with old browsers that will ignore the second argument, still being able to create a normal element (a standard <button> in your example).
Answer 4 There are 2 different concepts behind the Custom Elements paradigm:
Both are defined with the same method registerElement. The extends/is option permits you to choose one of them.
The is syntax works only with Type Extensions and is therefore always associated with the extends option.
With Type Extensions, you keep all the semantics of the element you extend: CSS styles, built-in behaviour (interfaces), accessibility features. Backward compatibility is another benefit of this syntax.
With Custom Tags, you loose the semantics and your custom element is expected to implement only the HTMLElement interface, with no built-in style or behavior.
Update: the next example (for Chrome and Opera) illustrates the difference between Type Extension and Custom Tag.
//Method
function callback() {
this.textContent = this //Get the HTML semantics
this.onclick = function(event) {
try {
var output = this.id + " "
output += this.name //works only with <button is=...>
}
catch (e) {
output += "a generic element"
}
alert(output)
}
}
//Type Extension
var newProto = Object.create(HTMLButtonElement.prototype)
newProto.createdCallback = callback
var XFooButtonExt = document.registerElement('x-foo-button', {
prototype: newProto,
extends: 'button'
})
//Custom Tag
var newProto2 = Object.create(HTMLButtonElement.prototype)
newProto2.createdCallback = callback
var XFooButtonCust = document.registerElement('x-foo-button-2', {
prototype: newProto2,
})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Custom Elements</title>
</head>
<body>
<h3>Type Extension</h3>
<button is="x-foo-button" id="I'm" name="a button">Type Extension</button>
<h3>Custom Tag</h3>
<x-foo-button-2 id="I'm" name="a button">Custom Tag</x-foo-button-2>
</body>
</html>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With