3

我想创建一个 Web 组件,其中包含可以添加到的元素列表。例如,如果我有一个初始模板,例如:

const template = document.createElement("template");
template.innerHTML = `<input type="text"></input><button>add div</button>`;

class MyElement extends HTMLElement {
  constructor() {
    super();
    this._shadowRoot = this.attachShadow({ mode: "open" });
    this._shadowRoot.appendChild(template.content.cloneNode(true));
    const button = this._shadowRoot.querySelector("button");
    button.addEventListener("click", this.addDiv);
  }
  addDiv(e) {
    // ...
  }
}
customElements.define("my-element", MyElement);

并且每次单击该按钮时,<div>都会添加一个包含输入字段中的文本的 a,从而创建如下内容:

<input type="text"></input><button>add div</button>
<div>first text from input added</div>
<div>second text from input added</div>
...
4

3 回答 3

2

在您的情况下,您不能insertAjacentHTML()在 Shadow DOMshadowRoot属性上使用,因为 Shadow Root 没有实现 Element 接口。

使用绑定(这个)

更好的解决方案是在属性上使用appendChild() 。shadowRoot但是,您需要在事件回调上添加一个特殊bind()操作。click

在事件回调中,这确实引用了触发事件的元素,而不是定义回调的对象。

为了获得对自定义元素的引用(为了访问 Shadow DOM 中的输入元素shadowRoot,请调用bind(this)inside addEventListener()

button.addEventListener( "click", this.addDiv.bind( this ) )

请参阅下面的完整示例:

const template = document.createElement("template");
template.innerHTML = `<input type="text"></input><button>add div</button>`;

class MyElement extends HTMLElement {
  constructor() {
    super();
    this._shadowRoot = this.attachShadow({ mode: "open" });
    this._shadowRoot.appendChild(template.content.cloneNode(true));
    const button = this._shadowRoot.querySelector("button");
    button.addEventListener("click", this.addDiv.bind( this ) );
  }
  addDiv(e) {
    var div = document.createElement( 'div' )
    div.textContent = this.shadowRoot.querySelector( 'input' ).value
    this.shadowRoot.appendChild( div )
  }
}
customElements.define("my-element", MyElement);
<my-element></my-element>


使用箭头功能

另一种解决方案应该是使用箭头函数。使用箭头函数, this 不会被重新定义,所以你不需要使用bind().

class MyElement extends HTMLElement {
  constructor() {
    super()
    const sh = this.attachShadow( { mode: "open" } )
    sh.innerHTML = `<input type="text"></input><button>add div</button>`
    const button = sh.querySelector( "button" )
    button.onclick = ev => {
      let div = document.createElement( "div" )
      div.textContent = sh.querySelector( "input" ).value
      sh.appendChild( div )
    }
  }
}
customElements.define( "my-element", MyElement )
<my-element></my-element>

于 2019-12-16T10:39:50.943 回答
2

扩展 Supersharps 答案:

  • attachShadow() this.shadowRoot默认设置
  • attachShadow() 返回shadowRoot,所以你可以链接.innerHTML
  • appendChild()返回附加的 DIV,因此您可以将其链接起来

  class MyElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: "open"})
        .innerHTML = '<input type="text"></input><button>add div</button>';
    this.shadowRoot.querySelector('button').onclick = evt =>
      this.shadowRoot
          .appendChild(document.createElement("div"))
          .innerHTML = this.shadowRoot.querySelector("input").value
  }
}
customElements.define("my-element", MyElement)
<my-element></my-element>

或者将其重写为一个帮助函数 $append
,这使得其余代码可读

class MyElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: "open"});

    let $append = ( tag, html = '' ) => (
          tag = this.shadowRoot.appendChild(document.createElement(tag)),
          tag.innerHTML = html,
          tag // return tag, so onclick can be chained
        );

    let input = $append('input');
    $append('button', 'add div').onclick = evt => $append("div", input.value);
  }
}
customElements.define("my-element", MyElement)
<my-element></my-element>

于 2019-12-16T12:11:35.330 回答
1

感谢 Danny 的回答,我意识到我不需要 create this._shadowRoot,所以我的问题和 Supersharp 的回答可以简化为以下内容。我保留了模板,因为从模板创建 Web 组件是一种很好的做法,因为性能比使用shadowRoot.innerHTML.

const template = document.createElement("template");
template.innerHTML = `<input type="text"></input><button>add div</button>`;

class MyElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
    const button = this.shadowRoot.querySelector("button");
    button.addEventListener("click", this.addDiv.bind(this));
  }
  addDiv(e) {
    const div = document.createElement("div");
    div.textContent = this.shadowRoot.querySelector("input").value;
    this.shadowRoot.appendChild(div);
  }
}
customElements.define("my-element", MyElement);
于 2019-12-16T22:01:15.643 回答