自定义元素不拾取属性

Custom element not picking up attributes

我正在尝试查看自定义元素及其工作原理,虽然 MDN 上的示例工作正常,但我似乎无法自己复制它们。

MDN 文章是 here

This 是来自 MDN 的工作示例。

我的问题是我似乎永远无法将属性传递到我的组件中,它们总是以 null 形式出现,而不是传递参数的值。

我的 JS 是 (test.js)

  class PopUpInfo extends HTMLElement {
    constructor() {
      // Always call super first in constructor
      super();
  
      // Create a shadow root
      const shadow = this.attachShadow({mode: 'open'});
  
      // Create spans
      const wrapper = document.createElement('span');
      const info = document.createElement('span');
 
      // Take attribute content and put it inside the info span
      const text = this.getAttribute('foo'); // <-- this always returns null
      info.textContent = `(${text})`;

      shadow.appendChild(wrapper);
      wrapper.appendChild(info);
    }
  }
  
  // Define the new element
  customElements.define('popup-info', PopUpInfo);

还有我的Html:

<html>
    <head>
        <script src="test.js"></script>
    </head>
    <body>

        <hr>

        <popup-info foo="Hello World"></popup-info> 

        <hr>

    </body>
</html>

我希望在屏幕上看到的是文字

(Hello World)

但我所看到的只是

(null)

当我调试时,我可以看到 this.attributes 的长度为 0,因此它没有被传入。

有人在创建自定义元素时见过这个吗?

您在 HTML 中的脚本导入中缺少延迟属性,并且它没有正确加载,这就是问题所在。 defer属性允许脚本在页面解析后执行

class PopUpInfo extends HTMLElement {
  constructor() {
    // Always call super first in constructor
    super()

    // Create a shadow root
    const shadow = this.attachShadow({ mode: 'open' })

    // Create spans
    const wrapper = document.createElement('span')
    const info = document.createElement('span')

    // Take attribute content and put it inside the info span
    const text = this.getAttribute('foo') // <-- this always returns null

    info.textContent = `(${text})`

    shadow.appendChild(wrapper)
    wrapper.appendChild(info)
  }
}

// Define the new element
customElements.define('popup-info', PopUpInfo)
<html>
  <head>
    <script src="app.js" defer></script>
  </head>
  <body>
    <hr />

    <popup-info foo="Hello World"></popup-info>

    <hr />
  </body>
</html>

尽管当我在此处尝试 运行 片段时您的示例似乎 运行 不错,但我仍然想提出改进建议。

使用 observedAttributes 静态 getter 定义组件应关注的属性列表。当属性值已更改且属性名称在列表中时,将调用 attributeChangedCallback 回调。在那里你可以断言每当你的属性值被改变时要做什么的逻辑。

在这种情况下,您可以构建您想要的字符串。这也有副作用,每当再次更改属性值时,字符串将被更新。

class PopUpInfo extends HTMLElement {

  /**
   * Observe the foo attribute for changes.
   */
  static get observedAttributes() {
    return ['foo'];
  }

  constructor() {
    super();

    const shadow = this.attachShadow({
      mode: 'open'
    });

    const wrapper = document.createElement('span');
    const info = document.createElement('span');
    wrapper.classList.add('wrapper');

    wrapper.appendChild(info);
    shadow.appendChild(wrapper);
  }
  
  /**
   * Returns the wrapper element from the shadowRoot.
   */
  get wrapper() {
    return this.shadowRoot.querySelector('.wrapper')
  }
  
  /**
   * Is called when observed attributes have a changed value.
   */
  attributeChangedCallback(attrName, oldValue, newValue) {
    switch(attrName) {
      case 'foo':
        this.wrapper.textContent = `(${newValue})`;
        break;
    }
  }
  
}

// Define the new element
customElements.define('popup-info', PopUpInfo);
<html>

<head>
  <script src="test.js"></script>
</head>

<body>

  <hr>

  <popup-info foo="Hello World"></popup-info>

  <hr>

</body>

</html>

让埃米尔保持他的正确答案。

只是为了表明可能有替代的和更短的符号:

customElements.define('popup-info', class extends HTMLElement {
  static get observedAttributes() {
    return ['foo'];
  }

  constructor() {
    const wrapper = document.createElement('span');
    super().attachShadow({mode:'open'})// both SETS and RETURNS this.shadowRoot
           .append(wrapper);
    this.wrapper = wrapper;
  }
  
  attributeChangedCallback(name, oldValue, newValue) {
    switch(name) {
      case 'foo':
        this.wrapper.textContent = `(${newValue})`;
        break;
    }
  }
  
});
<popup-info 
  foo="Hello World"
  onclick="this.setAttribute('foo','Another world')"
  >
</popup-info>