LoginSignup
1
0

More than 1 year has passed since last update.

はいさい!ちゅらデータのオースティンやいびーん!

概要

Web Componentsの作成を助けてくれるLitでShadow DOMではなく、Light DOMにレンダーする部品を作る方法を紹介します!

背景

LitElementで部品を作ると、デフォルトでShadow DOMを追加し、そのShadow DOMにHTMLエレメント、CSSのスタイルなどをレンダーしていきます。

しかし、Shadow DOMではなく、Light DOMにレンダーしたい場合があります。

  1. グローバルCSSを適応したい時
  2. ログインフォームでブラウザのキーマネージャーと相性良く付き合いたい時(Chrome、SafariはShadow DOMに対してパスワードの自動入力・保存ができないのです)
  3. Light DOMの<form>に入る入力部品を作りたい時

こういう時に以下の方法でLight DOMにレンダーしていただけます!

コード

LitElementは、customElement.defineでブラウザに登録された時に、内蔵化されているLitElement.createRenderRootを実行して、部品のHTMLをどこに入れるかを定めます。

この関数を以下のように上書きすると、this.shadowRootではなく、thisをRenderRootに指定することができます。

src/lit-element.ts
import { LitElement, html } from "lit";

export default class LitComponent extends LitElement {
  protected createRenderRoot() {
    return this; // Light DOMを使うように
    // thisがLight DOMの<lit-component>のエレメントに当たります
  }

  #handleSubmit: EventListener = (event) => {
    event.preventDefault();
  };

  render() {
    return html`
      <h1>Lit Form</h1>
      <form @submit=${this.#handleSubmit}>
        <label for="name">名前</label>
        <input type="text" id="name" name="name" />
      </form>
    `;
  }
}

これで、この部品はLight DOMにレンダーされるようになります!

実際にブラウザで見てみよう。

Webpackなどのバンドラーのエントリーポイントで上記のLitComponentを読み込んで、customElements.defineでブラウザに登録します。

src/index.ts
import LitComponent from "./lit-component"

customElements.define("lit-component", LitComponent);

また、HTMLのテンプレートに<lit-element>を入れます。また、Light DOMの<style>タグでh1の文字の色を赤にしています。

<lit-element>内の<h1>が赤くなっていれば、Light DOMにレンダーできていることがわかります。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <style>
    h1 {
      color: red;
    }
  </style>
  <lit-component></lit-component>
</body>

</html>

これをビルドして、ブラウザで開いてみると、スクリーンショット 2022-07-08 11.53.36.png
出た!赤いね。

そして、InspectorでHTMLを見てみると、
スクリーンショット 2022-07-08 11.54.33.png
Shadow DOMはなく、Light DOMにレンダーされていることが確認できます。

注意点: LitElementのstylesが使えなくなる!

このように、Light DOMにレンダーすると、通常のLitElementのCSSを指定するstatic styles = css``が使えなくなります。

通常は、以下のように書きます。

import { LitElement, html, css } from "lit";

export default class LitComponent extends LitElement {
  #handleSubmit: EventListener = (event) => {
    event.preventDefault();
  };

  static styles = css`
    input {
      height: 40px;
    }
  `;

  render() {
    return html`
      <h1>Lit Form</h1>
      <form @submit=${this.#handleSubmit}>
        <label for="name">名前</label>
        <input type="text" id="name" name="name" />
      </form>
    `;
  }
}

しかし、上記のLight DOMをRenderRootに指定する方法だと、static stylesが反映されません。

なぜなら、実は、LitElement.createRenderRootでは、デフォルトで以下のようなコードを実行しているからです。

import { LitElement, html, css, adoptStyles } from "lit";

...

protected createRenderRoot() {
    this.attachShadow({ mode: "open" });
    adoptStyles(this.shadowRoot!, [LitComponent.styles])
    return this.shadowRoot; 
}

このcreateRenderRootを上書きすると、lit.adoptStylesがなくなってしまいます。もっと残念な知らせですが、このadoptStylesにthisだけを渡すことができないようです。

これで、以下のような工夫が必要になります。

import { LitElement, html, css } from "lit";

export default class LitComponent extends LitElement {
  protected createRenderRoot() {
    return this; // Light DOMを使うように
    // thisがLight DOMの<lit-component>のエレメントに当たります
  }

  #handleSubmit: EventListener = (event) => {
    event.preventDefault();
  };

  render() {
    const styles = css`
      input {
        height: 40px;
      }
    `;
    return html`
      <style>${styles}</style>
      <h1>Lit Form</h1>
      <form @submit=${this.#handleSubmit}>
        <label for="name">名前</label>
        <input type="text" id="name" name="name" />
      </form>
    `;
  }
}

まとめ

これで、LitElementをShadow DOMからLight DOMにレンダーするように指定する方法を紹介してきましたが、いかがでしょうか?

次の記事では、Shadow DOMとLight DOMの両方にレンダーする方法を紹介します!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0