Alpine.jsはドロップダウンメニューやタブの切り替え、モーダルなどの機能をVue.jsやReactなど利用せず手軽に実装したい場合に利用できるJavaScriptのフレームワークです。実際に利用してみるとその手軽さを実感することができます。

JavaScriptのフレークワークということでVue.sjやReactとは別の新しいフレークワークをまた一から学習しなければと不安になる人もいるかもしれませんがVue.jsやReactなどを代替するものではありません。jQueryやVanilla JavaScriptの代替となるものです。Vue.jsやReactを学習している人ならより手軽さを感じられると思うので一度はAlpine.jsの動作を確認してみてください。

本文書ではLaravelフレークワークでも利用されているドロップダウンメニューの作成に必要なAlpine.jsの基礎を説明し実際にスクラッチからドロップダウンメニューを作成します。

Alpine.jsを使うための準備

index.htmlファイルを作成してcdnを使ってalpine.jsをロードします。これでAlpline.jsを使うための準備は完了です。


<!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>
  <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.8.1/dist/alpine.js" defer></script>
</head>
<body>
  <h1>Hello World</h1>
</body>
</html>

初めてAlpine.js

Alpine.jsを使ってh1タグで表示されているHello Worldを表示させてみましょう。

divタグにx-data属性を設定します。Alpine.jsのドキュメントでは”x-dataは新しいコンポーネントスコープを宣言します”と記述されていますがもし意味がわからなければAlpine.jsを利用するために必須な宣言と理解しておきましょう。x-dataの中では、messageにHello Worldを設定しているのでx-dataを設定したdivの内側であればこのmessageプロパティを自由に利用することができます。


<body>
  <div x-data="{message: "Hello World"}"></div>
</body>

messageをブラウザに表示させるためにx-textディレクティブを追加します。x-textにmessageを設定することでh1タグにmessageの内容が挿入され表示されます。


<div x-data="{message: 'Hello World'}">
  <h1 x-text="message"></h1>
</div>

ブラウザで確認するとmessageに設定されたHello Worldが表示されます。

Alpine.jsでHello World
Alpine.jsでHello World

x-dataのdivの内側(スコープの範囲)であれば複数のv-textを利用してもmessageを表示させることができますがその範囲外になるとv-textは利用できません。


<body>
  <div x-data="{message: 'Hello World'}">
    <h1 x-text="message"></h1>
    <h1 x-text="message"></h1>
    <h1 x-text="message"></h1>
  </div>
  <h1 x-text="message"></h1>
</body>

ブラウザで見るとスコープの範囲を超えたx-textは表示されないことが確認できます。

x-dataのスコープの意味
x-dataのスコープの意味

1つのHTMLページの中に複数のx-dataを宣言することも可能です。messageに異なる値を設定しています。


<body>
  <div x-data="{message: 'Hello World'}">
    <h1 x-text="message"></h1>
  </div>
  <div x-data="{message: 'Hello Alpine.js'}">
    <h1 x-text="message"></h1>
  </div>
</body>
1つのページで複数のx-dataを設定
1つのページで複数のx-dataを設定

messageに対してStringとしてJavaScriptのメソッドを実行することも可能です。


<h1 x-text="message.split('').reverse().join('')"></h1>
messageでメソッドを実行
messageでメソッドを実行

clickイベントの設定

clickイベントを利用して他の処理を行うことができます。ここではclickイベントを検知してmessageの文字列を別の文字列で上書きします。button要素を追加しclickイベントに対してイベントリスナーを設定することができます。clickイベントを設定する時はx-on:をclickの前に追加します。


<div x-data="{message: 'Hello World'}">
  <h1 x-text="message.split('').reverse().join('')"></h1>
  <button x-on:click="message = 'Hello Alpine.js'">クリック</button>
</div>

最初はHello Worldが逆に表示されいますがボタンをクリックするとmessageがHello Alpine.jsで上書きされます。

クリックイベントの設定
クリックイベントの設定

