如何传递属性来帮助实现自定义元素?

How to pass attributes to help implement a custom element?

当我们使用 Web 组件技术创建自定义元素时,有时自定义元素的实现涉及使用主文档中生成的元素上存在的属性。如以下示例所示:
主文档:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="import" href="web-components/my-portrait.html">
</head>
<body>
    <my-portrait src="images/pic1.jpg" />
</body>
</html>

我的-portrait.html:

<template id="my-portrait">
    <img src="" alt="portait">
</template>

<script>
    (function() {
        var importDoc = document.currentScript.ownerDocument;
        var proto = Object.create(HTMLElement.prototype, {
            createdCallback: {
                value: function() {
                    var t = importDoc.querySelector("#my-portrait");
                    var clone = document.importNode(t.content, true);
                    var img = clone.querySelector("img");
                    img.src = this.getAttribute("src");
                    this.createShadowRoot().appendChild(clone);
                }
            }
        });
        document.registerElement("my-portrait", {prototype: proto});
    })();
</script>

在 createdCallback 中,我们使用 this.getAttribute("src") 获取肖像元素上定义的 src 属性。
但是,这种获取属性的方式只能在通过元素标签声明实例化元素时使用。但是,如果元素是使用 JavaScript 创建的:document.createElement("my-portrait") 怎么办?当此语句执行完毕时,createdCallback 已被调用,并且 this.getAttribute("src") 将 return null 因为该元素没有 src创建时立即属性。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="import" href="web-components/my-portrait.html">
</head>
<body>
    <!--<my-portrait src="images/pic1.jpg" />-->
    <script>
        var myPortrait = document.createElement("my-portrait");
        myPortrait.src = "images/pic2.jpg"; // too late
        document.getElementsByTagName("body")[0].appendChild(myPortrait);
    </script>
</body>
</html>

那么当我们使用 JavaScript 实例化自定义元素时,我们如何将属性传递给 createdCallback?如果有beforeAttach回调,我们可以在那里设置属性,但是没有这样的回调。

您可以实施名为 attributeChangedCallback:

的生命周期回调

我的-portrait.html:

proto.attributeChangedCallback = function ( name, old, value )
{
  if ( name == "src" )
    this.querySelector( "img" ).src = value
  //...
}
  • name是修改(或添加)属性的名称,
  • old是属性的旧值,或者undefined如果是刚创建的,
  • value 是属性的新值(string 类型)。

要调用回调,请对您的自定义元素使用 setAttribute 方法。

主文档:

<script>
    var myPortrait = document.createElement( "my-portrait" )
    myPortrait.setAttribute( "src", "images/pic2.jpg" ) // OK
    document.body.appendChild( myPortrait )
</script>

例子:

var proto = Object.create(HTMLElement.prototype, {
  createdCallback: {
    value: function() {
      this.innerHTML = document.querySelector("template").innerHTML
      var path = this.getAttribute("src")
      if (path)
        this.load(path)
    }
  },

  attributeChangedCallback: {
    value: function(name, old, value) {
      if (name == "src")
        this.load(value)
    }
  },

  load: {
    value: function(path) {
      this.querySelector("img").src = path
    }
  }
})
document.registerElement("image-test", { prototype: proto })

function add() {
  var el = document.createElement("image-test")
  el.setAttribute("src", "https://placehold.it/100x50")
  document.body.appendChild(el)
}
  <image-test src="https://placehold.it/100x100">
  </image-test>

  <template>
    <img title="portrait" />
  </template>

  <button onclick="add()">
    add
  </button>