Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Event listener outside of Shadow DOM won't bind to elements inside of Shadow DOM

I have an Angular web component installed on my site. It uses Shadow DOM so it's super fast (which it has to be in my case).

On my site I also have a shortcut on h which opens up a popup that displays some helpful information. It's a must that this h keybinding stays as it is. Example code of how it was implemented can be seen here: https://jsfiddle.net/js1edv37/

It's a simple event listener that listens on document:

$(document).on("keyup", function(e) {

}

However, this also gets triggered when my web component has focused textarea or input elements. This happens because it uses Shadow DOM, which a script from the outside cannot access.

You can test it by pressing h on the keyboard inside and outside of the input and textarea elements.

Is there a way to let my script from outside of the Shadow DOM web component, still listen for the keyup event, but make it listen for all elements on the page? Even the ones inside the Shadow DOM.

like image 786
MortenMoulder Avatar asked Apr 16 '19 08:04

MortenMoulder


People also ask

Why do scripts in the main document ignore events in Shadow DOM?

But scripts in the main document have no idea about the shadow DOM internals, especially if the component comes from a 3rd-party library. So, to keep the details encapsulated, the browser retargets the event. Events that happen in shadow DOM have the host element as the target, when caught outside of the component.

What is the difference between light Dom and Shadow DOM Event bubbling?

That’s an element from the light DOM, so no retargeting. On the other hand, if the click occurs on an element originating from shadow DOM, e.g. on <b>Name</b>, then, as it bubbles out of the shadow DOM, its event.target is reset to <user-card>. For purposes of event bubbling, flattened DOM is used.

What is the inner target of an event in Shadow DOM?

Events that happen in shadow DOM have the host element as the target, when caught outside of the component. If you click on the button, the messages are: Inner target: BUTTON – internal event handler gets the correct target, the element inside shadow DOM.

What happens when I click the button in the Shadow DOM?

Once you click the button, the following messages will be shown: Inner target: BUTTON - the internal event handler receives the correct target, the element within the shadow DOM. Outer target: USER-CARD - the document event handler receives the shadow host as a target.


2 Answers

In the Web Component, get the input element with a querySelector() call on the shadowRoot property:

let textareainshadow = div.shadowRoot.querySelector( 'textarea' )

Then listen to the keyup event and stop its propagation with the help of the stopImmediatePropagation() method.

textareainshadow.addEventListener( 'keyup' , ev => {
    console.log( 'caught', ev.type )
    ev.stopImmediatePropagation()
}) 

https://jsfiddle.net/7mkrxh25/1/

like image 154
Supersharp Avatar answered Nov 14 '22 23:11

Supersharp


If you save the reference to the shadow root you can always access it's children as search on those

$(document).on("keyup", function(e) {
    let focusedInputs = $("input:focus, textarea:focus").length + $(shadow).children("input:focus, textarea:focus").length;

    if (focusedInputs > 0) {
        return true;
    }

    if (e.keyCode === 72) {
        trigger();
    }
});

function trigger() {
    alert("If this was triggered, everything is perfectly fine");
}

let div = document.querySelector("div");
let shadow = div.createShadowRoot();
shadow.innerHTML = "<textarea>This shouldn't fail</textarea>";
textarea {
    width: 500px;
    height: 100px;
}

input {
    width: 250px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<textarea>Some stuff here</textarea>
<br />
<input type="text" value="Some more text here" />

<br />
<br />

<h1>Shadow DOM element WON'T fail now :)</h1>

<div></div>

Fiddle

like image 43
George Avatar answered Nov 14 '22 21:11

George