Reputation: 896
I have the following code, which creates a custom element, encapsulated with 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
});
The idea is, when 'Run' is clicked, the input would be taken from the input element and processed (in performTask()), then the output placed into '#result'. My two questions are:
This previous stack overflow post looks like it would have answered my question, but all the suggested links are no longer valid so am wondering if anyone could point me in the right direction :)
P.S. I'd rather not use templates since HTML Imports are not being supported by all browsers and I want all of my custom element code contained in one file.
Upvotes: 1
Views: 1965
Reputation: 31191
WITH CLOSURE
You can use the method querySelector
on your Shadow DOM root
to get inside elements:
'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: you can use template
without HTML Imports, in the same page. See the following snippet:
<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>
WITHOUT CLOSURE
If you don't want to use closure, you can declare a method called handleEvent
on your custom element, and add an Event Listener that will redirect on it:
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
}
Upvotes: 2
Reputation: 896
Turns out you can add functions to the shadow root itself, then you can just call this.parentNode.fn() on the shadow roots direct children to access the 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 });
(Thanks to MarcG for giving me the initial insight)
Upvotes: 2