Active Element

Web ComponentsShadow DOMEvents

events that are fired inside shadow DOM are adjusted to look like they come from the hosting element.

<x-focus>
  #shadow-root
    <input type="text" placeholder="input inside shadow dom">

Active Element in Shadow DOM

// ⭐️ 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:

function deepActiveElement() {
  // top-level active element
  let a = document.activeElement;
  // recursively drill into shadow roots to find activeElement
  while (a && a.shadowRoot && a.shadowRoot.activeElement) {
    a = a.shadowRoot.activeElement;
  }
  // return the real active element
  return 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.

💾 replit

// outer DOM events (⭐️ 收不到 `focus` 訊號❓)
document.body.addEventListener('focus', e => {
    let tag = document.activeElement.nodeName;
    console.log(`Active element (outer DOM): ${tag}`);
})

// main
function log(msg){
    const div = document.querySelector('my-log');
    div.log(msg);
}

Last updated