クリックイベントの設定をx-on:clickと記述しましたが@clickと記述することもできます。clickイベントを利用することでユーザはインタラクティブな処理を行うことができるようになります。


<button @click="message = 'Hello Alpine.js'">クリック

clickイベントとx-showの設定

Alpine.jsはclickイベントを使って要素の表示・非表示に利用することができます。表示・非表示といえば思い浮かぶのはドロップダウンメニューやモーダルなどではないでしょうか。

Alpine.jsでは要素の表示・非表示はx-showディレクティブを利用して制御します。x-showにはtrue or falseのbooleanを設定します。x-showの値がtrueであれば表示、falseであれば非表示となります。

x-dataの中でisShowプロパティを設定しtrueとします。h1タグにx-show=”isShow”を設定していますがisShowがfalseなのでh1タグは表示されません。


<div x-data="{isShow: false}">
  <h1 x-show="isShow">Hello World</h1>
</div>

isShowの値をfalseからtrueに変更するとHello Worldがブラウザに表示されます。


<div x-data="{isShow: true}">
  <h1 x-show="isShow">Hello World</h1>
</div>

clickイベントを使ってmessageの文字列を上書きできたようにisShowの値を更新できるようにclickイベントをボタンに設定します。ボタンを使ってisShowの値をtrueからfalse、falseからtrueに切り替えれるように設定を行っています。


<div x-data="{isShow: false}">
  <h1 x-show="isShow">Hello World</h1>
  <button @click="isShow = !isShow">表示・非表示</button>
</div>

最初はisShowがfalseなのでボタンのみが表示されている状態です。

最初はボタンのみ表示
最初はボタンのみ表示

”表示・非表示”ボタンをクリックするとHello Worldが表示されます。

表示された状態
表示された状態

ボタンにv-textと三項演算子を利用することでisShowの値によってボタンに表示される文字も切り替えることができます。


<button @click="isShow = !isShow" x-text="isShow ? '非表示':'表示'"></button>

Alpine.jsでは特別の初期設定を行うこともなく数行のコードのシンプルなコードの設定だけで表示・非表示を繰り返すことができるToggle機能を実装することが可能です。

非表示の時はstyle属性を利用してdisplya:noneが設定されます。

実践編ドロップダウンメニューを作成

ここまでのAlpine.jsの機能を利用してAlpine.jsが頻繁に利用させるドロップダウンメニューを作成することができます。Step By Stepでドロップダウンメニューを作成していきますがCSSにはtailwindcssを利用します。tailwindcssを使うとJavaScriptにおけるAlpine.jsのように手軽にCSSを設定することができます。

cdnを経由してtailwindcssを読み込むので以下のlinkタグをheaderタグに追加してください。


<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>
  <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.8.1/dist/alpine.js" defer></script>
  <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
</head>

ドロップダウンメニューをスクラッチから作成といってもほとんどの時間はドロップダウンメニューのCSSの設定に費やします。Alpine.jsを設定する処理はわずかです。それぐらいAlpine.jsは手軽に利用することができます。

ヘッダーとメインコンテンツ領域を作成

ヘッダーの背景色は黒に設定し、classでh-20の高さを設定します。


<body>
  <header class="bg-black h-20">
  </header>
  <main class="m-4">
    <p>ここからがコンテンツ</p>
  </main>
</body>
ヘッダーとメインコンテンツ領域
ヘッダーとメインコンテンツ領域

左側にログ、右側にドロップダウンメニューを表示させるためにflexを利用します。両端に表示させるためjustify-betweenを設定し、items-centerで中央揃えにしています。


<body>
  <header class="bg-black h-20 flex items-center justify-between p-4">
    <div class="text-white font-bold">ロゴ</div>
    <div>
      <button class="text-white">ドロップダウンメニュー</button>
    </div>
  </header>
  <main class="m-4">
    <p>ここからがコンテンツ</p>
  </main>
</body>
flexでヘッダーにメニューを追加
flexでヘッダーにメニューを追加

ドロップダウンにメニューを追加

