'How to attach a keyup event to Custom Element shadowRoot
I have searched for some time; but only find Polymer answers;
or answers where EventListeners are put on DOM elements inside the shadowRoot.
The effect I am trying to achieve with native Custom Elements:
- Only the focussed element should accept (and display) a keypress
It is possible to attach a click event to the shadowRoot, it seems I am doing something wrong for the 'keyup' event.
If I put the EventListener
on the window
all elements (of course) update with the same key info.
window.customElements.define('game-toes', class extends HTMLElement {
constructor() {
super().attachShadow({mode:'open'})
.innerHTML = this.tabIndex;
this.addEventListener('keyup',evt=>this.shadowRoot.innerHTML = 'key:'+evt.key);
}
});
game-toes{
display:inline-block;
height:auto;
width:100px;
padding:1em;
border:10px solid green;
}
game-toes:focus {
background-color: lightgreen;
}
<game-toes tabindex=1></game-toes>
<game-toes tabindex=2></game-toes>
<game-toes tabindex=3></game-toes>
Solution 1:[1]
You can do it like you were but you need to add some extra code to make it work:
function on(el, evt, cb) {
el.addEventListener(evt, cb);
return () => {
el.removeEventListener(evt, cb);
}
}
window.customElements.define('game-toes', class extends HTMLElement {
constructor() {
super()
.attachShadow({mode: 'open'})
.innerHTML = this.tabIndex;
}
connectedCallback() {
this._offKeyup = on(this, 'keyup', evt => {
this.shadowRoot.innerHTML = evt.key;
evt.stopPropagation(); // Prevent the event from leaving this element
});
}
disconnectedCallback() {
this._offKeyup();
}
});
game-toes{
display:inline-block;
height:auto;
width:100px;
padding:1em;
border:10px solid green;
}
game-toes:focus {
background-color: lightgreen;
}
<game-toes tabindex=1></game-toes>
<game-toes tabindex=2></game-toes>
<game-toes tabindex=3></game-toes>
- You may want to use
evt.stopPropagation()
to stop the event from leaving the component. - You either need to add your eventListener on the component itself OR, create an element in the shadowRoot with the ability to take focus and then set focus on the inner element when the component gets focus. And then you should be able to add the
keyup
event on that internal element. - It is safest to add the eventListener in
connectedCallback
and release them in thedisconnectedCallback
unless you never plan to remove your component.
Solution 2:[2]
In your example, the tabindex
attribute is set to the custom element <game-toes>
, and not to its Shadow DOM.
As a consequence, you should instead listen to the keyup
event on the custom element itself :
this.addEventListener('keyup',evt=>this.shadowRoot.innerHTML = 'key:'+evt.key);
window.customElements.define('game-toes', class extends HTMLElement {
constructor() {
super();
let shadowRoot=this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = this.tabIndex;
this.addEventListener('keyup',evt=>this.shadowRoot.innerHTML = 'key:'+evt.key);
}
});
game-toes{
display:inline-block;
height:auto;
width:100px;
padding:1em;
border:10px solid green;
}
game-toes:focus {
background-color: lightgreen;
}
<game-toes tabindex=1></game-toes>
<game-toes tabindex=2></game-toes>
<game-toes tabindex=3></game-toes>
Alternately, if you want to listen the keyup
event at the Shadow DOM level, you should set the tabindex
attribute in an element inside the Shadow DOM.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Danny '365CSI' Engelman |
Solution 2 |