通过 Shadow DOM 传递带有 composed=false 的标准 JavaScript 事件

Passing standard JavaScript event with composed=false through Shadow DOM

我有一个自定义的 lit web 组件,它的阴影 DOM 中包含一个 <input> 元素。我想对自定义元素外部的输入触发的 change 事件作出反应,但 change 事件默认为 composed: false,因此该事件不会通过 Shadow DOM边界。我可以在我的组件实现中捕获事件,但是 composed 属性 是只读的,所以我无法更新它并分派相同的事件对象。我可以用 new Event('change', {'composed': true}) 创建一个新对象,但是它没有像原始事件的 target 这样的属性。什么是好的方法?我应该手动将原始事件的属性复制到新事件对象吗?

无法多次发送单个 Event 实例,因此即使您可以修改原始事件的 composed 属性,您也无法重新发送它.

因此您需要创建一个新事件以从您的自定义元素中分派,但您希望如何创建事件以及事件应包含的内容的详细信息可能取决于您的用例。我建议尽量保持简单,并制作一个包含您需要的信息的事件,然后发送它。

在大多数情况下,本机 change event is not composed, but you can simulate propagating it out of your custom element by creating an event with the name change and dispatching it from your custom element. You probably don't even need to use composed 只是从您的自定义元素 (this) 中调度它,这可能是有原因的,这使得它在父范围内可用(从您的影子根向上一级) 在大多数情况下,事件可能应该在这里处理。

你的影子根中有一个 <input> 这一事实可能应该被视为一个实现细节(至少在某些情况下)并且不要不必要地暴露在外面,但是当你确实需要直接暴露它时,您可以使其可用,例如作为您的自定义元素上的 属性(然后可以从您的自定义事件访问),或者您可以在事件对象中包含对它的引用(例如,在 detail 属性 中 CustomEvent 或自定义事件的 属性 class).

例如 here's <vaadint-text-field> 等 Vaadin 组件如何传播 change 事件:

const changeEvent = new CustomEvent('change', {
  detail: {
    sourceEvent: e
  },
  bubbles: e.bubbles,
  cancelable: e.cancelable
});
this.dispatchEvent(changeEvent);

此处原始事件显式公开为 event.detail.sourceEvent,因此您可以从自定义 change 事件中获取输入值,例如 event.detail.sourceEvent.target.value.

如果您通过 属性(例如 myInput)公开输入元素,则不需要使用 CustomEventdetail,因为您可以执行类似event.target.myInput.value,或者如果原始 change 事件实际上导致自定义元素上的 value 属性 发生变化,您可以改为阅读它。

// Dispatch event (in your custom element)
const changeEvent = new Event('change', {
  bubbles: e.bubbles,
  cancelable: e.cancelable
});
this.dispatchEvent(changeEvent);

// Read the input value in event handler (assuming your custom element
// has declared `myInput` as a reference to the `<input>`)
event.target.myInput.value
// Alternatively access via shadowRoot (not very nice)
event.target.shadowRoot.querySelector('input').value

如果您创建自定义事件 class,您还可以在事件中包含自定义属性或方法:

// Declare event class once somewhere
class MyChangeEvent extends Event {
  constructor(sourceEvent) {
    super('change', {
      bubbles: sourceEvent.bubbles,
      cancelable: sourceEvent.cancelable,
    });
    this.sourceEvent = sourceEvent;
  }
}

// Trigger a custom event
this.dispatchEvent(new MyChangeEvent(e));

// Access custom event property in an event handler
event.sourceEvent

除了使用事件名称 change,您当然可以使用自己的自定义事件名称,例如 my-change-event,但是当您需要自定义元素时,使用 change 应该没问题表现得像原生 <input> ,这可能允许您的组件在某些情况下(可能有限制)或者如果您只是想模仿原生 change 开发人员已经熟悉的事件。在大多数情况下,您发送的 change 事件是创建为 EventCustomEvent 还是从 Event.

扩展的其他实例可能无关紧要