ドロップダウンにメニューを追加します。3つのメニューをaタグで追加しています。


<div>
  <button class="text-white">ドロップダウンメニュー</button>
  <div>
    <a href="#">Link1</a>
    <a href="#">Link2</a>
    <a href="#">Link3</a>
  </div>
</div>

aタグはインライン要素なので横並びに表示されていますが背景色が黒でフォントの色もデフォルトの黒なので何も表示されませんがドロップメニューの下には存在しています。

メニューを追加したが見えない
メニューを追加したが見えない

メニューをインライン要素からブロック要素に変更します。メニューの外側のdivに背景色の白を設定します。


<div>
  <button class="text-white">ドロップダウンメニュー</button>
  <div class="bg-white">
    <a href="#" class="block">Link1</a>
    <a href="#" class="block">Link2</a>
    <a href="#" class="block">Link3</a>
  </div>
</div>

メニューが表示されるようになりました。

背景色とブロック要素へ変更
背景色とブロック要素へ変更

メニューにabsoluteを設定し基準となる要素である外側の要素にrelativeを設定します。


<div class="relative">
  <button class="text-white">ドロップダウンメニュー</button>
  <div class="bg-white absolute">
    <a href="#" class="block">Link1</a>
    <a href="#" class="block">Link2</a>
    <a href="#" class="block">Link3</a>
  </div>
</div>

ドロップダウンメニューが開い時の状態に近づいてきました。

positionの設定をabsoluteに
positionの設定をabsoluteに

padding、margin、メニューの横幅、表示位置が右側の0が記述になるように変更します。


<div class="relative">
  <button class="text-white">ドロップダウンメニュー</button>
  <div class="bg-white absolute w-56 right-0 my-2 py-1">
    <a href="#" class="block p-2">Link1</a>
    <a href="#" class="block p-2">Link2</a>
    <a href="#" class="block p-2">Link3</a>
  </div>
</div>

横幅やmargin, paddingを設定したのでよりドロップダウンメニューらしくなりました。

padding, marginなどを設定
padding, marginなどを設定

メニューと背景の境界がわかりくいのでshadowを設定し、hoverを設定してマウスが上にきた時は背景色が変わるように設定します。


<div class="relative">
  <button class="text-white">ドロップダウンメニュー</button>
  <div class="bg-white absolute w-56 right-0 my-2 py-1 shadow-lg rounded-md">
    <a href="#" class="block p-2 hover:bg-gray-100">Link1</a>
    <a href="#" class="block p-2 hover:bg-gray-100">Link2</a>
    <a href="#" class="block p-2 hover:bg-gray-100">Link3</a>
  </div>
</div>
ドロップダウンメニューのCSS設定完了
ドロップダウンメニューのCSS設定完了

ここまででドロップダウンメニューのCSS設定は完了です。

Alpine.jsの設定

ドロップダウンメニューをクリックするとメニューが表示・非表示を切り替えられるようにAlpine.jsの設定を行います。

ドロップダウンメニューの外側のdiv要素にx-dataを追加してisOpenプロパティを追加します。


<div class="relative" x-data="{ isOpen:false }">
  <button class="text-white">ドロップダウンメニュー</button>
  <div class="bg-white absolute w-56 right-0 my-2 py-1 shadow-lg rounded-md">
    <a href="#" class="block p-2 hover:bg-gray-100">Link1</a>
    <a href="#" class="block p-2 hover:bg-gray-100">Link2</a>
    <a href="#" class="block p-2 hover:bg-gray-100">Link3</a>
  </div>
</div>

メニューの外側のdiv要素にx-showでisOpenを設定します。


<div class="relative" x-data="{ isOpen:false }">
  <button class="text-white" @click="isOpen = !isOpen">ドロップダウンメニュー</button>
  <div class="bg-white absolute w-56 right-0 my-2 py-1 shadow-lg rounded-md" x-show="isOpen">
    <a href="#" class="block p-2 hover:bg-gray-100">Link1</a>
    <a href="#" class="block p-2 hover:bg-gray-100">Link2</a>
    <a href="#" class="block p-2 hover:bg-gray-100">Link3</a>
  </div>
