在 Web 组件中扩展元素时 "is" 语法有什么意义?
What is the point of the "is" syntax when extending elements in web components?
在 Web 组件中,要注册一个元素,您只需键入:
var XFoo = document.registerElement('x-foo', {
prototype: Object.create(HTMLElement.prototype)
});
要创建元素,您可以执行以下操作之一:
<x-foo></x-foo>
var xFoo = new XFoo();
document.body.appendChild(xFoo);
var xFoo = document.createElement( 'x-foo')
document.body.appendChild(xFoo);
一切都很好,很漂亮。当您谈论扩展现有元素时,问题就开始了。
var XFooButton = document.registerElement('x-foo-button', {
prototype: Object.create(HTMLButtonElement.prototype),
extends: 'button'
});
问题1:为什么会重复?在这里,'button'
应该足够了(特别是因为使用 Object.getPrototypeOf(document.createElement(tag));
可以很容易地计算出元素的原型
问题 2:内部如何使用这些信息?例如,如果你有 prototype: Object.create(HTMLFormElement.prototype
和 extends: 'button'
会发生什么(extends
之后的内容与传递的原型不匹配)
要创建一个,您可以执行以下操作之一:
<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);
问题3:既然明明x-foo-button
扩展了button
,为什么我们在使用[=24=时还要指定两者]?我怀疑这是因为 document.createElement()
只是用语法 <button is="x-foo-button"></button>
创建了一个标签,这让我想到了下一个问题:
问题 4:is
语法的意义何在?这样做的实际区别是什么:
var XFooButton = document.registerElement('x-foo-button', {
prototype: Object.create(HTMLButtonElement.prototype),
extends: 'button'
});
还有这个:
var XFooButton = document.registerElement('x-foo-button', {
prototype: Object.create(HTMLButtonElement.prototype),
});
除了 1) 第一种语法需要 <button is="x-foo-button"></button>
在文档中创建一个实例 2) 第二种语法可以用于 任何 元素,而不仅仅是自定义的扩展?
回答 1
明显的重复是因为您的示例非常简单。在真实的虚拟生活中,你会提供一个与registerElement
不同的原型。
点击时显示弹出窗口的自定义按钮示例:
//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
不同于 HTMLButtonElement
的 prototype
。
使用以下 HTML 代码:
<button is="x-foo-button" id="Hello" value="World"> Hello </button>
点击它会在弹出窗口中显示 "Hello World"。
回答 2
extends: 'button'
是一种语义指示,它告诉浏览器所提供的新原型实现了 HTMLButtonElement
接口。这就是为什么从 HTMLButtonElement
继承的对象开始会更容易。相反,您可以从 HTMLFormElement
原型开始,但您必须重新实现 HTMLButtonElement
接口的所有属性和方法。
否则,元素行为将不正确。在上面的示例中,如果您将一行替换为:
var newProto = Object.create( HTMLFormElement.prototype )
...点击它会失败,因为 属性 value
没有在 <form>
元素中实现。
属性id
总是正确的,因为它是由HTMLElement
接口提供的,由每个元素(包括<form>
)实现。
请注意,您可以将缺少的属性添加到 attributeChangedCallback
方法中 link 它们的属性中。
回答 3
你是对的。这保持了与将忽略第二个参数的旧浏览器的向后兼容性,仍然能够创建 normal 元素(在您的示例中为标准 <button>
)。
回答 4
自定义元素范式背后有两个不同的概念:
- 类型扩展(自定义内置元素)如果你想扩展一个标准的HTML元素。
- 自定义标签(自治自定义元素)如果你想用新名称定义自定义元素。
两者都是用相同的方法定义的registerElement
。 extends
/is
选项允许您选择其中之一。
is
语法仅适用于 类型扩展 ,因此始终与 extends
选项相关联。
使用 类型扩展 ,您可以保留所扩展元素的所有语义:CSS 样式、内置行为(界面)、辅助功能。向后兼容性是此语法的另一个好处。
使用 自定义标签 ,您失去了语义,您的自定义元素预计仅实现 HTMLElement
界面,没有内置样式或行为。
Update:下一个示例(针对 Chrome 和 Opera)说明了 Type Extension 和 [=77 之间的区别=]自定义标签.
//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>
在 Web 组件中,要注册一个元素,您只需键入:
var XFoo = document.registerElement('x-foo', {
prototype: Object.create(HTMLElement.prototype)
});
要创建元素,您可以执行以下操作之一:
<x-foo></x-foo>
var xFoo = new XFoo();
document.body.appendChild(xFoo);
var xFoo = document.createElement( 'x-foo')
document.body.appendChild(xFoo);
一切都很好,很漂亮。当您谈论扩展现有元素时,问题就开始了。
var XFooButton = document.registerElement('x-foo-button', {
prototype: Object.create(HTMLButtonElement.prototype),
extends: 'button'
});
问题1:为什么会重复?在这里,'button'
应该足够了(特别是因为使用 Object.getPrototypeOf(document.createElement(tag));
问题 2:内部如何使用这些信息?例如,如果你有 prototype: Object.create(HTMLFormElement.prototype
和 extends: 'button'
会发生什么(extends
之后的内容与传递的原型不匹配)
要创建一个,您可以执行以下操作之一:
<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);
问题3:既然明明x-foo-button
扩展了button
,为什么我们在使用[=24=时还要指定两者]?我怀疑这是因为 document.createElement()
只是用语法 <button is="x-foo-button"></button>
创建了一个标签,这让我想到了下一个问题:
问题 4:is
语法的意义何在?这样做的实际区别是什么:
var XFooButton = document.registerElement('x-foo-button', {
prototype: Object.create(HTMLButtonElement.prototype),
extends: 'button'
});
还有这个:
var XFooButton = document.registerElement('x-foo-button', {
prototype: Object.create(HTMLButtonElement.prototype),
});
除了 1) 第一种语法需要 <button is="x-foo-button"></button>
在文档中创建一个实例 2) 第二种语法可以用于 任何 元素,而不仅仅是自定义的扩展?
回答 1
明显的重复是因为您的示例非常简单。在真实的虚拟生活中,你会提供一个与registerElement
不同的原型。
点击时显示弹出窗口的自定义按钮示例:
//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
不同于 HTMLButtonElement
的 prototype
。
使用以下 HTML 代码:
<button is="x-foo-button" id="Hello" value="World"> Hello </button>
点击它会在弹出窗口中显示 "Hello World"。
回答 2
extends: 'button'
是一种语义指示,它告诉浏览器所提供的新原型实现了 HTMLButtonElement
接口。这就是为什么从 HTMLButtonElement
继承的对象开始会更容易。相反,您可以从 HTMLFormElement
原型开始,但您必须重新实现 HTMLButtonElement
接口的所有属性和方法。
否则,元素行为将不正确。在上面的示例中,如果您将一行替换为:
var newProto = Object.create( HTMLFormElement.prototype )
...点击它会失败,因为 属性 value
没有在 <form>
元素中实现。
属性id
总是正确的,因为它是由HTMLElement
接口提供的,由每个元素(包括<form>
)实现。
请注意,您可以将缺少的属性添加到 attributeChangedCallback
方法中 link 它们的属性中。
回答 3
你是对的。这保持了与将忽略第二个参数的旧浏览器的向后兼容性,仍然能够创建 normal 元素(在您的示例中为标准 <button>
)。
回答 4 自定义元素范式背后有两个不同的概念:
- 类型扩展(自定义内置元素)如果你想扩展一个标准的HTML元素。
- 自定义标签(自治自定义元素)如果你想用新名称定义自定义元素。
两者都是用相同的方法定义的registerElement
。 extends
/is
选项允许您选择其中之一。
is
语法仅适用于 类型扩展 ,因此始终与 extends
选项相关联。
使用 类型扩展 ,您可以保留所扩展元素的所有语义:CSS 样式、内置行为(界面)、辅助功能。向后兼容性是此语法的另一个好处。
使用 自定义标签 ,您失去了语义,您的自定义元素预计仅实现 HTMLElement
界面,没有内置样式或行为。
Update:下一个示例(针对 Chrome 和 Opera)说明了 Type Extension 和 [=77 之间的区别=]自定义标签.
//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>