zz-shopify-components
Version:
Reusable Shopify components for theme projects
280 lines (252 loc) • 8 kB
JavaScript
// 处理产品变体逻辑
class ProductSelector extends HTMLElement {
currentVariantId;
options;
root;
loading = false;
constructor() {
super();
}
connectedCallback() {
this.currentVariantId = JSON.parse(this.dataset.currentVariantId);
if (!this.currentVariantId) {
throw new Error('选择器未传入变体');
}
this.options = this.dataset.options;
this.init();
console.log(
'this.options, this.currentVariantId',
this.options,
this.currentVariantId
);
}
// 初始化 change 事件
init() {
this.bandOptionsSelector();
this.bandVersionSelector();
this.bindVersionEvents();
this.updatePrice();
}
bandOptionsSelector() {
const options = Array.from(
this.getElementsByClassName('product-normal-option')
);
options.forEach((el) => {
el.addEventListener('change', () => {
const { productUrl, variantId, sectionId } = el.dataset;
this.updateVersionSelector({ productUrl, variantId, sectionId });
this.updateURL(productUrl, variantId);
// 通知care更新
const careProducts = document.querySelector('care-product-list');
const careDialog = document.querySelector(
'product-detail-dialog-hovercare-choose'
);
if (careDialog) {
careDialog.updateSelectProduct();
}
if (careProducts) {
careProducts.updateSelectProduct();
}
});
});
}
bandVersionSelector() {
const versionOptions = Array.from(
this.getElementsByClassName('product-version-option')
);
versionOptions.forEach((el) => {
el.addEventListener('change', () => {
console.log('version change');
const { productUrl, variantId, sectionId, price, before } = el.dataset;
// this.currentVariantId = sectionId;
this.updateURL(productUrl, variantId);
});
});
}
// Version Selector 更新函数
updateVersionSelector({ productUrl, variantId, sectionId }) {
if (!variantId) {
console.warn('无效的变体 ID:', variantId);
return;
}
this.fetchAndUpdateDOM(productUrl, variantId, sectionId)
.then(() => this.updateURL(productUrl, variantId))
.catch((error) => console.error('更新选择器时出错:', error));
}
// Section rendering api 获取变体信息
async fetchAndUpdateDOM(productUrl, variantId, sectionId) {
try {
this.toggleLoading();
const currentElement = document.getElementById(
'product-version-selector'
);
const response = await fetch(
`${productUrl}?variant=${variantId}§ion_id=${sectionId}`
);
if (!response.ok) {
throw new Error(`网络错误:${response.status}`);
}
// 更新DOM
const responseText = await response.text();
const html = new DOMParser().parseFromString(responseText, 'text/html');
const newElement = html.getElementById('product-version-selector');
if (newElement && currentElement) {
currentElement.parentNode.replaceChild(newElement, currentElement);
this.bandVersionSelector();
this.bindVersionEvents();
} else {
console.warn('无法找到替换的 DOM 元素');
}
} catch (error) {
console.error('fetchAndUpdateDOM 错误:', error);
} finally {
this.toggleLoading();
}
}
// 更新路由参数和产品信息
updateURL(productUrl, variantId) {
window.history.replaceState({}, '', `${productUrl}?variant=${variantId}`);
this.currentVariantId = variantId;
this.updatePrice();
}
// 清理事件监听器或其他资源
disconnectedCallback() {
// const options = this.querySelectorAll('.product-version-option');
// options.forEach((el) => {
// el.removeEventListener('click', this.handleOptionClick);
// });
// console.log('ProductSelector 已被移除');
}
// 更新价格
updatePrice() {
const bar = document.querySelector('buy-now-bottom-bar');
if (bar) {
bar.updatePrice();
}
}
// loading 状态
toggleLoading() {
this.loading = !this.loading;
// const params = {
// duration: 0.3,
// opacity: this.loading ? 0.2 : 1,
// pointerEvents: this.loading ? 'none' : 'auto',
// };
// gsap.to('#product-version-selector-container', params);
// gsap.set('product-buy-now-selector', {
// pointerEvents: this.loading ? 'none' : 'auto',
// });
}
bindVersionEvents() {
const versionOptions = Array.from(
this.querySelectorAll(".version-selector-item > button.show-detail")
);
versionOptions.forEach((el) => {
el.addEventListener("click", (event) => {
// 处理详情按钮点击
const detailButton = event.target.closest(".show-detail");
if (detailButton) {
event.stopPropagation();
event.preventDefault();
const dialog = detailButton
.closest(".version-selector-item")
?.querySelector("dialog");
if (dialog) dialog.showModal();
return;
}
});
});
}
}
class ProductBuyNowCounter extends HTMLElement {
constructor() {
super();
this.input = null;
this.min = parseInt(this.getAttribute('min')) || 1;
this.max = parseInt(this.getAttribute('max')) || 99;
}
connectedCallback() {
this.input = this.querySelector('input');
if (this.input) {
this.init();
}
}
disconnectedCallback() {
const minusButton = this.querySelector('.minus');
const addButton = this.querySelector('.add');
if (minusButton) {
minusButton.removeEventListener('click', this.decrementValue);
}
if (addButton) {
addButton.removeEventListener('click', this.incrementValue);
}
if (this.input) {
this.input.removeEventListener('input', this.handleInput);
this.input.removeEventListener('blur', this.handleBlur);
}
}
init() {
const minusButton = this.querySelector('.minus');
const addButton = this.querySelector('.add');
if (minusButton) {
minusButton.addEventListener('click', () => this.decrementValue());
}
if (addButton) {
addButton.addEventListener('click', () => this.incrementValue());
}
this.input.addEventListener('input', (e) => this.handleInput(e));
this.input.addEventListener('blur', () => this.handleBlur());
}
handleInput() {
let value = parseInt(this.input.value);
if (isNaN(value)) {
this.input.value = '';
} else {
value = Math.min(Math.max(value, this.min), this.max);
this.input.value = value;
}
// this.toggleButtonState();
}
handleBlur() {
let value = parseInt(this.input.value);
if (isNaN(value) || value < this.min) {
this.input.value = this.min;
} else if (value > this.max) {
this.input.value = this.max;
}
this.updatePrice();
// this.toggleButtonState();
}
decrementValue() {
let value = parseInt(this.input.value) || this.min;
value = Math.max(value - 1, this.min);
this.input.value = value;
this.updatePrice();
// this.toggleButtonState();
}
incrementValue() {
let value = parseInt(this.input.value) || this.min;
value = Math.min(value + 1, this.max);
this.input.value = value;
this.updatePrice();
// this.toggleButtonState();
}
toggleButtonState() {
const minusButton = this.querySelector('.minus');
const addButton = this.querySelector('.add');
if (minusButton) {
minusButton.disabled = parseInt(this.input.value) <= this.min;
}
if (addButton) {
addButton.disabled = parseInt(this.input.value) >= this.max;
}
}
updatePrice() {
const bar = document.querySelector('buy-now-bottom-bar');
if (bar) {
bar.updatePrice();
}
}
}
customElements.define('product-buy-now-counter', ProductBuyNowCounter);
customElements.define('product-buy-now-selector', ProductSelector);