スクリプトとイベントハンドリング
標準的なHTMLの<script>
タグを使用すれば、React・Svelte・Vue等のUIフレームワークを利用せずにインタラクティブ性をAstroコンポーネントへ追加できます。これによってブラウザ上で実行されるJavaScriptを送信してAstroコンポーネントに機能を追加できるようになります。
Astroで<script>
を使用する
セクションタイトル: Astroで<script>を使用する.astro
ファイル内に単数(もしくは複数)の<script>
タグを追加することによってクライアントサイドJavascriptを追加できます。
下の例では、<Hello />
コンポーネントをページに追加することでブラウザコンソール上にログメッセージを出力します。
<h1>Welcome, world!</h1>
<script> console.log('Welcome, browser console!');</script>
スクリプトのバンドル
セクションタイトル: スクリプトのバンドルデフォルトで<script
>タグはAstroで処理されます。
- すべてのインポートはバンドルされ、ローカルファイルやNodeモジュールをインポートできます。
- 処理されたスクリプトは、
type="module"
としてページの<head>
に挿入されます。 - TypeScriptを対応しており、TypeScriptファイルもインポートできます
- コンポーネントがページに複数回使われている場合、スクリプトは一度だけ含まれます。
<script> // 処理される! バンドルされる! TypeScriptサポート! // ローカルスクリプトやNodeモジュールのインポートも動作します。</script>
スクリプトがバンドルされることを避けるために、is:inline
ディレクティブを利用できます。
<script is:inline> // 書かれたとおりにレンダリングされます。 // ローカルインポートが解決されずに動作しません。 // コンポーネント内の場合、コンポーネントが利用されている箇所全てに繰り返しレンダリングされます。</script>
📚 <script>
タグで利用可能なディレクティブについてもっと知りたい場合は、テンプレートディレクティブ (EN)を参照してください。
スクリプトのロード
セクションタイトル: スクリプトのロードスクリプトを.js
や.ts
ファイルのように分離して書きたい場合や、他のサーバーから外部スクリプトを参照したい場合には<script>
タグのsrc
属性の参照を使えます。
ローカルスクリプトのインポート
セクションタイトル: ローカルスクリプトのインポートこれを使うケース: スクリプトがsrc
に存在している場合。
Astroはスクリプトのバンドルのルールに従って、ビルド、最適化、そしてページ内にスクリプトを追加します。
<!-- `src/scripts/local.js`への相対パス --><script src="../scripts/local.js"></script>
<!-- このローカルのTypeScriptファイルも動作します。 --><script src="./script-with-types.ts"></script>
外部スクリプトをロードする
セクションタイトル: 外部スクリプトをロードするこれを使うケース: JavaScriptファイルがpublic
ディレクトリに存在しているか、CDNの場合。
プロジェクトのsrc
フォルダ以外のスクリプトをロードするには、is:inline
ディレクティブを含めます。このアプローチでは、上記のようにスクリプトをインポートする際にAstroが提供しているJavaScriptの処理・バンドル・最適化はスキップされます
<!-- `public/my-script.js`スクリプトへの絶対パス --><script is:inline src="/my-script.js"></script>
<!-- リモートサーバー上にあるスクリプトへの完全なURL --><script is:inline src="https://my-analytics.com/script.js"></script>
共通スクリプトパターン
セクションタイトル: 共通スクリプトパターンonclick
や他のイベントをハンドリングする
セクションタイトル: onclickや他のイベントをハンドリングするいくつかのUIフレームワークではonclick={...}
(React/Preact)や@click="..."
(Vue)のように独自の構文でイベントを処理しています。Astroは標準的なHTMLにより近く、イベントのために独自の構文は利用しません。
その代わりに、ユーザーのインタラクションをハンドリングするために<script>
タグ内にaddEventListener
を利用します。
<button class="alert">Click me!</button>
<script> // ページ内から`alert`クラスを持つすべてのボタンを探す。 const buttons = document.querySelectorAll('button.alert');
// 各ボタンがクリックされたときのハンドリング buttons.forEach((button) => { button.addEventListener('click', () => { alert('Button was clicked!'); }); });</script>
カスタム要素を持つWebコンポーネント
セクションタイトル: カスタム要素を持つWebコンポーネントWebコンポーネント標準を使うことで独自の動作をするHTML要素を作成できます。.astro
コンポーネントでカスタム要素を定義するとUIフレームワークライブラリを利用しなくてもインタラクティブなコンポーネントを作ることができます。
下の例では、ハートボタンがクリックされた回数を記録し、その最新のカウント数を<span>
に更新する<astro-heart>
HTML要素を新しく定義しています。
<!-- カスタム要素 "astro-heart" でコンポーネント要素を囲みます。 --><astro-heart> <button aria-label="Heart">💜</button> × <span>0</span></astro-heart>
<script> // 新しいタイプのHTML要素の動作を定義する class AstroHeart extends HTMLElement { constructor() { super(); let count = 0;
const heartButton = this.querySelector('button'); const countSpan = this.querySelector('span');
// ボタンがクリックされるごとにカウントを更新する。 heartButton.addEventListener('click', () => { count++; countSpan.textContent = count; }); } }
// <astro-heart>要素としてAstroHeartクラスを利用することをブラウザに教える customElements.define('astro-heart', AstroHeart);</script>
ここでカスタム要素を利用するメリットは2つあります。
document.querySelector()
を使って全ページを検索する変わりに、this.querySelector()
を使えば検索範囲が現在のカスタム要素のインスタンスに限られます。これにより、一回で一つのコンポーネントだけを操作しやすくなります。<script>
は一度だけしか実行されませんが、ブラウザはページ上の<astro-heart>
を見つける度にカスタム要素のconstructor()
メソッドを実行します。これにより、ページで複数回コンポーネントを使用する場合でも一つのコンポーネントを安全に記述できます。
📚 web.dev’s Reusable Web Components guideとMDN’s introduction to custom elementsから多くのカスタム要素について学ぶことができます。
フロントマター変数をスクリプトに渡す
セクションタイトル: フロントマター変数をスクリプトに渡すAstroコンポーネントでは、---
フェンスの間にあるフロントマターのコードはサーバー上で実行され、ブラウザでは利用できません。サーバーからクライアントへ変数を渡すには、変数を保存しておきJavaScriptがブラウザで実行されたときに読み込む必要があります。
これを実現するための1つの手段はdata-*
attributesを利用してHTML出力に変数の値を保存する方法です。カスタム要素を含むスクリプトはブラウザ上にHTMLがロードされると要素のdataset
を利用することで、この属性を読み込むことができます。
下の例では、message
propをdata-message
属性に保存されているので、カスタム要素がthis.dataset.message
を読み込みブラウザ上で値を取得しています。
---const { message = 'Welcome, world!' } = Astro.props;---
<!-- message propをdata属性に保存する --><astro-greet data-message={message}> <button>Say hi!</button></astro-greet>
<script> class AstroGreet extends HTMLElement { constructor() { super();
// data属性からmessageを読み込む const message = this.dataset.message; const button = this.querySelector('button'); button.addEventListener('click', () => { alert(message); }); } }
customElements.define('astro-greet', AstroGreet);</script>
これでコンポーネントは何度でも利用でき、異なるmessageを表示できます
---import AstroGreet from '../components/AstroGreet.astro';---
<!-- messageの初期値「Welcome, world!」を利用する --><AstroGreet />
<!-- propsとしてカスタムmessageを渡す --><AstroGreet message="Lovely day to build components!" /><AstroGreet message="Glad you made it! 👋" />