TL;DR
addEventListenerのapline:init
で色々するとJSとマークアップを分離できそう。
Alpine.jsってなんぞ
私とAlpine.jsの出会いは Laravel Livewireでした。
Laravelフレームワーク上でSPAライクな実装ができるようになるフレームワークなのですが、低レベルなところで色々やっている部分の基底技術がこのAlpine.js
です。
特徴としては以下のような感じです。
- 15の属性、6のプロパティ、 2のメソッドから構成(※正確には18の属性、9のプロパティ、3のメソッド)。すごくシンプル
- 紐づけするためのオブジェクトもフィールドまたはgetterプロパティでシンプル
兎にも角にもシンプルさが目立つイメージです。けれどもデータバインド周りの機能は十分なので、jQueryの代替として利用することもできそうです。
そのサンプル、インラインじゃね?
Alpine.jsの簡単なサンプルを見ると、以下のようなものが多いです。
<div x-data="{ count: 0 }">
<button x-on:click="count++">Increment</button>
<span x-text="count"></span>
</div>
上記のサンプルはAlpine.js start-here; building a counterのコードですが……
Alpine.jsってインラインじゃないと使えない……?
サンプルコードではx-data
属性で使用するオブジェクト定義をしていて、x-on
属性にはcount
フィールドのインクリメント、x-text
属性でinnerHTML
に対するバインディングをしています。
x-text
属性は分かる。表示するのに紐付けないと何にもできないから。
しかし。
x-data
のオブジェクト指定、x-on
属性のロジックはなんとかできない? 分離したくない?
複雑なデータを扱おうとすると、x-data
属性に対して1行でいろんなことを書かないといけないため、シッチャカメッチャカになる未来が見えます。
<!-- こんな未来が見える -->
<div x-data="{fieldA: '', fieldB: '', fieldC: '', fieldD: '', fieldE: '', doSomething() { console.log('なにかする');}, }" >
マークアップとAlpine.jsの世界を分ける
バージョンなど
ライブラリ | バージョン |
---|---|
Alpine.js | 3.10.5 |
Pure.css | 3.0.0 |
Pure.cssはおまけです。サンプルのUIにグリッドを導入したかったので。
実装サンプル in Codepen
See the Pen Alpinejs code behind by FilunK (@filunK) on CodePen.
上記サンプルは以下のことをしています。
- windowサイズを追跡、表示
- チェックボックスのチェック状態をバインド、チェックボックスやらボタンやらでいじくれるように
詳説
HTML、JSのソースを説明していきます。
HTML
<div x-data="alpineCodebehind">
<div class="pure-g">
<div class="pure-u-1">
<label>
<input type="checkbox" x-model="checked"/>
チェックボックス
</label>
<button class="mx" x-on:click="toggle">チェックをイジる</button>
<span x-text="checked"></span>
</div>
<div class="pure-u-1">
<label>
WIDTH: <span x-text="width"></span>
</label>
</div>
<div class="pure-u-1">
<label>
HEIGHT: <span x-text="height"></span>
</label>
</div>
</div>
</div>
インラインでよく見るサンプルとの違いは以下のポイントです。
-
x-data
で指定するのはJSのオブジェクトではなく、データの名前 -
x-on
系統の属性にはロジックではなくメソッド名を指定
JS
// ES モジュール方式でのロード
import Alpine from "https://cdn.skypack.dev/alpinejs@3.10.5";
// Alpinejs INITイベントで初期化
document.addEventListener('alpine:init', () => {
// Alpinejsの外部からAlpinejsの世界にバインドする変数を定義
const windowSize = Alpine.reactive({
width: window.innerWidth,
height: window.innerHeight,
});
// Alpinejsの外部世界: window.resizeイベント
window.addEventListener('resize', (e) => {
windowSize.width = window.innerWidth;
windowSize.height = window.innerHeight;
});
// 画面のデータバインディング
Alpine.data('alpineCodebehind', () => ({
// チェック?
checked: false,
// computed: 幅
get width () {
return windowSize.width;
},
// computed: 高さ
get height () {
return windowSize.height;
},
// 振る舞い: トグル
toggle () {
this.checked = ! this.checked;
},
}));
});
// Alpinejsの開始
Alpine.start();
インラインでよく見るサンプルとの違いは以下のポイントです。
- CDN直読み込みではない。
-
Alpine.start()
の前にalpine:init
イベントに対するイベントリスナーを実装- イベントリスナー内で
Alpine.data()
メソッドにてデータ名とマークアップの後ろに控えるフィールド・メソッドを実装
- イベントリスナー内で
Alpine.data()
メソッドの第1引数が、メソッドの表すデータの名前になります。これがマークアップ側でのx-data
属性に指定する値です。
要は明示的にAlpine.start()
メソッドが実行する前に予めイベントリスナーを実装することが必要なわけです。
CDN直読み込みだと、その時点で初期化のイベントが終了してしまうので、x-data
属性にデータ名を指定できなくなってしまうんですね。知らんけど。
コレはおそらく、CodepenにおけるHTMLやJSのロードに関係する問題と思われます。
私自身が試しているわけではありませんが、CDN直読み込みでもalpine:init
でいい感じに実装しているコードもネット上では見受けられるので。
今回、サンプルではESモジュール方式でAlpine.js
をインポートしています。このあたりは色々と選択肢があると思います。vite
なりwebpack
なりでバンドルする、なんてこともありでしょう。
分離した際の思わぬメリット
このコードだとHTMLとJSを分離することに成功しているわけですが、別のメリットもあります。
Alpine.jsの外の世界のイベントをAlpine.jsの世界にバインドできる 点です。
サンプルのコードでは、以下のステップでAlpine.jsの外からAlpine.jsの中の世界に情報を持ち込んでいます。
-
Alpine.reactive()
メソッドでバインド用のデータ※以下『REACTIVE』を用意 - 他のイベントリスナーの中で REACTIVE を弄る
-
Alpine.data()
メソッドの世界の中で REACTIVE を使用する
該当のコードをサンプルから抜き出すと以下のコードになります。
// Alpinejsの外部からAlpinejsの世界にバインドする変数を定義
const windowSize = Alpine.reactive({
width: window.innerWidth,
height: window.innerHeight,
});
// Alpinejsの外部世界: window.resizeイベント
window.addEventListener('resize', (e) => {
windowSize.width = window.innerWidth;
windowSize.height = window.innerHeight;
});
// 画面のデータバインディング
Alpine.data('alpineCodebehind', () => ({
// computed: 幅
get width () {
return windowSize.width;
},
// computed: 高さ
get height () {
return windowSize.height;
},
}));
サンプルではwindowサイズの変更をリアルタイムに表示するため、window.resize
イベントで幅と高さを取得して、算出プロパティとして実装しています。
最後に
Javascriptの世界はいろんな技術が生まれては廃れてを繰り返していて、技術を追っかけるのが大変な一方で楽しい領域でもあります。しかも思いもしないところから
実はな、これ、JSのライブラリを利用してるんだよ
みたいなところがあって(特にWebアプリ・システムを構築しようとすると)、新鮮な刺激になります。
Alpine.js
もLaravel Livewire
きっかけで知ったものですが、コレ単体でも色々とできそうな可能性を感じました。
IEが一応亡き者となった今、こういった新しい技術を取り込んでゆくにはよい機会でしょう。