何度かフォーラムで質問に回答していたので、自分のブログにまとめておきたいと思います。調べて試して書いていますので間違っている部分があるかも知れずその点留意ください。
前提
- Shopify使用
- 販売チャネルで「購入ボタン」を追加し、カスタマイズ後のコードを任意のサイトに貼り付ける
- 管理画面でのカスタマイズでは対応できない範囲のカスタマイズを行う
購入ボタンの概要
- Shopifyの管理画面内でカスタマイズ可能
- 個別の商品のみとコレクション(複数商品が並んだ状態)で作成可能
- 表示情報はある程度表示非表示の変更が可能
- コード設置したページでは、JSによってiframeで購入ボタンのソースが表示されるため、コレクションなどは多少表示が遅れる印象
※管理画面では設定不可能なはずだがiframeを使わないようにする設定もあり - 基本的には「商品+商品名+価格+カートに入れるボタン」が表示され、カートに商品がある場合にはページ右端中央部にカートアイコンとカート内商品点数のみのカートを開くためのバッジがつく(ECのヘッダーによくあるカートアイコンのようなもの)
WordPressの場合はクラシックブロックに放り込むのが安全かもしれません。またはビジュアルエディタではなくコードエディタを使うか。
日本語情報が少ない場合の検索ワードは「Buy Button」となります。BuyもButtonも一般的すぎますが、「Shopify Buy Button」や、後述しますが「Shopify Buybutton.js」あたりを使えば探しやすいと思います。
基本参考情報
注意点
上記2つ目のヘルプに以下の注記がありますので、Shopifyのストア内に購入ボタンを使うのは非推奨です。
Shopifyでは、チェックアウトプロセスで問題が発生する可能性があるため、ShopifyオンラインストアまたはShopifyブログで購入ボタンの使用をおすすめしません。代わりに、カートを事前読み込みするためにパーマリンクの使用を検討してください。
またTwitterにて以下のようにサポートから情報が出ています。
ヘルプページ内の記載の通り、ストア内のページやブログへBuyButtonを設置なさることはお勧め出来ませんので、代替手段としてパーマリンクの使用(https://t.co/fEdDv7uOLP)をご検討下さい。(資料が英語のみとなり恐縮ですがご確認ください)ご指摘頂きありがとうございました! 2/2 -Mie
— Shopify Japan (ショッピファイと読みます!) (@ShopifyJP) April 6, 2021
購入ボタンのカスタマイズ
- Shopify BuyButton.js
- Shopify/buy-button-js: BuyButton.js is a highly customizable UI library for adding ecommerce functionality to any website.
基本的に上記のサイトでBuyButton.jsの情報を調べればカスタマイズ可能です。
BuyButton.jsは単体で購入ボタンを生成できるライブラリのようなので、実際には購入ボタンと同一ではないですが中身は同じはずです。
Shopify管理画面では以下に記載する部分のある程度の範囲が変更可能であり、カスタマイズの結果を反映した状態でコードが出力されます。そのため管理画面で対応可能な範囲であればJSを書き換える必要はありません。
つまりBuybutton.jsのドキュメントを利用してカスタマイズするのは、Shopify管理画面では対応できないカスタマイズを行うためということになります。
カスタマイズの基本
コンポーネントが用意されており、それを書き換えたり新たに追加することでカスタマイズすることが可能です。
個人的に利用することが多そうな部分をまとめておきたいと思います。今後内容が更新されるかもしれませんのであくまで現時点に限りますが。
大枠
options = {
product: {},
cart: {},
modal: {}, // configure the modal created by a product embed
productSet: {}, // configure a collection or set of products
toggle: {}, // configure the tab that toggles the cart open
modalProduct: {}, // configure the product within the modal
option: {}, // configure the variant option selectors within a product
lineItem: {}, // configure the individual line items within a cart
}
購入ボタンの構成要素の大枠です。ここの中に収まっているコンポーネントに対してカスマイズを行いますので、ここにないものを追加するのは無理だと思われます(未確認)。
styles
var styles = {
button: {
'color': 'red',
':hover': {
'color': 'orange'
}
}
}
デザイン変更する際の設定です。コンポーネントにstylesで値を渡すことで変更可能です。
なお、iframe内にCSSを読み込ませることもできる場合があり(試したところクロスオリジンで弾かれませんでした)、この件は以下のフォーラムで回答しています。
const addStyleSheet = () => {
// 以下の#collection-component-xxxxxはBuybutton用のコードの上部にあるdivのidを指定
const t = document.querySelector('#collection-component-xxxxx iframe');
const css = `<link rel="stylesheet" type="text/css" media="screen" href="https://example.com/example.css" />`;
t.contentWindow.document.getElementsByTagName('head')[0].insertAdjacentHTML('beforeend',css);
}
window.setTimeout(addStyleSheet, 2000);
フォーラムではsetTimeoutで無理やり動作させていますが、Buybutton.jsには以下のようなイベントが用意されていますし、この辺を使うか独自でタイミングをとって実行させるのが現実的ではと思います。
var events = {
'beforeInit': function (component) {}, // before component is initialized
'afterInit': function (component) {},
'beforeRender': function (component) {}, // before component is rendered, after it has a `model` defined
'afterRender': function (component) {},
'beforeDelegateEvents': function (component) {}, // before events are bound to the DOM
'afterDelegateEvents': function(component) {},
'beforeUpdateConfig': function(component) {}, // before configuration is updated (only relevant in uncommon customizations)
'afterUpdateConfig': function(component) {},
}
Product
var contents = {
img: true,
title: true,
variantTitle: false,
price: true,
options: true,
quantity: false, // determines whether to show any quantity inputs at all
quantityIncrement: false, // button to increase quantity
quantityDecrement: false, // button to decrease quantity
quantityInput: true, // input field to directly set quantity
button: true,
description: false,
}
商品単体のコンポーネントであり、後述するコレクションの内部の商品もこれと同一です。
var text = {
button: 'SHOP NOW',
outOfStock: 'Out of stock',
unavailable: 'Unavailable',
}
設定されているテキストです。
var events = {
addVariantToCart: function (product) {},
updateQuantity: function (product) {},
openModal: function (product) {},
openOnlineStore: function (product) {},
openCheckout: function (product) {},
}
イベントも用意されています。計測にも使えるのではと。
Cart
商品を追加した場合に表示されるカート画面部分が該当します。この画面はドロワーのように右からスライドして表示され、カートに入れた商品の点数変更や削除が可能です。
なお、CSSでposition:absolute;などを使って組まれており、新たに要素を追加するのはかなり大変そうなのでお勧めできません。
var contents = {
title: true,
lineItems: true,
footer: true,
note: false,
discounts: true,
},
var text = {
title: 'Cart',
empty: 'Your cart is empty.',
button: 'Checkout',
total: 'Total',
currency: 'CAD',
notice: 'Shipping and discount codes are added at checkout.',
noteDescription: 'Special instructions for seller',
},
var events = {
openCheckout: function (cart) {},
updateItemQuantity: function (cart) {},
}
LineItem component
カートに追加された商品が該当します。あまりいじる必要はないと思いますが、「カートではシンプルに画像なしにしたい」などの要望があればここを触ることになります。
var contents = {
image: true,
variantTitle: true,
title: true,
price: false,
priceWithDiscounts: true,
quantity: true,
quantityIncrement: true,
quantityDecrement: true,
quantityInput: true,
},
ProductSet component
コレクションの購入ボタンを作成した場合に該当します。前述のProductを含んだ形なので、ProductSet自体は枠の設定のようなものとなります。
var contents = {
products: true,
pagination: true,
}
var text = {
nextPageButton: 'Next page',
},
var events = {
loadNextPage: function (productSet) {},
}
Modal component
「カートに入れる」ボタンを押した際の動作として「商品の詳細を開く」が選ばれている場合に該当します。
ただし、「全表示レイアウト」を選択した場合や、「カートに入れる」ボタンを押して「チェックアウトに移動する」や「カートに商品を追加する」が選ばれているとモーダルは表示されません(多分)。
var events = {
closeModal: function (modal) {},
}
Toggle component
カート内に商品が表示した場合に画面右端中央部に表示されるボタンです。基本的にはカートアイコンとカート内の商品点数が表示されているはずです。
var contents = {
count: true,
icon: true,
title: false,
}
var text = {
title: 'cart', // not visible by default, but read to screen readers
},
Window component
Toggle内のチェックアウトボタンを押した際に表示される、チェックアウト用別ウィンドウが該当します。
var window = {
height: 600,
width: 600,
toolbar: 0,
scrollbars: 0,
status: 0,
resizable: 1,
left: 0,
top: 0,
center: 0,
createnew: 1,
location: 0,
menubar: 0,
onUnload: null,
},
Advanced Attributes
任意で新規のコンポーネントを追加することができます。
以下はドキュメントに書かれている「new」というバッジを商品名につけるためのサンプルコードです。しかしながら不完全で、このままでは商品名が消えますしCSSも当たりません。
var product = {
templates: {
title: '<h1 class=""> <span class="">NEW</span></h1>',
},
classes: {
newBadge: 'badge--new'
},
styles: {
newBadge: {
'background-color': 'red',
'border-radius': '10px',
'color': 'yellow'
}
}
}
そこで以下のように書き換える必要があります。
なおbuybutton.jsではなく購入ボタン用コードとして追加する形に書き換えています。
{{data.title}}などはドキュメントにありませんが、前出のGithub内を見ることで概ね利用法がわかると思います。
"product": {
"styles": {
"product": {
"newBadge": {
"background-color": "red",
"border-radius": "10px",
"color": "yellow"
}
},
"templates": {
"title": `<h1 class=""> <span class="{{data.classes.product.newBadge}}">NEW</span>{{data.title}}</h1>`,
},
"classes": {
"newBadge": 'badge--new'
}
}
},
iframeなしの設定
var options = {
product: {
iframe: false
},
cart: {
iframe: false
}
}
If you want direct control over the css in your components you can selectively opt out of the iframe sandboxing by passing iframe: false through to a component’s options. The component will then be rendered directly into the host DOM and you can target the selectors with CSS.
The following code will render the product and cart components directly in the host DOM, but will still render the cart toggle in an iframe.
iframeなしにした場合、購入ボタンを設置したサイトに購入ボタン用のCSSが反映されずそのまま出力されます。
そのためこのままでは設置サイト側のCSSの影響を受けてしまうので、設置サイト側でしっかりと設定する必要があります。
カートの開閉などのJSはiframe版と同様に動作しますが、CSSと同様にサイト側のJSが影響を与える可能性はあるかもしれません。
iframeなし手間がかかる反面、自由にレイアウトが可能となり、iframeではないためGoogle Analyticsなどで購入ボタンのクリックに関連する計測をする際に手間もかからず実装可能です。
初期値
orderをいじったりする際に初期値がわからないと対応できないため、Githubから引用します。今後も一定である保証はないので、あくまでこの時点での内容となりますが。
const defaults = {
product: {
iframe: true,
buttonDestination: 'cart',
isButton: false,
layout: 'vertical',
manifest: ['product', 'option'],
width: '280px',
order: [
'img',
'imgWithCarousel',
'title',
'variantTitle',
'price',
'options',
'quantity',
'button',
'buttonWithQuantity',
'description',
],
contents: {
img: true,
imgWithCarousel: false,
title: true,
variantTitle: false,
price: true,
unitPrice: true,
options: true,
quantity: false,
quantityIncrement: false,
quantityDecrement: false,
quantityInput: true,
button: true,
buttonWithQuantity: false,
description: false,
},
templates: productTemplates,
classes: {
wrapper: 'shopify-buy__product-wrapper',
product: 'shopify-buy__product',
img: 'shopify-buy__product__variant-img',
imgWrapper: 'shopify-buy__product-img-wrapper',
carousel: 'shopify-buy__carousel',
carouselNext: 'carousel-button--next',
carouselPrevious: 'carousel-button--previous',
carouselItem: 'shopify-buy__carousel-item',
carouselItemSelected: 'shopify-buy__carousel-item--selected',
blockButton: 'shopify-buy__btn--parent',
button: 'shopify-buy__btn',
buttonWrapper: 'shopify-buy__btn-wrapper',
title: 'shopify-buy__product__title',
prices: 'shopify-buy__product__price',
price: 'shopify-buy__product__actual-price',
compareAt: 'shopify-buy__product__compare-price',
unitPrice: 'shopify-buy__product__unit-price',
loweredPrice: 'shopify-buy__price--lowered',
variantTitle: 'shopify-buy__product__variant-title',
description: 'shopify-buy__product-description',
options: 'shopify-buy__product__variant-selectors',
disabled: 'shopify-buy__btn-disabled',
buttonBesideQty: 'shopify-buy__beside-quantity',
quantity: 'shopify-buy__quantity-container',
quantityInput: 'shopify-buy__quantity',
quantityButton: 'shopify-buy__btn--seamless',
quantityIncrement: 'shopify-buy__quantity-increment',
quantityDecrement: 'shopify-buy__quantity-decrement',
buttonWithQuantity: 'shopify-buy__btn-and-quantity',
quantityWithButtons: 'shopify-buy__quantity-with-btns',
vertical: 'shopify-buy__layout-vertical',
horizontal: 'shopify-buy__layout-horizontal',
},
text: {
button: 'ADD TO CART',
outOfStock: 'Out of stock',
unavailable: 'Unavailable',
unitPriceAccessibilityLabel: 'Unit price',
unitPriceAccessibilitySeparator: 'per',
regularPriceAccessibilityLabel: 'Regular price',
salePriceAccessibilityLabel: 'Sale price',
},
},
modalProduct: {
iframe: false,
layout: 'horizontal',
contents: {
img: true,
imgWithCarousel: false,
title: true,
variantTitle: false,
price: true,
unitPrice: true,
options: true,
button: true,
buttonWithQuantity: false,
quantity: false,
quantityIncrement: false,
quantityDecrement: false,
description: true,
},
order: [
'img',
'imgWithCarousel',
'title',
'variantTitle',
'price',
'options',
'buttonWithQuantity',
'button',
'description',
],
classes: {
wrapper: 'shopify-buy__modal-product-wrapper',
hasImage: 'has-image',
},
buttonDestination: 'cart',
text: {
button: 'ADD TO CART',
},
},
modal: {
iframe: true,
manifest: ['modal', 'product', 'option'],
classes: {
overlay: 'shopify-buy__modal-overlay',
modal: 'shopify-buy__modal',
contents: 'shopify-buy__modal-contents',
close: 'shopify-buy__btn--close',
wrapper: 'shopify-buy__modal-wrapper',
product: 'shopify-buy__product-modal',
img: 'shopify-buy__modal-img',
imgWithCarousel: 'shopify-buy__modal-img',
footer: 'shopify-buy__modal-footer',
footerWithImg: 'shopify-buy__modal-footer--has-img',
imgWithImg: 'shopify-buy__modal-img--has-img',
contentsWithImg: 'shopify-buy__modal-contents--has-img',
scrollContents: 'shopify-buy__modal-scroll-contents',
},
contents: {
contents: true,
},
order: ['contents'],
templates: modalTemplates,
},
productSet: {
iframe: true,
manifest: ['product', 'option', 'productSet'],
contents: {
title: false,
products: true,
pagination: true,
},
order: ['title', 'products', 'pagination'],
templates: {
title: '<h2 class="{{data.classes.productSet.title}}">{{data.collection.attrs.title}}</h2>',
products: '<div class="{{data.classes.productSet.products}}"></div>',
pagination: '<button class="{{data.classes.productSet.paginationButton}} {{data.nextButtonClass}}">{{data.text.nextPageButton}}</button>',
},
classes: {
wrapper: 'shopify-buy__collection-wrapper',
productSet: 'shopify-buy__collection',
title: 'shopify-buy__collection__title',
collection: 'shopify-buy__collection',
products: 'shopify-buy__collection-products',
product: 'shopify-buy__collection-product',
paginationButton: 'shopify-buy__collection-pagination-button shopify-buy__btn',
},
text: {
nextPageButton: 'Next page',
},
},
option: {
templates: optionTemplates,
contents: {
option: true,
},
order: ['option'],
classes: {
option: 'shopify-buy__option-select',
wrapper: 'shopify-buy__option-select-wrapper',
select: 'shopify-buy__option-select__select',
label: 'shopify-buy__option-select__label',
optionDisabled: 'shopify-buy__option--disabled',
optionSelected: 'shopify-buy__option--selected',
selectIcon: 'shopify-buy__select-icon',
hiddenLabel: 'visuallyhidden',
},
},
cart: {
iframe: true,
templates: cartTemplates,
startOpen: false,
popup: true,
manifest: ['cart', 'lineItem', 'toggle'],
contents: {
title: true,
lineItems: true,
footer: true,
note: false,
discounts: true,
},
order: ['title', 'lineItems', 'footer'],
classes: {
wrapper: 'shopify-buy__cart-wrapper',
cart: 'shopify-buy__cart',
header: 'shopify-buy__cart__header',
title: 'shopify-buy__cart__title',
lineItems: 'shopify-buy__cart-items',
footer: 'shopify-buy__cart-bottom',
discount: 'shopify-buy__cart__discount',
discountText: 'shopify-buy__cart__discount__text',
discountIcon: 'shopify-buy__cart__discount__text__icon',
discountAmount: 'shopify-buy__cart__discount__amount',
subtotalText: 'shopify-buy__cart__subtotal__text',
subtotal: 'shopify-buy__cart__subtotal__price',
notice: 'shopify-buy__cart__notice',
currency: 'shopify-buy__cart__currency',
button: 'shopify-buy__btn shopify-buy__btn--cart-checkout',
close: 'shopify-buy__btn--close',
cartScroll: 'shopify-buy__cart-scroll',
cartScrollWithDiscounts: 'shopify-buy__cart-scroll--discounts',
cartScrollWithCartNote: 'shopify-buy__cart-scroll--cart-note',
empty: 'shopify-buy__cart-empty-text',
note: 'shopify-buy__cart__note',
noteDescription: 'shopify-buy__cart__note__description',
noteTextArea: 'shopify-buy__cart__note__text-area',
},
text: {
title: 'Cart',
empty: 'Your cart is empty.',
button: 'CHECKOUT',
total: 'SUBTOTAL',
currency: 'CAD',
notice: 'Shipping and discount codes are added at checkout.',
noteDescription: 'Special instructions for seller',
closeAccessibilityLabel: 'Close cart',
},
},
lineItem: {
templates: lineItemTemplates,
contents: {
image: true,
variantTitle: true,
title: true,
price: false,
priceWithDiscounts: true,
quantity: true,
quantityIncrement: true,
quantityDecrement: true,
quantityInput: true,
},
order: [
'image',
'title',
'variantTitle',
'price',
'priceWithDiscounts',
'quantity',
],
classes: {
lineItem: 'shopify-buy__cart-item',
image: 'shopify-buy__cart-item__image',
variantTitle: 'shopify-buy__cart-item__variant-title',
itemTitle: 'shopify-buy__cart-item__title',
price: 'shopify-buy__cart-item__price',
priceWithDiscounts: 'shopify-buy__cart-item__price-and-discounts',
fullPrice: 'shopify-buy__cart-item__full-price',
discount: 'shopify-buy__cart-item__discount',
discountIcon: 'shopify-buy__cart-item__discount__icon',
quantity: 'shopify-buy__quantity-container clearfix',
quantityInput: 'shopify-buy__quantity shopify-buy__cart-item__quantity-input',
quantityButton: 'shopify-buy__btn--seamless',
quantityIncrement: 'shopify-buy__quantity-increment',
quantityDecrement: 'shopify-buy__quantity-decrement',
},
text: {
quantityInputAccessibilityLabel: 'Quantity',
quantityDecrementAccessibilityLabel: 'Reduce item quantity by one',
quantityIncrementAccessibilityLabel: 'Increase item quantity by one',
},
},
toggle: {
templates: toggleTemplates,
manifest: ['toggle'],
iframe: true,
sticky: true,
contents: {
count: true,
icon: true,
title: false,
},
order: [
'count',
'icon',
'title',
],
classes: {
wrapper: 'shopify-buy__cart-toggle-wrapper',
toggle: 'shopify-buy__cart-toggle',
title: 'shopify-buy__cart-toggle__title',
count: 'shopify-buy__cart-toggle__count',
icon: 'shopify-buy__icon-cart shopify-buy__icon-cart--side',
iconPath: 'shopify-buy__icon-cart__group',
},
text: {
title: 'cart',
},
},
window: {
height: 600,
width: 400,
toolbar: 0,
scrollbars: 1,
status: 0,
resizable: 1,
center: 0,
createnew: 1,
location: 0,
menubar: 0,
onUnload: null,
titlebar: 'yes',
},
};
カスタマイズ例
少しですが購入ボタンのカスタマイズ例を記載します。
商品名に改行を加える
ShopifyBuy.UI.onReady(client).then(function(ui) {
ui.createComponent('product', {
id: '111111111111',
node: document.getElementById('product-component-111111111111'),
moneyFormat: '%C2%A5%7B%7Bamount_no_decimals%7D%7D',
options: {
"product": {
"styles": {
"product": {
"@media (min-width: 601px)": {
"max-width": "calc(25% - 20px)",
"margin-left": "20px",
"margin-bottom": "50px"
}
}
},
"contents": {
"button": false,
"buttonWithQuantity": true,
},
"templates": {
"title": `<h1 class="shopify-buy__product__title" data-element="product.title">改行前のタイトル<br>改行後のタイトル</h1>`
},
"text": {
"button": "Add to cart"
}
},
上記は購入ボタンのコードの一部ですが、後から以下のコードを追加した状態です。
"templates": {
"title": `<h1 class="shopify-buy__product__title" data-element="product.title">改行前のタイトル<br>改行後のタイトル</h1>`
以下ページにあるtemplatesを利用して、用意されていたtitleコンポーネントの中身を直書きしつつ、brタグを入れています。ハードコーディングなので柔軟性は全くありません。
なお、この件はフォーラムで質問に回答したものでもあります。
任意の要素を追加する
商品情報の最後に任意の文字列を追加するサンプルコードです。なお完全なコードではなく部分かつ省略しているためこのままでは使えないコードです。
"product": {
"contents": {
"Mytext": true
}
"templates": {
"Mytext": `<p>任意の文章</p>`,
}
"order": [
"img",
"imgWithCarousel",
"title",
"variantTitle",
"price",
"options",
"quantity",
"button",
"buttonWithQuantity",
"description",
"Mytext"
],
}
流れとしては以下のような感じです。
- templatesで任意のキー(今回はMytext)を定義する
- contentsで表示をtrueにする
- orderで表示場所を決める
一見簡単なのですが、試作時にかなりの時間を取られたのがorderです。途中に入れる方法がわからなかったので、デフォルトの内容を列記してから追加しないと意図通りに表示ができませんでした。
ここはもっと簡単にできるのではと思うのですがまだ方法がわからず。
備考欄(カートメモ)を表示して見出しを変更する
"cart": {
"text": {
"noteDescription" : "Memo",
},
"contents": {
"note": true
}
},
上記のようにcartコンポーネントの中にcontentsとnoteをtrueで記載すれば備考欄が出力されます。ただこの部分はストアで購入ボタン作成時のカスタマイズで操作可能です。
textのnoteDescriptionは備考欄の直上に表示される見出しに対する指定です。この部分は直接コードを変更しないと操作できないと思われます。
なお、こうして出力した備考欄に入力された内容はちゃんと受注管理画面の「メモ」の箇所に登録されますので、Shopifyのストアと同様に運用できるはずです。
現状では購入ボタンではcart.attributesが使えないようので、日時指定などを行う場合はこの備考欄を使うしかないかもしれません。
templatesを使用時にWordpressのクラシックでエラーが出る場合
"templates": {
"title": '<h1>タイトル</h1>'
}
上記のようなtemplatesの記述があり、かつWordpressに記述する場合、エラーが出る可能性があります。その場合は以下のようにするとエラーは出ないはずです。
"templates": {
"title": `<h1>タイトル</h1>`
}
またはお勧めしませんが以下のように分解することでエラーを消せることを確認しました。
"templates": {
"title": '<' + 'h1' + '>' + 'タイトル' + '<' + '/h1' + '>'
}
特定のタイミングで何らかの動作を実行する
var events = {
'beforeInit': function (component) {}, // before component is initialized
'afterInit': function (component) {},
'beforeRender': function (component) {}, // before component is rendered, after it has a `model` defined
'afterRender': function (component) {},
'beforeDelegateEvents': function (component) {}, // before events are bound to the DOM
'afterDelegateEvents': function(component) {},
'beforeUpdateConfig': function(component) {}, // before configuration is updated (only relevant in uncommon customizations)
'afterUpdateConfig': function(component) {},
}
上記のイベントは、modalProductとlineItem以外で実行可能です。
ただしproductで確認したところbeforeDelegateEvents以降の4つの動作タイミングは確認できておらず、デフォルトでそのまま使えるのは上から4つという感じでした。
optionに関してはイベントのみならず他の設定も全て無視される模様です。
ドキュメントにも以下のように書かれています。
Configures the option selector contained within a product.
No configurable contents or text.
イベント実行順序
- beforeInit
- beforeRender
- afterRender
- afterInit
- beforeRender
- afterRender
上記以降はバリエーション変更ごとにbeforeRenderとafterRenderが発生します。
環境のせいなのか、afterInitの後に再度beforeRenderとafterRenderが発生する(つまり2回レンダリングしている)ため、「表示時に一回だけ動作させる」という処理が難しい状態です。
本来ならafterInitが適していると思うのですが、その後の再レンダリングでafterInitにて加工した内容が上書きされてしまうためです。
iframeへアクセスできるイベント
productにイベントを設定した際に、イベント内でiframeにアクセスできるのは以下の3つでした。
- beforeRenderafterRenderafterInit
しかしながら、実際に何か処理を行うのであれば結局afterRenderが唯一のタイミングになるかもしれません。
イベントではなく独自のJSのみでproductのiframeにアクセスする場合、loadイベントでは安定してアクセスできないので、setTimeout()を使って実行タイミングをずらすなど対策を講じる必要があります。
iframeへアクセスして要素を操作する
//省略
"events": {
'afterRender': function(component){
const iframe = component.node.childNodes[0];
const iframeDocument = iframe.contentWindow.document;
const select = iframeDocument.querySelector(".shopify-buy__option-select__select");
}
},
// 以下でもアクセス可能でした。
// const iframe = component.view.iframe.el;
//省略
各コンポーネント(オブジェクト?)内に前項のeventsを設定し、上記のような感じでiframeにアクセスできます。
あとはquerySelectorなどでいろいろ操作が可能です。
直接テンプレートを書き換える
前出のイベントを利用して以下のような形で対応可能です。「バリエーションを選択してください。」という1文をプルダウンの上に追加する例です。
"product": {
"events": {
'beforeInit': function(component){
const temp = `
<p>バリエーションを選択してください。</p>
<div class="{{data.classes.option.option}}" data-element="option.option">
<label for="{{data.selectId}}" class="{{data.classes.option.label}} {{#data.onlyOption}}{{data.classes.option.hiddenLabel}}{{/data.onlyOption}}" data-element="option.label">{{data.name}}</label>
<div class="{{data.classes.option.wrapper}}" data-element="option.wrapper">
<select id="{{data.selectId}}" class="{{data.classes.option.select}}" name="{{data.name}}" data-element="option.select">
{{#data.values}}
<option {{#selected}}selected{{/selected}} value="{{name}}">{{name}}</option>
{{/data.values}}
</select>
<svg class="{{data.classes.option.selectIcon}}" data-element="option.selectIcon" viewBox="0 0 24 24"><path d="M21 5.176l-9.086 9.353L3 5.176.686 7.647 12 19.382 23.314 7.647 21 5.176z"></path></svg>
</div>
</div>
`;
component.childTemplate.templates.option = temp;
}
なおテンプレートの初期状態は以下で確認可能です。
バリエーション部分の動作
<select id="{{data.selectId}}" class="{{data.classes.option.select}}" name="{{data.name}}" data-element="option.select">
{{#data.values}}
<option {{#selected}}selected{{/selected}} value="{{name}}">{{name}}</option>
{{/data.values}}
</select>
上記が該当しますが、buybutton.js側でループを作成して出力し、レンダリングの際にselectedを設定しています。
そのため以下のようにして任意のoptionを設定すると、追加したオプション選択時状態で買い物カゴに入れた際の動作がおかしくなります(ページ表示時のバリエーションでカートに入る模様)。
また、{{#data.values}}で作られたループと関連がないため、追加したオプションにselectedは設定されません。
<select id="{{data.selectId}}" class="{{data.classes.option.select}}" name="{{data.name}}" data-element="option.select">
{{#data.values}}
<option {{#selected}}selected{{/selected}} value="{{name}}">{{name}}</option>
{{/data.values}}
<option value="example">Example</option>
</select>
selectedの付与はJSでどうにかできるのですが、ボタン押下後の動作はどうにもならないと思いますので、optionを後で追加するようなカスタマイズは不可能かなと思います。
結び
ドキュメントはありますからある程度なら簡易にカスタマイズ可能です。
簡易に対応できない場合は、Githubでbuybutton.jsのコードを参照することで概ね解決できると思います。
より高度な購入ボタンを作るには
実際に組んだことがないので明確には分かりませんが、上記の記事などを見ますとbuybutton.jsで全て構築すればもう少し自由に実現可能なようです。
2人がこの記事を評価
役に立ったよという方は上の「記事を評価する」ボタンをクリックしてもらえると嬉しいです。
連投防止のためにCookie使用。SNSへの投稿など他サービスとの連動は一切ありません。