// โญ๏ธ if you click <input> inside a shadow root,// the `focus` event will look like it came from <x-focus>,// not the <input>โ๏ธdocument.activeElement // โญ๏ธ <x-focus>// โญ๏ธ only works with {mode: open}.document.activeElement.shadowRoot.activeElement // โญ๏ธ <input>
Active Element down Multiple Levels of Shadow DOM
If there are multiple levels of shadow DOM at play (say a custom element within another custom element), you need to recursively drill into the shadow roots to find the activeElement:
functiondeepActiveElement() {// top-level active elementlet a =document.activeElement;// recursively drill into shadow roots to find activeElementwhile (a &&a.shadowRoot &&a.shadowRoot.activeElement) { a =a.shadowRoot.activeElement; }// return the real active elementreturn a;}
{delegatesFocus: true}
If you click a node inside shadow DOM and the node is not a focusable area, the first focusable area becomes focused.
When a node inside shadow DOM gains focus, :focus applies to the host in addition to the focused element.
// outer DOM events (โญ๏ธ ๆถไธๅฐ `focus` ่จ่โ)document.body.addEventListener('focus', e => {let tag =document.activeElement.nodeName;console.log(`Active element (outer DOM): ${tag}`);})// mainfunctionlog(msg){constdiv=document.querySelector('my-log');div.log(msg);}
// register <x-focus>customElements.define('x-focus',classextendsHTMLElement {constructor() {// always call super() first in the constructor.super(); /* โญ๏ธ {delegatesFocus: true} option -------------------------------- โข If you click a node inside shadow DOM and the node is not a focusable area, the first focusable area becomes focused. โข When a node inside shadow DOM gains focus, :focus applies to the host in addition to the focused element. */constdelegatesFocus=this.hasAttribute('delegates-focus');constroot=this.attachShadow({ mode:'open', delegatesFocus: delegatesFocus // โญ๏ธ delegates focus });root.innerHTML =` <link rel="stylesheet" href="x-focus-styles.css" /> <div>${delegatesFocus ?'':'Not '}Clickable text</div> <input type="text" placeholder="input inside shadow dom"> `;// โญ๏ธ `focus` event inside shadow DOM:this.addEventListener('focus',function(e) {let tag =root.activeElement.nodeName;console.log(`Active element (inside shadow dom): ${tag}`); }); }// end: constructor()});