Делегирование событий ванильного JavaScript при работе с веб-компонентами

В моем текущем проекте используются веб-компоненты (Custom Elements и Shadow DOM), которые позволяют мне инкапсулировать сложную логику и стили вне Light DOM.

К сожалению, сейчас у меня возникла загвоздка, когда мне нужно иметь возможность переключать элементы по своему желанию, не беспокоясь об отвязке и повторной привязке обработчиков событий.

Для меня это звучит как задача делегирования событий, поэтому я попытался добавить прослушиватель событий к родительскому узлу в Light DOM, надеясь, что событие всплывет из Shadow DOM.

Похоже, что это противоречит инкапсуляции Shadow DOM, и event.target всегда является ShadowRoot, а не дочерним элементом.

Могу ли я что-то сделать, чтобы разрешить классическое делегирование событий в этой ситуации? Приведенный ниже фрагмент кода показывает проблему. Я хочу иметь возможность щелкнуть внутренний DIV и обработать щелчок в обработчике событий щелчка, но 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>


person michael    schedule 24.07.2018    source источник
comment
Я не знаю, насколько он стандартный, но вы можете получить к нему доступ в oEvent.path[0] в Chrome (поддерживают ли другие браузеры ShadowDOM?)   -  person Kaiido    schedule 24.07.2018
comment
Я считаю, что Safari поддерживает, а FF имеет это за флагом (из-за стандартизации в v63). Еще ничего от Edge   -  person michael    schedule 24.07.2018
comment
AFK прямо сейчас. еще раз не знаю стандартов. FF предоставляет свойство Event.explicitOriginalTarget, которое может утечка этой информации? Для Safari, IDK ...   -  person Kaiido    schedule 24.07.2018
comment
Вы пробовали использовать посредничество Mid DOM?   -  person Bekim Bacaj    schedule 24.07.2018
comment
Итак, я вернулся к клавиатуре, и действительно, FF 63 действительно просачивается через oEvent.explicitOriginalTarget, Safari, с другой стороны, похоже, не предоставляет никаких средств для доступа к нему в таком виде.   -  person Kaiido    schedule 24.07.2018


Ответы (1)


Если теневой режим DOM равен open, можно получить щелчок по внутреннему элементу с помощью _ 2_, который вернет массив пересеченных узлов (сначала внутренний узел).

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>

person Supersharp    schedule 24.07.2018