处理Web组件时的Vanilla JavaScript事件委托

时间:2018-07-24 08:26:41

标签: javascript web-component shadow-dom event-delegation

我当前的项目使用Web组件(“自定义元素”和“阴影DOM”),这些组件使我能够将复杂的逻辑和样式封装在Light DOM之外。

不幸的是,我现在遇到了一个麻烦,我需要能够随意切换元素,而不必担心取消绑定和重新绑定事件处理程序的麻烦。

这听起来像是将事件委派给我的工作,所以我尝试将事件侦听器添加到Light DOM的父节点中,希望事件从Shadow DOM中冒出来。

这似乎与Shadow DOM和事件的封装背道而驰。target始终是ShadowRoot,而不是子代。

在这种情况下是否可以做一些事情来允许经典事件委派?下面的代码片段显示了该问题。我希望能够单击内部DIV并处理click事件处理程序中的单击,但是event.target始终是custom-el

/* jshint esversion: 6 */

customElements.define('custom-el', class extends HTMLElement {

	constructor() {
		super();

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

		const oInnerDiv = document.createElement('div');
		oInnerDiv.classList.add('inner');
    oInnerDiv.style.border = '2px solid blue';
    oInnerDiv.style.padding = '3rem';
		this._shadowRoot.appendChild(oInnerDiv);
	}

});

document.addEventListener('click', oEvent => {
	document.getElementById('result').innerText = oEvent.target.tagName;
}, true);
html {
	box-sizing: border-box;
}

*,
*::before,
*::after {
	box-sizing: inherit;
}

body {
	margin: 0;
	padding: 0;
}

main,
div,
custom-el {
	display: inline-block;
	border: 2px solid black;
	padding: 3rem;
}
<main>
    <custom-el>
</main>
  
<p id="result"></p>

1 个答案:

答案 0 :(得分:2)

如果阴影DOM模式为open,则可以借助Event.composedPath()方法来使内部元素被单击,该方法将返回交叉的节点的数组(首先是内部节点)

document.addEventListener('click', oEvent => {
    result.innerText = oEvent.composedPath()[0].tagName;
}, true);

此方法替换了旧的Event.path属性。

customElements.define('custom-el', class extends HTMLElement {
  constructor() {
    super();
    this._shadowRoot = this.attachShadow({ mode: 'open' });
    const oInnerDiv = document.createElement('div');
    oInnerDiv.classList.add('inner');
    oInnerDiv.style.border = '2px solid blue';
    oInnerDiv.style.padding = '1rem';
		this._shadowRoot.appendChild(oInnerDiv);      
  }
});

document.addEventListener('click', oEvent => {
  result.innerText = oEvent.composedPath()[0].tagName;
});
html {
	box-sizing: border-box;
}

*,
*::before,
*::after {
	box-sizing: inherit;
}

body {
	margin: 0;
	padding: 0;
}

main,
div,
custom-el {
	display: inline-block;
	border: 2px solid black;
	padding: 1rem;
}
<main>
    <custom-el></custom-el>
</main>
  
<p id="result"></p>