Как добавить JavaScript в пользовательские элементы?
У меня есть следующий код, который создает пользовательский элемент, инкапсулированный с Shadow DOM:
'use strict'
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
var root = this.createShadowRoot();
var divEl = document.createElement('div');
divEl.setAttribute("id", "container");
divEl.innerHTML =
"<input id='input' type='text'>"
+ "<br>"
+ "Result: <span id='result'></span>"
+ "<br><button onclick='performTask()'>Run</button>";
root.appendChild(divEl);
};
document.registerElement('custom-ele', {
prototype: proto
});
Идея состоит в том, что при нажатии "Выполнить" ввод будет взят из элемента ввода и обработан (в executeTask()), а затем вывод помещен в "#result". Мои два вопроса:
- Как мне получить значение из поля ввода в Shadow DOM?
- Как бы я поместил вывод в #result?
Это предыдущее сообщение о переполнении стека выглядит так, как будто оно ответило бы на мой вопрос, но все предложенные ссылки больше не действительны, поэтому мне интересно, может ли кто-нибудь указать мне правильное направление:)
PS Я бы предпочел не использовать шаблоны, так как импорт HTML поддерживается не всеми браузерами, и я хочу, чтобы весь код пользовательского элемента содержался в одном файле.
2 ответа
Оказывается, вы можете добавлять функции в сам теневой корень, затем вы можете просто вызвать this.parentNode.fn() для теневых корней, чтобы дети получили доступ к shadowRoot...
proto.createdCallback = function() {
let root = this.createShadowRoot();
root.innerHTML = "<input id='input' type='text'>"
+ "<br>"
+ "Result: <span id='result'></span>"
+ "<br><button onclick='this.parentNode.process()'>Run</button>";
this.shadowRoot.process = function() {
let spanEle = this.querySelector('span');
let inputEle = this.querySelector('input');
spanEle.textContent = performAlgorithm(inputEle.value.split(','));
};
};
document.registerElement('custom-ele', { prototype: proto });
(Спасибо MarcG за предоставленную мне начальную информацию)
С ЗАКРЫТИЕМ
Вы можете использовать метод querySelector
на вашем Shadow DOM root
попасть внутрь элементов:
'use strict'
var proto = Object.create( HTMLElement.prototype )
proto.createdCallback = function ()
{
//HTML ROOT
var root = this.createShadowRoot()
root.innerHTML = "<input id='input' type='text'>"
+ "<br>"
+ "Result: <span id='result'></span>"
+ "<br><button>Run</button>"
//UI
var buttonEle = root.querySelector( "button" )
var inputEle = root.querySelector( "input" )
var spanEle = root.querySelector( "#result" )
buttonEle.onclick = function ()
{
var input = inputEle.value
// do some processing...
spanEle.textContent = input
}
}
document.registerElement( 'custom-ele', { prototype: proto } )
NB: вы можете использовать template
без импорта HTML, на той же странице. Смотрите следующий фрагмент:
<html>
<body>
<custom-ele></custom-ele>
<template id="custelem">
<input id='input' type='text'>
<br>Result:
<span id='result'></span>
<br>
<button>Run</button>
</template>
<script>
var proto = Object.create(HTMLElement.prototype)
proto.createdCallback = function() {
//HTML ROOT
var root = this.createShadowRoot()
root.innerHTML = custelem.innerHTML
//UI
var buttonEle = root.querySelector("button")
var inputEle = root.querySelector("input")
var spanEle = root.querySelector("#result")
buttonEle.onclick = function() {
var input = inputEle.value
// do some processing...
spanEle.textContent = input
}
}
document.registerElement('custom-ele', {
prototype: proto
})
</script>
</body>
</html>
БЕЗ ЗАКРЫТИЯ
Если вы не хотите использовать замыкание, вы можете объявить методhandleEvent
на свой пользовательский элемент и добавьте прослушиватель событий, который будет перенаправлять на него:
proto.createdCallback = function ()
{
//HTML ROOT
var root = this.createShadowRoot()
root.innerHTML = custelem.innerHTML
//EVENT
var buttonEle = root.querySelector( "button" )
buttonEle.addEventListener( "click", this )
}
proto.handleEvent = function ( ev )
{
var inputEle = this.shadowRoot.querySelector( "input" )
var spanEle = this.shadowRoot.querySelector( "#result" )
// do some processing...
spanEle.textContent = inputEle.value
}