在 Web 组件中扩展元素时,“is”语法的意义何在?

What is the point of the "is" syntax when extending elements in web components?

提问人:Merc 提问时间:12/3/2015 最后编辑:SupersharpMerc 更新时间:11/3/2016 访问量:856

问:

在 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这些信息如何在内部使用?例如,如果您拥有 and(之后的内容与传递的原型不匹配),会发生什么情况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-buttonbuttondocument.createElement()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) 第一种语法需要在文档中创建一个实例 2) 第二种语法可以用于任何元素,而不仅仅是自定义元素的扩展?<button is="x-foo-button"></button>

JavaScript Web 组件 自定义元素

评论

0赞 Danny '365CSI' Engelman 4/25/2019
4年后...举个例子:除了标准的自治自定义元素:我还添加了 52 个扩展 IMG: 的元素:在 github.com/card-ts/playingcardts<card-t cid='queen-of-cards'></card-t><img is=queen-of-hearts>

答:

15赞 Supersharp 12/8/2015 #1

答案 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'HTMLButtonElementHTMLButtonElementHTMLFormElementHTMLButtonElement

否则,元素行为将不正确。在上面的示例中,如果将一行替换为:

var newProto = Object.create( HTMLFormElement.prototype )

...单击它将失败,因为该属性未在元素中实现。value<form>

该属性始终是正确的,因为它由接口提供,由每个元素(包括 )实现。idHTMLElement<form>

请注意,您可以添加缺少的属性,并将它们链接到方法中的属性。attributeChangedCallback


答案 3你是对的。这保持了与旧浏览器的向后兼容性,这些浏览器将忽略第二个参数,仍然能够创建一个普通元素(示例中的标准)。<button>


答案 4自定义元素范式背后有 2 个不同的概念:

  1. 如果要扩展标准 HTML 元素,请键入 ExtensionsCustomized Built-in Elements)。
  2. 自定义标签(自治自定义元素),如果要定义具有新名称的自定义元素

两者都使用相同的方法定义。/ 选项允许您选择其中之一。registerElementextendsis

该语法仅适用于类型扩展,因此始终与选项相关联。isextends

使用类型扩展,可以保留扩展元素的所有语义:CSS 样式、内置行为(接口)、辅助功能。向后兼容性是此语法的另一个好处。

使用自定义标记时,您将失去语义,并且您的自定义元素应仅实现接口,而没有内置样式或行为。HTMLElement

更新:下一个示例(适用于 Chrome 和 Opera)说明了类型扩展自定义标记之间的区别。

//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>

评论

0赞 Merc 12/9/2015
很好的答案。答案 4 中的两点:“使用类型扩展,您可以保留扩展元素的所有语义:CSS 样式、内置行为(接口)、辅助功能”。你不是通过打字就能得到所有这些吗?如果您使用正确的原型制作此处指定的自定义标签,难道您不能保留没有内置样式或行为的语义吗?var XFooButton = document.registerElement('x-foo-button', { prototype: Object.create(HTMLButtonElement.prototype), });
0赞 Supersharp 12/9/2015
不,你不保留语义。这意味着您的浏览器会忽略您的自定义标记是从按钮派生的。盲人阅读器不会将其检测为按钮。(当然,所有方法和属性继承的表单原型都可以工作,尽管没有与按钮的特定属性相关联:,等)HTMLButtonElementvaluename
0赞 Merc 12/9/2015
天哪,我不知道!所以。。。浏览器以特定的方式处理特定元素,应用 CSS 和特定行为?这是如何工作的?<button>
0赞 Supersharp 12/9/2015
我想它基于元素名称(或接口)。这就是为什么它适用于保留原始元素名称的类型扩展。在我的代码片段中,如果您使用 Dev Tools 检查第一个按钮,您会看到 Chrome 在其上应用了一个名为 .user agent stylesheet
0赞 Merc 12/10/2015
谢谢。我认为这个问题 - 尤其是你的回答 - 绝对很棒。这是我开始的最好的赏金!