Open2
【Shopify】Dawn の JS を理解する

やること
Shopify の無料テーマ「Dawn」の JS ファイル内のコードを理解
目的
Shopify テーマ開発
Dawn 内のJSファイル
Dawn には以下の18ファイルが存在する。
cart-notification.js
cart.js
customer.js
details-disclosure.js
details-modal.js
facets.js
global.js
media-gallery.js
password-modal.js
pickup-availability.js
predictive-search.js
product-form.js
product-modal.js
product-model.js
quick-add.js
share.js
show-more.js
theme-editor.js

cart-notification.js
header.liquid
でスニペットのcart-notification.liquid
といっしょに読み込まれている。
cart-notification.js
class CartNotification extends HTMLElement {
// コンストラクタ関数を定義
constructor() {
super();
// id="cart-notification" のノードを this.notification に代入
this.notification = document.getElementById('cart-notification');
// <sticky-header></sticky-header> のノードを取得
this.header = document.querySelector('sticky-header');
// handleBodyClick 関数内で使用する this を CartNotification クラスに固定
this.onBodyClick = this.handleBodyClick.bind(this);
// escape キーが押されたら、CartNotification クラス内の close() 関数を実行
this.notification.addEventListener('keyup', (evt) => evt.code === 'Escape' && this.close());
// type="button" である<button></button>をすべて取得し、各ボタンがクリックされたとき close() を実行
this.querySelectorAll('button[type="button"]').forEach((closeButton) =>
closeButton.addEventListener('click', this.close.bind(this))
);
}
// open() 関数を定義
open() {
// コンストラクタ関数で取得した this.notification ノードに 'animate' と 'active' というクラスを追加
this.notification.classList.add('animate', 'active');
// CSS の transion(アニメーション)が終了したときに実行
this.notification.addEventListener('transitionend', () => {
// this.notification ノードに フォーカス(選択された状態)
this.notification.focus();
// global.js の関数 trapForcus()
trapFocus(this.notification);
}, { once: true });
// <body></body> をクリックした時、onBodyClick() を実行
document.body.addEventListener('click', this.onBodyClick);
}
// close() 関数を定義
close() {
// コンストラクタ関数で取得した this.notification ノードから active というクラスを削除
this.notification.classList.remove('active');
// <body></body> をクリックした時、open() 関数内の addEventListener で登録したイベントを解除
document.body.removeEventListener('click', this.onBodyClick);
// global.js の関数 removeTrapForcus()
removeTrapFocus(this.activeElement);
}
// renderContents() 関数を定義
// product-form.jsで使用されている
renderContents(parsedState) {
// this.cartItemKeyを定義
this.cartItemKey = parsedState.key;
// getSectionsToRender() の返り値を forEach で繰り返し処理
this.getSectionsToRender().forEach((section => {
// section オブジェクト内の id を持っているノードにHTMLを挿入
document.getElementById(section.id).innerHTML =
// section オブジェクト内の id と selector を getSectionInnerHTML() 関数に渡す
this.getSectionInnerHTML(parsedState.sections[section.id], section.selector);
}));
// this.header がある時 header.liquid ファイルで定義されている reveal() 関数を実行
// reveal() {
// this.header.classList.add('shopify-section-header-sticky', 'animate');
// this.header.classList.remove('shopify-section-header-hidden');
// }
if (this.header) this.header.reveal();
// open() 関数を実行
this.open();
}
// 3つのオブジェクトを持つ配列を返す getSectionsToRender() を定義
// renderContent() 内で使用
getSectionsToRender() {
return [
{
id: 'cart-notification-product',
selector: `[id="cart-notification-product-${this.cartItemKey}"]`,
},
{
id: 'cart-notification-button'
},
{
id: 'cart-icon-bubble'
}
];
}
// HTML ノードを返す getSectionInnerHTML() 関数を定義
// html と selector を受け取る
// selector のデフォルト値は shopify-section
// querySelector(selector)で取得した中身を返す
getSectionInnerHTML(html, selector = '.shopify-section') {
return new DOMParser()
.parseFromString(html, 'text/html')
.querySelector(selector).innerHTML;
}
// イベント(evt)を受けとり target にイベントを発生させたオブジェクトへの参照を代入する
// closest()は、CSSのクラスを受け取り、一番近い一致する要素を返す(自分自身 or 親要素)
// close()関数を走らせる
handleBodyClick(evt) {
const target = evt.target;
if (target !== this.notification && !target.closest('cart-notification')) {
const disclosure = target.closest('details-disclosure, header-menu');
this.activeElement = disclosure ? disclosure.querySelector('summary') : null;
this.close();
}
}
// activeElement に 引数の要素を代入する
setActiveElement(element) {
this.activeElement = element;
}
}
customElements.define('cart-notification', CartNotification);