</div>

x-dataのisOpenの値がfalseの場合のみメニューが非表示になるか確認してください。次はtrueにしてメニューが表示されればAlpine.jsは正常に動作しています。

ドロップダウンメニューをクリックすると表示・非表示が切り替わるようにclickイベントを設定します。

最初はメニューが閉じているのでクリックすると表示されます。

ドロップダウンメニューをクリック
ドロップダウンメニューをクリック

再度クリックするとドロップダウンメニューが非表示になります。

メニューがクリックで非表示に
メニューがクリックで非表示に

クリックした際にボタンの外側にラインが表示されるので表示しないようにbutton要素にoutline-noneのclassを設定します。


<div class="relative" x-data="{ isOpen:false }">
  <button class="text-white focus:outline-none" @click="isOpen = !isOpen">ドロップダウンメニュー</button>

away modifierの追加

設定したドロップダウンメニューはドロップダウンメニューボタンをクリックすると表示・非表示が切り替わります。x-dataの範囲外でクリックするとメニューが閉じるようにaway modifierを設定します。


<div class="relative" x-data="{ isOpen:false }" @click.away = "isOpen = false">
  <button class="text-white focus:outline-none" @click="isOpen = !isOpen">ドロップダウンメニュー</button>

開いた状態でドロップダウンメニュー以外の領域をクリックするとメニューが閉じます。

ドロップダウンメニューの全体
ドロップダウンメニューの全体

そのほかに@keydown.escapeを設定するとESCキーを押してもメニューを閉じさせることができます。


<div class="relative" x-data="{ isOpen:false }" @click.away = "isOpen = false" @keydown.escape="isOpen = false>

Alpine.jsを使ってドロップメニューの完成です。CSSの調整に時間がかかるだけでAlpine.jsの設定はほとんどなく簡単にドロップダウンメニューが作成できることができました。

アニメーションの設定

ドロップダウンメニューをクリックすると表示・非表示が即座に行われるのでアニメーションを設定してスムーズに切り替えが行えるようにします。x-trantisionディレクティブを利用しますが例がhttps://github.com/alpinejs/alpineに公開されているのでそれらの値を利用します。x-showディレクティブが設定されている要素に追加します。


<div class="relative" x-data="{ isOpen:false }" @click.away = "isOpen = false" @keydown.escape="isOpen = false">
  <button class="text-white focus:outline-none" @click="isOpen = !isOpen">ドロップダウンメニュー</button>
  <div class="bg-white absolute w-56 right-0 my-2 py-1 shadow-lg rounded-md" 
      x-show="isOpen"
      x-transition:enter="transition ease-out duration-300"
      x-transition:enter-start="opacity-0 transform scale-90"
      x-transition:enter-end="opacity-100 transform scale-100"
      x-transition:leave="transition ease-in duration-300"
      x-transition:leave-start="opacity-100 transform scale-100"
      x-transition:leave-end="opacity-0 transform scale-90"
    >
    <a href="#" class="block p-2 hover:bg-gray-100">Link1</a>
    <a href="#" class="block p-2 hover:bg-gray-100">Link2</a>
    <a href="#" class="block p-2 hover:bg-gray-100">Link3</a>
  </div>
</div>

設定後はゆっくりとメニューが表示・非表示されることが確認できます。

x-transtionディレクティブを複数記述しなくても細かな設定必要ない場合は以下のように設定することも可能です。2秒をかけてゆっくりと表示・非表示されます。


x-show.transition.duration.2000ms="isOpen"

設定方法についてはドキュメントにも記載されていますがここにも貼り付けておきます。各値を変更することで最適な値を見つけてください。

transitionの設定方法
transitionの設定方法

以上でアニメーションを入れたドロップダウンメニューのAlpine.jsの設定は完了です。

ドロップダウンメニューを作成を通してAlpine.jsの手軽さが実感できたのではないでしょうか。