在 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.prototypeextends: '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> 创建了一个标签,这让我想到了下一个问题:

问题 4is 语法的意义何在?这样做的实际区别是什么:

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 不同于 HTMLButtonElementprototype

使用以下 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 自定义元素范式背后有两个不同的概念:

  1. 类型扩展自定义内置元素)如果你想扩展一个标准的HTML元素。
  2. 自定义标签自治自定义元素)如果你想用新名称定义自定义元素。

两者都是用相同的方法定义的registerElementextends/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>