> 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/examples/popup-info.md).

# \<popup-info>

* :blue\_book: [Using shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM)
* :blue\_book: [shadowRoot.innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/innerHTML)

{% hint style="warning" %}
About [using external CSS styles](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#internal_vs._external_styles)

Note that [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) elements **do not block** paint of the shadow root, so there may be a **flash of unstyled content** (**FOUC**) while the stylesheet loads.
{% endhint %}

{% tabs %}
{% tab title="JS (external css)" %}

```javascript
/*
    Using Shadow DOM
    https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM
*/

// ⭐️ 1. custom web component
class PopUpInfo extends HTMLElement {

    // constructor
    constructor() {

        // tell HTMLElement to initialize itself.
        super();

        // Create a shadow root
        let shadow = this.attachShadow({mode: 'open'});

        // get attributes from this <popup-info>
        let info = this.getAttribute('data-text');
        let src = this.getAttribute('img') ?? 'https://mdn.github.io/web-components-examples/popup-info-box-web-component/img/alt.png';

        // set the shadow tree
        shadow.innerHTML = PopUpInfo.template(src, info);
        
    }

    // template HTML (⭐️ external css styles)
    static template(imgURL, infoText){
        return `
            <link href="popup.css" rel="stylesheet" />

            <span class="wrapper">
                <span class="icon" tabindex=0>
                    <img src="${imgURL}">
                </span>
                <span class="info">${infoText}</span>
            </span>
        `;
    }

} 

// ⭐️ 2. define custom tag <popup-info>
// ⭐️⭐️ custom element names must contain a "hyphen".
customElements.define('popup-info', PopUpInfo);

```

{% endtab %}

{% tab title="JS" %}

```javascript
// ⭐️ 1. custom web component
class PopUpInfo extends HTMLElement {

    // constructor
    constructor() {

        // tell HTMLElement to initialize itself.
        super();

        // Create a shadow root
        let shadow = this.attachShadow({mode: 'open'});

        // get attributes from this <popup-info>
        let info = this.getAttribute('data-text');
        let src = this.getAttribute('img') ?? 'https://mdn.github.io/web-components-examples/popup-info-box-web-component/img/alt.png';

        // set the shadow tree
        shadow.innerHTML = PopUpInfo.template(src, info);
        
    }

    // template HTML (domString)
    static template(imgURL, infoText){
        return `
            <style>
            .wrapper {
                position: relative;
            }

            .info {
                font-size: 0.8rem;
                width: 200px;
                display: inline-block;
                border: 1px solid black;
                padding: 10px;
                background: hsla(60, 80%, 50%, 0.9);
                border-radius: 10px;
                opacity: 0;
                transition: 0.6s all;
                position: absolute;
                top: 20px;
                left: 10px;
                z-index: 3;
            }

            img {
                width: 1.2rem;
            }

            .icon:hover + .info, .icon:focus + .info {
                opacity: 1;
            }
            </style>

            <span class="wrapper">
                <span class="icon" tabindex=0>
                    <img src="${imgURL}">
                </span>
                <span class="info">${infoText}</span>
            </span>
        `;
    }

} 

// ⭐️ 2. define custom tag <popup-info>
// ⭐️⭐️ custom element names must contain a "hyphen".
customElements.define('popup-info', PopUpInfo);

```

{% endtab %}

{% tab title="📁 popup.css" %}

```css
.wrapper {
    position: relative;
}

.info {
    font-size: 0.8rem;
    width: 200px;
    display: inline-block;
    
    border: 1px solid black;
    padding: 10px;
    background: hsla(60, 80%, 50%, 0.9);
    border-radius: 10px;
    opacity: 0;
    transition: 0.6s all;

    position: absolute;
    top: 20px;
    left: 10px;
    z-index: 3;
}

img {
    width: 1.2rem;
}

.icon:hover + .info, .icon:focus + .info {
    opacity: 1;
}
```

{% endtab %}

{% tab title="codepen 1" %}
{% embed url="<https://codepen.io/lochiwei/pen/ExXyKOp>" %}
使用 domString
{% endembed %}
{% endtab %}

{% tab title="JS" %}

```javascript
// ⭐️ 1. custom web component
class PopUpInfo extends HTMLElement {

    // constructor
    constructor() {

        // tell HTMLElement to initialize itself.
        super();

        // Create a shadow root
        let shadow = this.attachShadow({mode: 'open'});

        // create spans
        let wrapper = tag('span', {'class': 'wrapper'});
        let icon = tag('span', {'class': 'icon', 'tabindex': 0});

        let info = tag('span', {'class': 'info'});
        info.textContent = this.getAttribute('data-text');

        // img
        let src = this.getAttribute('img') ?? 'https://mdn.github.io/web-components-examples/popup-info-box-web-component/img/alt.png';
        let img = tag('img', {'src': src});

        icon.appendChild(img);

        // attach elements to shadow dom    // shadow root
        shadow.appendChild(this.style());   //   ├ style
        shadow.appendChild(wrapper);        //   └ wrapper
        wrapper.appendChild(icon);          //       ├ icon
        wrapper.appendChild(info);          //       │   └ img
    }                                       //       └ info

    // css style
    style() {
        // Create some CSS to apply to the shadow dom
        let style = document.createElement('style');

        style.textContent = `
        .wrapper {
            position: relative;
        }

        .info {
            font-size: 0.8rem;
            width: 200px;
            display: inline-block;
            border: 1px solid black;
            padding: 10px;
            background: hsla(60, 80%, 50%, 0.9);
            border-radius: 10px;
            opacity: 0;
            transition: 0.6s all;
            position: absolute;
            top: 20px;
            left: 10px;
            z-index: 3;
        }

        img {
            width: 1.2rem;
        }

        .icon:hover + .info, .icon:focus + .info {
            opacity: 1;
        }`;

        return style;
    }

} 

// ⭐️ 2. define custom tag <popup-info>
// ⭐️⭐️ custom element names must contain a "hyphen".
customElements.define('popup-info', PopUpInfo);


/* -------- helpers -------- */

function tag(name, attrs){
    // create element
    let elem = document.createElement(name);
    // set attributes
    for (const [key, value] of Object.entries(attrs)) {
        elem.setAttribute(key, value);
    }
    // return element
    return elem;
}
```

{% endtab %}

{% tab title="HTML" %}

```markup
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. 
Autem, assumenda aut voluptas perspiciatis impedit alias. 
Fugit atque eos voluptatem suscipit quos nostrum! 
Libero tenetur deleniti minima! Atque laborum minima non.</p>

<!-- web component -->
<popup-info 
    img="https://mdn.github.io/web-components-examples/popup-info-box-web-component/img/alt.png" 
    data-text="Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card."
></popup-info>

<span>hover over this image.</span>

<h1>Hello</h1>
```

{% endtab %}

{% tab title="codepen 2" %}
{% embed url="<https://codepen.io/lochiwei/pen/GREqZbJ>" %}
使用 DOM API
{% endembed %}
{% endtab %}
{% endtabs %}
