> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/web/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/web/component/shadow-dom/events/active-element.md).

# Active Element

[Web Components](/web/component.md) ⟩ [Shadow DOM](/web/component/shadow-dom.md) ⟩ [Events](/web/component/shadow-dom/events.md) ⟩

{% tabs %}
{% tab title="📗 參考" %}

* Google ⟩ Web Component ⟩ [Handling focus](https://developers.google.com/web/fundamentals/web-components/shadowdom#focus)
  {% endtab %}

{% tab title="📘 手冊" %}

{% endtab %}

{% tab title="👉 相關" %}

* [\<my-log> ❤️](/web/component/examples/less-than-my-log-greater-than.md)
  {% endtab %}
  {% endtabs %}

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

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

## Active Element in Shadow DOM <a href="#in-shadow-dom" id="in-shadow-dom"></a>

```javascript
// ⭐️ 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 <a href="#multiple-levels-of-shadow-dom" id="multiple-levels-of-shadow-dom"></a>

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**:

```javascript
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} <a href="#delegates-focus" id="delegates-focus"></a>

{% hint style="info" %}

* 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**.
  {% endhint %}

💾 [replit](https://replit.com/@pegasusroe/delegatesFocus#script.js)

{% tabs %}
{% tab title="main.js" %}

```javascript
// 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);
}
```

{% endtab %}

{% tab title="x-focus" %}

```javascript
// register <x-focus>
customElements.define('x-focus', class extends HTMLElement {

    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.
        */

        const delegatesFocus = this.hasAttribute('delegates-focus');

        const root = 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()
});
```

{% endtab %}

{% tab title="html" %}

```javascript
<x-focus delegates-focus></x-focus>
<x-focus></x-focus>

<my-log></my-log>
```

{% endtab %}

{% tab title="css (outer)" %}

```css
:focus {
    outline: 2px solid red;
}
```

{% endtab %}

{% tab title="css (inner)" %}

```javascript
:host {
    display: flex;
    gap: 8px;
    border: 1px dotted black;
    padding: 16px;
}

:focus {
    outline: 2px solid blue;
}
```

{% endtab %}

{% tab title="codepen" %}
{% embed url="<https://codepen.io/lochiwei/pen/MWoqKZo>" %}

{% endtab %}

{% tab title="👉 相關" %}

* [\<my-log> ❤️](/web/component/examples/less-than-my-log-greater-than.md)
  {% endtab %}
  {% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lochiwei.gitbook.io/web/component/shadow-dom/events/active-element.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
