webpack Web组件“无法设置null的”innerHTML“属性”

w6lpcovy  于 2023-10-19  发布在  Webpack
关注(0)|答案(2)|浏览(108)

我创建了一个非常基本的自定义元素,它可以根据提供的属性person更改其值。但是每当我加载我的自定义元素时,我都会得到这个错误:Cannot set property 'innerHTML' of null。当我向attributeChangedCallback函数添加断点时,我确实可以看到加载时元素不在那里。当我继续加载时,元素会完美地加载。
我可以想象,因为我使用webpack来捆绑我所有的文件,问题来自于在主体的末尾加载元素,而不是在我的头部加载元素。

my-element.js:

class MyElement extends HTMLElement {
  constructor() {
     super();

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

  get person() {
     return this._name;
  }

  set person(val) {
     this.setAttribute('person', val);
  }

  static get observedAttributes() {
     return ['person'];
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
     let myElementInner = this.shadow.querySelector('.my-element-inner');

     switch (attrName) {
        case 'person':
           this._person = newVal;

           // ======================
           // The error occures here
           // ======================
           myElementInner.innerHTML = `My name is ${this._person}`;

     }
  }

  connectedCallback() {
     var template =
     `
        <style>
        .my-element-inner {
           outline: blue dashed 1px;
           background-color: rgba(0,0,255,.1);
        }
        </style>
        <span class="my-element-inner">My name is ${this._person}</span>
     `

     this.shadow.innerHTML = template;
  }
}
customElements.define('my-element', MyElement);

index.html:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>WebPack Test Page</title>
</head>
<body>

  <my-element person="André"></my-element>

  <!-- Here goes the bundle.js -->
</body>
</html>
ncgqoxb0

ncgqoxb01#

attributeChangedCallback()可以在connectedCallback之前或之后调用,具体取决于如何使用自定义元素。
如果您将connectedCallback逻辑移到构造函数中,那么一切都会很好
另一种选择是检查myElementInner是否是null,并将代码保留在connectedCallback中。

class MyElement extends HTMLElement {
  constructor() {
    super();

    this.shadow = this.attachShadow({mode: 'open'});
    this._person = '';
    var template =
      `
        <style>
        .my-element-inner {
           outline: blue dashed 1px;
           background-color: rgba(0,0,255,.1);
        }
        </style>
        <span class="my-element-inner">My name is ${this._person}</span>
     `

    this.shadow.innerHTML = template;
  }

  get person() {
    return this._person;
  }

  set person(val) {
    this.setAttribute('person', val);
  }

  static get observedAttributes() {
    return ['person'];
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    let myElementInner = this.shadow.querySelector('.my-element-inner');

    switch (attrName) {
      case 'person':
        this._person = newVal;
        if (myElementInner) {
          myElementInner.innerHTML = `My name is ${this._person}`;
        }

    }
  }
}
customElements.define('my-element', MyElement);
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>WebPack Test Page</title>
</head>

<body>

  <my-element person="André"></my-element>

  <!-- Here goes the bundle.js -->
</body>

</html>
ttygqcqt

ttygqcqt2#

由于规范鼓励将尽可能多的设置推到connectedCallback上,这对一些开发人员来说可能更好。
请注意,如果元素上存在任何(观察到的)属性,则在解析原始HTML时仍然可以调用attributeChangedCallback。对于这种情况,我使用私有#mounted属性。此属性对于防止connectedCallback中的逻辑块冗余运行非常有用(请参阅上面的规范链接),但它也可以用于防止attributeChangedCallback过早运行。

class MyElement extends HTMLElement {
  #mounted = false;
  #person = '';

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

  get person() {
     return this.#person;
  }

  set person(val) {
     this.setAttribute('person', val);
  }

  static get observedAttributes() {
     return ['person'];
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
     if (!this.#mounted) return;
     let myElementInner = this.shadow.querySelector('.my-element-inner');

     switch (attrName) {
        case 'person':
           this.#person = newVal;
           myElementInner.innerHTML = `My name is ${this.#person}`;
     }
  }

  connectedCallback() {
     const template = `
        <style>
        .my-element-inner {
           outline: blue dashed 1px;
           background-color: rgba(0,0,255,.1);
        }
        </style>
        <span class="my-element-inner">My name is ${this.#person}</span>
     `

     this.shadow.innerHTML = template;
     this.#mounted = true;
  }
}

customElements.define('my-element', MyElement);

相关问题