ShadowDOM v1 - 在 lightdom 中更改了输入文本

ShadowDOM v1 - Input text changed in lightdom

我正在尝试监听自定义组件中的文本更改

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
    <script type="text/javascript" src="component.js"></script>
  </head>
  <body>
    <fancy-p>
      <p>Bar</p>
      <!-- the input should be listened to -->
      <input id="foo" type="text" name="text" placeholder="Hello"></input>
    </fancy-p>
  </body>
</html>

component.js

customElements.define('fancy-p', class extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
  }
  get foo() {
    return "foo"
  }
  connectedCallback() {
    this.shadowRoot.innerHTML = `
      <style>
      p {
        color: red;
      }
      :host {
        background-color: blue;
      }
      </style>
      <div data-foo="bar">
        <slot></slot>
      </div>
    `;

    let input = this.querySelector("input");
    console.log("input is:" + input);
  }
});

我尝试监听文本更改并在 connectedCallback 中使用 querySelector 但在 Chrome 70.xx 中选择器 returns null .

似乎当我设置断点时 lightDOM 未填充(即插槽),但我不知道如何向输入添加事件侦听器。

我该怎么做???

在您的示例中,<input> 被放置在 <slot> 中,这意味着自定义元素之外的代码拥有 <input> 标记。

在下面的代码中,您看到我在自定义元素外部使用 document.querySelector 来获取 <input> 元素,而不是在自定义元素内部使用 this.querySelectorthis.shadowRoot.querySelector自定义元素代码。

customElements.define('fancy-p', class extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
  }
  get foo() {
    return "foo"
  }
  connectedCallback() {
    this.shadowRoot.innerHTML = `
      <style>
      p {
        color: red;
      }
      :host {
        background-color: blue;
      }
      </style>
      <div data-foo="bar">
        <slot></slot>
      </div>
    `;
    let input = this.shadowRoot.querySelector("input");
    console.log('inside input=',input); // input will be null
  }
});


//Since the `<input>` tag is *owned* by the outside DOM you need to get the events from the outside:


let input = document.querySelector("input");
console.log('outside input=', input);
input.addEventListener('input', () => {
  console.log("input is:" + input.value);
});
<fancy-p>
  <p>Bar</p>
  <input id="foo" type="text" name="text" placeholder="Hello"/></fancy-p>

如果你想通过 shadowDOM 访问它,那么你需要在 shadowDOM 中定义它:

customElements.define('fancy-p', class extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
  }
  get foo() {
    return "foo"
  }
  connectedCallback() {
    this.shadowRoot.innerHTML = `
      <style>
      p {
        color: red;
      }
      :host {
        background-color: blue;
      }
      </style>
      <div data-foo="bar">
        <slot></slot>
        <input id="foo" type="text" name="text" placeholder="Hello">
      </div>
    `;

    let input = this.shadowRoot.querySelector("input");
    console.log('inside input=',input);
    input.addEventListener('input', () => {
      console.log("input is:" + input.value);
    });
  }
});
<fancy-p>
  <p>Bar</p>
</fancy-p>

当调用 connectedCallback() 方法时,<input> 元素还没有附加到灯 DOM 上。所以你那个时候用this.querySelector('input')是拿不到的。

这不是真正的问题:您可以在自定义元素本身上监听 {input} 事件,因为该事件将 冒泡 到父元素。

this.addEventListener( 'input', ev => console.log( ev.target, ev.target.value ) )  

参见 运行 示例:

customElements.define('fancy-p', class extends HTMLElement {
   constructor() {
      super()
      this.attachShadow({mode: 'open'})
          .innerHTML = `
             <style>
               ::slotted(p) { color: red; }
               :host { display:inline-block; background-color: blue; }
             </style>
             <div data-foo="bar">
               <slot></slot>
            </div>`
      this.addEventListener('input', ev => console.log( '%s value is %s', ev.target, ev.target.value))   
   }
})
<fancy-p>
  <p>Bar</p>
  <input id="foo" type="text" name="text" placeholder="Hello">
</fancy-p>