Shopifyのカスタマイズに役立つJavaScriptの基本概念として「Webコンポーネント」を解説。再利用性・保守性の高いUI設計の鍵となる仕組みをやさしく紹介します。
Shopifyストアをカスタマイズするうえで、JavaScriptは非常に重要な役割を担っています。
テーマエディタやLiquidだけでは実現できない、動きのある表現やユーザー操作への反応などを実装する際に、JavaScriptの知識が求められます。
とくに多く使われるのが「カートまわり」の機能ではないでしょうか。商品をカートに追加したり、数量を変更したり、非同期でカート情報を取得・更新するなど、ユーザー体験を左右する重要なシーンです。
今回から数回にわたって、Shopifyで使われるJavaScriptの中でも「cart.js」の理解に役立つ基本概念を解説していきます。
第1回は、カート以外の場面でも活躍する「Web Components(ウェブコンポーネント)」に注目して、どのような場面で使われ、どのような仕組みで動いているのかをわかりやすくご紹介します。
そもそもなぜJavaScriptが多用されるの?
Shopifyには、独自のプログラミング言語のLiquidがありますが、なぜJavaScriptが多用されるのでしょうか。
その理由は、Liquidだけでは完結できない場面が多いためです。
以下の記事に、理由と具体的な活用シーンをまとめましたので、ぜひチェックしてみてください。
ShopifyではLiquidがあるのになぜJavaScriptが多用されるの?
Webコンポーネントとは
Webコンポーネントの代表的なメソッドをご紹介する前に、まずは「Webコンポーネントとは何か?」をあらためて整理しておきましょう。
Webコンポーネントとは、JavaScriptで作成された“仕様書”のようなもので、ある部品(コンポーネント)をどのように表示し、どんな機能を持たせるかを定義する技術です。見た目と動作をセットでひとまとめにできるため、「再利用可能なUIパーツ」として注目されています。
たとえば、ポップアップ用のWebコンポーネントをJavaScriptで定義し、HTMLファイル内では次のように呼び出して使用します。
<my-modal></my-modal>
このように、HTMLタグとして挿入するだけで、背景の黒い幕や中央のボックス、開閉アニメーションなどが自動的に実現されます。デザインや動作を毎回書かずに済むのが、大きなメリットです。
なぜわざわざWebコンポーネントを?
Webコンポーネントの使い方を学んでいると、ふと頭をよぎる疑問があります。
「わざわざJavaScriptでコンポーネントを定義しなくても、普通にHTMLで書けばいいのでは?」というものです。
たしかに、単発のモーダルやスライダーを1回だけ使うのであれば、HTML+CSS+JSをそのまま書く方法でも問題はありません。むしろシンプルで直感的です。ですが、Webサイトやアプリケーションの規模が大きくなってくると、その方法ではいくつかの課題が出てきます。
たとえば、同じパーツを複数ページにわたって何度も使う場合、そのたびにHTML・CSS・JSをコピー&ペーストするのは効率が悪く、保守の手間も増えてしまいます。また、他のパーツとCSSが衝突してレイアウトが崩れる、といったトラブルも少なくありません。
こうした問題を解決するために登場したのが、Webコンポーネントという仕組みです。見た目・機能・データ構造をひとまとめにして、タグ1つで呼び出せる「自作タグ」を実現するこの技術は、大規模なUI設計において非常に強力な味方になります。
「cart.js」にWebコンポーネントが使用される理由
Shopifyの「cart.js」では、カート内の各商品を動的に追加・更新・削除するため、柔軟なUI設計が求められます。
商品数や金額といった情報は、HTMLの「data-attribute」を使って要素ごとに管理されており、JavaScript側でそれを読み取って処理する構造になっています。
こうした動きは、見た目・機能・状態を一体で扱えるWebコンポーネントの設計思想と非常に相性が良く、保守性や再利用性の高い実装につながります。
Webコンポーネントに欠かせない「Shadow DOM」とは?
Webコンポーネントの仕組みの中で、欠かせない存在が「Shadow DOM(シャドウDOM)」です。これは、コンポーネント内に作られる“独立した小さなDOMツリー”のことを指します。
通常のHTMLでは、CSSやJavaScriptはすべての要素に対して影響を与える可能性があります。たとえば、あるクラス名に対してスタイルを定義した場合、意図せず別の要素にもそのスタイルが適用されてしまうことがあります。
しかし、Shadow DOM内にある要素は、外部のCSSやJavaScriptの影響を一切受けません。また、逆にShadow DOM内で定義されたスタイルやスクリプトも、外部には漏れない仕組みになっています。
このように、Shadow DOMを使うことで、外部と完全に切り離された“カプセル化されたUI部品”を作ることができるのです。Webコンポーネントの強みである「安全性」と「再利用性」は、このShadow DOMによって実現されているといっても過言ではありません。
Webコンポーネントの基本構造
Webコンポーネントを書くときは、だいたい以下のような流れで構成されています。
①まず、コンポーネントを宣言します: 「この名前でこんな部品を作りますよ」という宣言です。
②次に、上述したShadow DOM(シャドウDOM)を作成します:これは「この部品の中だけで完結する空間」を作るイメージです。WebコンポーネントにおいてShadow DOMは必須ではありませんが、ほぼほぼ使われると理解していいと思います。
③Shadow DOMの中にHTMLやCSSを入れます。
④Webコンポーネントを再現させるオリジナルのタグ名を登録します(例:
)。
⑤HTMLにタグを書くと、さっき定義した中身が表示されます。
以下は、サンプルコードです。
<script>
// ① Webコンポーネントの設計(宣言)をする
class MyComponent extends HTMLElement {
constructor() {
super(); // お作法として必ず書くものです
// ② Shadow DOMという「この部品の中だけで完結する空間」を用意する
this.attachShadow({ mode: 'open' });
}
// ③ このタグが画面に置かれたときの処理
connectedCallback() {
this.shadowRoot.innerHTML = `
<style>
p {
background: #f5f5f5;
padding: 10px;
border-radius: 4px;
}
</style>
<p>こんにちは、これはWebコンポーネントです!</p>
`;
}
}
// ④ 実際のタグ名として登録する
customElements.define('my-component', MyComponent);
</script>
<!-- ⑤ HTMLで使えるようになる -->
<my-component></my-component>
Webコンポーネントメソッド一覧
メソッド名 | 説明 |
---|---|
constructor() |
カスタムエレメントのインスタンスが生成されたときに最初に呼び出されます。初期設定やイベントリスナーの登録などに使用します。 |
connectedCallback() |
カスタムエレメントがDOMに初めて接続されたときに呼び出されます。要素の描画やデータのフェッチに適しています。 |
disconnectedCallback() |
カスタムエレメントがDOMから切断されたときに呼び出されます。イベントリスナーの解除やタイマーのクリアなど、クリーンアップ処理に利用します。 |
adoptedCallback() |
カスタムエレメントが別のドキュメント(例: iframe)に移動されたときに呼び出されます。 |
attributeChangedCallback(name, oldValue, newValue) |
監視対象の属性(observedAttributes で指定)の値が変更されたときに呼び出されます。属性変更に応じて要素の振る舞いを更新するのに使います。 |
static get observedAttributes() |
attributeChangedCallback を機能させるために、監視対象とする属性名を文字列の配列で返します。 |
customElements.define(tagName, classDefinition, options) |
新しいカスタムエレメントをブラウザに登録します。独自のHTMLタグとして利用できるようになります。 |
実際のコード
では、多くのShopifyテーマの「cart.js」に実際に載っているコードの例を見ていきましょう。
class CartRemoveButton extends HTMLElement {
constructor() {
super();
this.addEventListener('click', (event) => {
event.preventDefault();
const cartItems = this.closest('cart-items') || this.closest('cart-drawer-items');
cartItems.updateQuantity(this.dataset.index, 0);
});
}
}
customElements.define('cart-remove-button', CartRemoveButton);
これは、カートに入っている特手のアイテムを削除するWebコンポーネントです。
①class CartRemoveButton extends HTMLElement
:「CartRemoveButton」という、HTML要素を生成するWebコンポーネントを定義
②constructor()
:コンポーネントが最初に動くときの準備
③this.addEventListener('click', (event) => { ... });
:「this」は「このコンポーネント(ここでは削除ボタン)」。クリックイベントが発生した時の処理をまとめている。(ここでは、特定のアイテムを削除)
event.preventDefault()
で、イベント発生時にリロードを防止。
④const cartItems = this.closest('cart-items') || this.closest('cart-drawer-items');
:クリックイベントが発生したら、cart-items
または cart-drawer-items
という名前の親要素を探して、cartItems
に格納
⑤cartItems.updateQuantity(this.dataset.index, 0);
:各削除ボタンが属する親要素のアイテムの数を「0」にする。詳しい解説は割愛しますが、updateQuantity()
は予め「cart.js」の中で宣言されたメソッドです。
⑥customElements.define('cart-remove-button', CartRemoveButton)
:これが最後のステップで、Webコンポーネントをブラウザに登録。
***
今回は、Shopifyのカスタマイズにおいて欠かせないJavaScriptのWebコンポーネントの基本構造や活用シーンについてご紹介しました。
JavaScriptとLiquidをうまく組み合わせることで、より柔軟で再利用性の高いUI設計が可能になります。
ShopifyのJavaScript理解を一歩深めたい方は、ぜひ引き続きチェックしてみてください。