商品バリエーションの表示に使用されるvariant-radiosですが、商品詳細ページ内に、通常の位置+スクロール時の追従バナー内に二箇所設置したいです。(イメージは土屋鞄様の商品ページです)
→単にコピペして貼り付けると、バリエーションパラメータが付与されず、カートに選択したバリエーションが追加できません。
元コード(Grobal.jsのClass)を編集する必要があるのでしょうか?
ちなみにコピペしますと、バリエーションクリック時に以下のエラーが返ってきます
Uncaught TypeError: Cannot read properties of undefined (reading 'value')
at global.js?v=47979560903246619271709634806:739:91
at Array.map (<anonymous>)
at VariantRadiosForCartBanner.updateOptions (global.js?v=47979560903246619271709634806:738:30)
at VariantRadiosForCartBanner.onVariantChange (global.js?v=47979560903246619271709634806:576:10)
Grobal.js
class VariantSelects extends HTMLElement {
constructor() {
super();
this.addEventListener('change', this.onVariantChange);
}
onVariantChange() {
this.updateOptions();
this.updateMasterId();
this.toggleAddButton(true, '', false);
this.updatePickupAvailability();
this.removeErrorMessage();
if (!this.currentVariant) {
this.toggleAddButton(true, '', true);
this.setUnavailable();
} else {
this.updateMedia();
this.updateURL();
this.updateVariantInput();
this.renderProductInfo();
}
}
updateOptions() {
this.options = Array.from(this.querySelectorAll('select'), (select) => select.value);
}
updateMasterId() {
this.currentVariant = this.getVariantData().find((variant) => {
return !variant.options.map((option, index) => {
return this.options[index] === option;
}).includes(false);
});
}
updateMedia() {
if (!this.currentVariant) return;
if (!this.currentVariant.featured_media) return;
const newMedia = document.querySelector(
`[data-media-id="${this.dataset.section}-${this.currentVariant.featured_media.id}"]`
);
if (!newMedia) return;
const modalContent = document.querySelector(`#ProductModal-${this.dataset.section} .product-media-modal__content`);
const newMediaModal = modalContent.querySelector( `[data-media-id="${this.currentVariant.featured_media.id}"]`);
const parent = newMedia.parentElement;
if (parent.firstChild == newMedia) return;
modalContent.prepend(newMediaModal);
parent.prepend(newMedia);
this.stickyHeader = this.stickyHeader || document.querySelector('sticky-header');
if(this.stickyHeader) {
this.stickyHeader.dispatchEvent(new Event('preventHeaderReveal'));
}
window.setTimeout(() => { parent.querySelector('li.product__media-item').scrollIntoView({behavior: "smooth"}); });
}
updateURL() {
if (!this.currentVariant || this.dataset.updateUrl === 'false') return;
window.history.replaceState({ }, '', `${this.dataset.url}?variant=${this.currentVariant.id}`);
}
updateVariantInput() {
const productForms = document.querySelectorAll(`#product-form-${this.dataset.section}, #product-form-installment`);
productForms.forEach((productForm) => {
const input = productForm.querySelector('input[name="id"]');
input.value = this.currentVariant.id;
input.dispatchEvent(new Event('change', { bubbles: true }));
});
}
updatePickupAvailability() {
const pickUpAvailability = document.querySelector('pickup-availability');
if (!pickUpAvailability) return;
if (this.currentVariant && this.currentVariant.available) {
pickUpAvailability.fetchAvailability(this.currentVariant.id);
} else {
pickUpAvailability.removeAttribute('available');
pickUpAvailability.innerHTML = '';
}
}
removeErrorMessage() {
const section = this.closest('section');
if (!section) return;
const productForm = section.querySelector('product-form');
if (productForm) productForm.handleErrorMessage();
}
renderProductInfo() {
fetch(`${this.dataset.url}?variant=${this.currentVariant.id}§ion_id=${this.dataset.section}`)
.then((response) => response.text())
.then((responseText) => {
const id = `price-${this.dataset.section}`;
const html = new DOMParser().parseFromString(responseText, 'text/html')
const destination = document.getElementById(id);
const source = html.getElementById(id);
if (source && destination) destination.innerHTML = source.innerHTML;
const price = document.getElementById(`price-${this.dataset.section}`);
if (price) price.classList.remove('visibility-hidden');
this.toggleAddButton(!this.currentVariant.available, window.variantStrings.soldOut);
});
}
toggleAddButton(disable = true, text, modifyClass = true) {
const productForm = document.getElementById(`product-form-${this.dataset.section}`);
if (!productForm) return;
const addButton = productForm.querySelector('[name="add"]');
const addButtonText = productForm.querySelector('[name="add"] > span');
if (!addButton) return;
if (disable) {
addButton.setAttribute('disabled', true);
if (text) addButtonText.textContent = text;
} else {
addButton.removeAttribute('disabled');
addButtonText.textContent = window.variantStrings.addToCart;
}
if (!modifyClass) return;
}
setUnavailable() {
const button = document.getElementById(`product-form-${this.dataset.section}`);
const addButton = button.querySelector('[name="add"]');
const addButtonText = button.querySelector('[name="add"] > span');
const price = document.getElementById(`price-${this.dataset.section}`);
if (!addButton) return;
addButtonText.textContent = window.variantStrings.unavailable;
if (price) price.classList.add('visibility-hidden');
}
getVariantData() {
this.variantData = this.variantData || JSON.parse(this.querySelector('[type="application/json"]').textContent);
return this.variantData;
}
}
customElements.define('variant-selects', VariantSelects);
class VariantRadios extends VariantSelects {
constructor() {
super();
}
updateOptions() {
const fieldsets = Array.from(this.querySelectorAll('fieldset'));
this.options = fieldsets.map((fieldset) => {
return Array.from(fieldset.querySelectorAll('input')).find((radio) => radio.checked).value;
});
}
}
customElements.define('variant-radios', VariantRadios);