LoginSignup
2
0

Web Componentsを使いやすくする kit.js

Last updated at Posted at 2022-08-06

はじめに

JavaScriptにはWeb Componentsという機能が存在します。
標準化されたコンポーネントは、個人的に欲しかったのでよく使ってますが、
機能が不足していたり、冗長な書き回しになったり、少し不便です。

そこでWeb Componentsを使いやすくするためのプログラムを作ったので、その紹介です。

kit.jsの使い方

  1. kit.jsをモジュールとして読み込む
  2. クラスのコンストラクタ等でkit(this)を実行する
<script type="module">
import kit from 'https://cdn.jsdelivr.net/gh/worldace/kit/kit.js'

class SampleElement extends HTMLElement{

    constructor(){
        super()
        kit(this) // これだけ
    }

    html(){
        return `<h1>タイトル</h1><p>本文</p>`
    }
}
customElements.define('sample-element', SampleElement)
</script>

<sample-element></sample-element>

基本的な使い方はこれだけです。
次からはkit.jsの機能について説明してきます。

HTMLの自動登録

  • クラスのhtml()が返すテンプレートが、自動的にShadowDOMに登録されます
  • テンプレートはHTML文字列DOM仮想DOMの3種類に対応
テンプレートがHTML文字列の場合
class SampleElement extends HTMLElement{

    html(){
        return `
          <h1>タイトル</h1>
          <p>本文</p>
        `
    }

    constructor(){
        super()
        kit(this)
    }
}
テンプレートをtemplateタグに書く場合
class SampleElement extends HTMLElement{

    html(){
        return document.querySelector('template') // JavaScript内にHTMLを書かずに済む
    }

    constructor(){
        super()
        kit(this)
    }
}

※仮想DOMについては後述

CSSの自動登録

  • クラスのcss()が返す文字列が、CSSとしてShadowDOMに登録されます
  • CSSファイルに書くのと同じ要領で書けます
class SampleElement extends HTMLElement{

    html(){
        return `<h1>タイトル</h1><p>本文</p>`
    }

    css(){
        return `
            h1{color:blue;}
            p{color:red;}
        `
    }

    constructor(){
        super()
        kit(this)
    }
}
  • css()の結果は再利用され、adoptedStyleSheetsに登録されるので効率的です。(Safari除く)
  • html()のテンプレート内に<style>タグを書くことも可能ですが、生成毎に処理が走るので非効率。

イベントの自動登録

クラスに$ID名_イベント名という名前のメソッドがあると、イベントとして登録されます

class SampleElement extends HTMLElement{

    constructor(){
        super()
        kit(this)
    }

    $title_click(event){ // IDが title のタグに click イベントを登録
        this.$.result.textContent = 'タイトルがクリックされました'

        // イベント解除例
        // this.$.title.removeEventListener('click', this.$title_click)
    }

    html(){
        return `<h1 id="title">タイトル</h1> <p id="result"></p>`
    }
}
  • イベント内のthisは固定の安心設計。(発生源はevent.targetで取得)
  • 特別なIDが登録先として使えます。例えば"$_click"ならshadowRootに登録されます
特別なID 登録先DOM
$ this.shadowRoot
$Host this
$Window window
$Document document
$Body document.body

HTML選択の短縮化

HTMLを選択するにはquerySelector()を使いますが、冗長なのでjQuery風の短縮構文があります。

//単数選択
this.$('セレクタ') // this.shadowRoot.querySelector('セレクタ') の短縮形

//複数選択はセレクタの頭に*を付ける。戻り値は配列
this.$('*セレクタ')

IDが付いてるタグには、さらに短くアクセスできます。

this.$.ID名 // this.shadowRoot.querySelector('#ID名') の短縮形

LightDOMでは、IDが付いてるタグにはwindow.ID名でアクセス可能です。
既存プロパティと重複しないように、ID名を $ から始めるのがベストプラクティス。

仮想DOMの使用

テンプレートには仮想DOMを使うこともできます。使い方は

  • html()kitタグ付きテンプレートか配列を返す
  • 描画したい時にkit(this)を実行する

文法はReact風で、直感的に書けるようにhtmpreactを採用。
注意点としてタグは必ず閉じてください。単独タグでも閉じてください。<br />

仮想DOMのサンプル

カウンタ
class SampleElement extends HTMLElement{
    constructor(){
        super()
        this.count = 0
        kit(this) // 初回は全描画される
    }

    click(event){
        this.count++
        kit(this) // 2回目からは差分描画
    }

    html(){
        return kit`<button onclick=${this.click}>+</button> <p>${this.count}</p>`
    }
}
フォームに入力されたものを表示する
class SampleElement extends HTMLElement{
    constructor(){
        super()
        this.result = '未入力'
        kit(this) // 全描画
    }

    input(event){
        this.result = event.target.value
        kit(this) // 差分描画
    }

    html(){
        return kit`<input oninput=${this.input} /> <p class=abc>${this.result}</p>`
    }
}
配列を使い、リストを動的に追加する
class SampleElement extends HTMLElement{
    constructor(){
        super()
        this.list = [Date.now()] // 現在の時間
        kit(this)
    }

    click(event){
        this.list.push(Date.now())
        kit(this)
    }

    html(){
        return kit`
            <button onclick=${this.click}>追加</button>
            <ol>
              ${this.list.map(v => kit`<li>${v}</li>`)}
            </ol>
     `}
}
二重ループ
class SampleElement extends HTMLElement{
    constructor(){
        super()
        this.table = [
            ['バナナ', '200円'],
            ['リンゴ', '100円'],
        ]
        kit(this)
    }

    html(){
        const tr = []
        for(const row of this.table){
            tr.push(kit`<tr>${row.map(v => kit`<td>${v}</td>`)}</tr>`)
        }
        // one-liner
        // this.table.map(row => kit`<tr>${row.map(v => kit`<td>${v}</td>`)}</tr>`)

        return kit`<table>${tr}</table>`
    }

}

作成事例

1.png


2.png


3.png

2
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
2
0