Diff
checker
텍스트
텍스트
이미지
문서
Excel
폴더
Legal
Enterprise
데스크톱
요금제
로그인
데스크톱 앱 다운로드
텍스트 비교
두 텍스트 파일의 차이점을 찾아보세요
도구
기록
실시간 편집
공백 변경 숨기기
변경 없는 행 숨기기
줄바꿈 비활성화
레이아웃
나란히 보기
합쳐 보기
비교 단위
스마트
단어
글자
텍스트 스타일
모양 변경
구문 강조
언어 선택
제외
텍스트 변환
첫 변경으로
수정
Diffchecker Desktop
가장 안전하게 Diffchecker를 사용하는 방법. 데스크톱 앱을 사용하면 비교 데이터가 외부로 전송되지 않습니다!
데스크톱 앱 받기
cart.js-default-vs-modified
생성일
5개월 전
비교 결과 만료 없음
초기화
내보내기
공유
설명
83 삭제
행
총
삭제
글자
총
삭제
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
297 행
복사
89 추가
행
총
추가
글자
총
추가
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
314 행
복사
class CartRemoveButton extends HTMLElement {
class CartRemoveButton extends HTMLElement {
constructor() {
constructor() {
super();
super();
this.addEventListener('click', (event) => {
this.addEventListener('click', (event) => {
event.preventDefault();
event.preventDefault();
const cartItems = this.closest('cart-items') || this.closest('cart-drawer-items');
const cartItems = this.closest('cart-items') || this.closest('cart-drawer-items');
cartItems.updateQuantity(this.dataset.index, 0, event);
cartItems.updateQuantity(this.dataset.index, 0, event);
});
});
}
}
}
}
customElements.define('cart-remove-button', CartRemoveButton);
customElements.define('cart-remove-button', CartRemoveButton);
class CartItems extends HTMLElement {
class CartItems extends HTMLElement {
constructor() {
constructor() {
super();
super();
this.lineItemStatusElement =
this.lineItemStatusElement =
document.getElementById('shopping-cart-line-item-status') || document.getElementById('CartDrawer-LineItemStatus');
document.getElementById('shopping-cart-line-item-status') || document.getElementById('CartDrawer-LineItemStatus');
const debouncedOnChange = debounce((event) => {
const debouncedOnChange = debounce((event) => {
this.onChange(event);
this.onChange(event);
}, ON_CHANGE_DEBOUNCE_TIMER);
}, ON_CHANGE_DEBOUNCE_TIMER);
this.addEventListener('change', debouncedOnChange.bind(this));
this.addEventListener('change', debouncedOnChange.bind(this));
}
}
cartUpdateUnsubscriber = undefined;
cartUpdateUnsubscriber = undefined;
connectedCallback() {
connectedCallback() {
this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, (event) => {
this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, (event) => {
if (event.source === 'cart-items') {
if (event.source === 'cart-items') {
return;
return;
}
}
return this.onCartUpdate();
return this.onCartUpdate();
});
});
}
}
disconnectedCallback() {
disconnectedCallback() {
if (this.cartUpdateUnsubscriber) {
if (this.cartUpdateUnsubscriber) {
this.cartUpdateUnsubscriber();
this.cartUpdateUnsubscriber();
}
}
}
}
resetQuantityInput(id) {
resetQuantityInput(id) {
const input = this.querySelector(`#Quantity-${id}`);
const input = this.querySelector(`#Quantity-${id}`);
input.value = input.getAttribute('value');
input.value = input.getAttribute('value');
this.isEnterPressed = false;
this.isEnterPressed = false;
}
}
setValidity(event, index, message) {
setValidity(event, index, message) {
event.target.setCustomValidity(message);
event.target.setCustomValidity(message);
event.target.reportValidity();
event.target.reportValidity();
this.resetQuantityInput(index);
this.resetQuantityInput(index);
event.target.select();
event.target.select();
}
}
validateQuantity(event) {
validateQuantity(event) {
const inputValue = parseInt(event.target.value);
const inputValue = parseInt(event.target.value);
const index = event.target.dataset.index;
const index = event.target.dataset.index;
let message = '';
let message = '';
if (inputValue < event.target.dataset.min) {
if (inputValue < event.target.dataset.min) {
message = window.quickOrderListStrings.min_error.replace('[min]', event.target.dataset.min);
message = window.quickOrderListStrings.min_error.replace('[min]', event.target.dataset.min);
} else if (inputValue > parseInt(event.target.max)) {
} else if (inputValue > parseInt(event.target.max)) {
message = window.quickOrderListStrings.max_error.replace('[max]', event.target.max);
message = window.quickOrderListStrings.max_error.replace('[max]', event.target.max);
} else if (inputValue % parseInt(event.target.step) !== 0) {
} else if (inputValue % parseInt(event.target.step) !== 0) {
message = window.quickOrderListStrings.step_error.replace('[step]', event.target.step);
message = window.quickOrderListStrings.step_error.replace('[step]', event.target.step);
}
}
if (message) {
if (message) {
this.setValidity(event, index, message);
this.setValidity(event, index, message);
} else {
} else {
event.target.setCustomValidity('');
event.target.setCustomValidity('');
event.target.reportValidity();
event.target.reportValidity();
this.updateQuantity(
this.updateQuantity(
index,
index,
inputValue,
inputValue,
event,
event,
document.activeElement.getAttribute('name'),
document.activeElement.getAttribute('name'),
event.target.dataset.quantityVariantId
event.target.dataset.quantityVariantId
);
);
}
}
}
}
onChange(event) {
onChange(event) {
this.validateQuantity(event);
this.validateQuantity(event);
}
}
onCartUpdate() {
onCartUpdate() {
if (this.tagName === 'CART-DRAWER-ITEMS') {
if (this.tagName === 'CART-DRAWER-ITEMS') {
return fetch(`${routes.cart_url}?section_id=cart-drawer`)
return fetch(`${routes.cart_url}?section_id=cart-drawer`)
.then((response) => response.text())
.then((response) => response.text())
.then((responseText) => {
.then((responseText) => {
const html = new DOMParser().parseFromString(responseText, 'text/html');
const html = new DOMParser().parseFromString(responseText, 'text/html');
const selectors = ['cart-drawer-items', '.cart-drawer__footer'];
const selectors = ['cart-drawer-items', '.cart-drawer__footer'];
for (const selector of selectors) {
for (const selector of selectors) {
const targetElement = document.querySelector(selector);
const targetElement = document.querySelector(selector);
const sourceElement = html.querySelector(selector);
const sourceElement = html.querySelector(selector);
if (targetElement && sourceElement) {
if (targetElement && sourceElement) {
targetElement.replaceWith(sourceElement);
targetElement.replaceWith(sourceElement);
}
}
}
}
})
})
.catch((e) => {
.catch((e) => {
console.error(e);
console.error(e);
});
});
} else {
} else {
return fetch(`${routes.cart_url}?section_id=main-cart-items`)
return fetch(`${routes.cart_url}?section_id=main-cart-items`)
.then((response) => response.text())
.then((response) => response.text())
.then((responseText) => {
.then((responseText) => {
const html = new DOMParser().parseFromString(responseText, 'text/html');
const html = new DOMParser().parseFromString(responseText, 'text/html');
const sourceQty = html.querySelector('cart-items');
const sourceQty = html.querySelector('cart-items');
this.innerHTML = sourceQty.innerHTML;
this.innerHTML = sourceQty.innerHTML;
})
})
.catch((e) => {
.catch((e) => {
console.error(e);
console.error(e);
});
});
}
}
}
}
getSectionsToRender() {
getSectionsToRender() {
return [
return [
{
{
id: 'main-cart-items',
id: 'main-cart-items',
section: document.getElementById('main-cart-items').dataset.id,
section: document.getElementById('main-cart-items').dataset.id,
selector: '.js-contents',
selector: '.js-contents',
},
},
{
{
id: 'cart-icon-bubble',
id: 'cart-icon-bubble',
section: 'cart-icon-bubble',
section: 'cart-icon-bubble',
복사
복사됨
복사
복사됨
selector: '.
shopify-section
',
selector: '.
cart-count-bubble
',
},
},
{
{
id: 'cart-live-region-text',
id: 'cart-live-region-text',
section: 'cart-live-region-text',
section: 'cart-live-region-text',
selector: '.shopify-section',
selector: '.shopify-section',
},
},
{
{
id: 'main-cart-footer',
id: 'main-cart-footer',
section: document.getElementById('main-cart-footer').dataset.id,
section: document.getElementById('main-cart-footer').dataset.id,
selector: '.js-contents',
selector: '.js-contents',
},
},
];
];
}
}
복사
복사됨
복사
복사됨
updateQuantity(line, quantity, event, name, variantId) {
updateQuantity(line, quantity, event, name, variantId) {
const eventTarget = event.currentTarget instanceof CartRemoveButton ? 'clear' : 'change';
const eventTarget = event.currentTarget instanceof CartRemoveButton ? 'clear' : 'change';
const cartPerformanceUpdateMarker = CartPerformance.createStartingMarker(`${eventTarget}:user-action`);
const cartPerformanceUpdateMarker = CartPerformance.createStartingMarker(`${eventTarget}:user-action`);
복사
복사됨
복사
복사됨
this.enableLoading(line);
this.enableLoading(line);
복사
복사됨
복사
복사됨
const body = JSON.stringify({
// Optimistic UI update
line,
const quantityElement =
quantity,
document.getElementById(`Quantity-${line}`) || document.getElementById(`Drawer-quantity-${line}`);
sections: this.getSectionsToRender().map((section) => section.section),
if (quantityElement) quantityElement.value = quantity;
sections_url: window.location.pathname,
});
복사
복사됨
복사
복사됨
fetch(`${routes.cart_change_url}`, { ...fetchConfig(), ...{
body
} })
const
body
= JSON.stringify(
{
.then((response) =>
{
line,
return response.text();
quantity,
})
sections: this.getSectionsToRender().map((section
) =>
section.section),
.then((state
) =>
{
sections_url: window.location.pathname,
const parsedState = JSON.parse(state
);
}
);
복사
복사됨
복사
복사됨
CartPerformance.measure
(`${
eventTarget}:paint-updated-sections`, (
) => {
fetch
(`${
routes.cart_change_url}`, { ...fetchConfig(), body })
const
quantityElement =
.then((response) => response.text())
document.getElementById(`Quantity-${line}`) || document.getElementById(`Drawer-quantity-${line}`);
.then((state
) => {
const items = document.querySelectorAll('.cart-item'
);
const
parsedState = JSON.parse(state
);
복사
복사됨
복사
복사됨
if (parsedState.errors) {
CartPerformance.measure(`${eventTarget}:paint-updated-sections`, () => {
quantityElement.value = quantityElement.getAttribute('value');
// Handle errors
this.updateLiveRegions(line, parsedState.errors);
if (parsedState.errors) {
return;
if (quantityElement)
quantityElement.value = quantityElement.getAttribute('value');
}
this.updateLiveRegions(line, parsedState.errors);
return;
}
복사
복사됨
복사
복사됨
this.classList.toggle('is-
empty
', parsedState.item_count === 0);
// Update
empty
state classes
const cartDrawerWrapper = document.querySelector('cart-drawer');
const cartDrawerWrapper = document.querySelector('cart-drawer');
const cartFooter = document.getElementById('main-cart-footer');
const cartFooter = document.getElementById('main-cart-footer');
const isEmpty = parsedState.item_count === 0;
복사
복사됨
복사
복사됨
if (cartFooter) cartFooter.classList.toggle('is-empty',
parsedState.item_count === 0
);
this.classList.toggle('is-empty', isEmpty);
if (cartDrawerWrapper) cartDrawerWrapper.classList.toggle('is-empty',
parsedState.item_count === 0
);
if (cartFooter) cartFooter.classList.toggle('is-empty',
isEmpty
);
if (cartDrawerWrapper) cartDrawerWrapper.classList.toggle('is-empty',
isEmpty
);
복사
복사됨
복사
복사됨
this.getSectionsToRender().forEach((section) => {
// Update only existing sections
const elementToReplace =
this.getSectionsToRender().forEach((section) => {
document.
getElementById(section.id).
querySelector(section.selector) ||
const sectionHTML = parsedState.sections?.[section.section];
document.getElementById(section.id);
if (!sectionHTML) return;
elementToReplace.innerHTML = this.getSectionInnerHTML(
parsedState.sections[section.
section
],
const elementToReplace =
section.selector
document.
querySelector(section.selector) ||
document.getElementById(section.id);
);
if (!elementToReplace) return;
}
);
const updatedValue = parsedState.items[line - 1]
? parsedState.items[line - 1]
.quantity
: undefined;
elementToReplace.innerHTML = this.getSectionInnerHTML(
section
HTML,
section.selector
);
let message = '';
});
if (items.length === parsedState.items.length && updatedValue !== parseInt(quantityElement.value)) {
if (typeof
updatedValue ===
'
undefined
') {
// Update quantity messages
message =
window.cartStrings.error
;
const items = document.querySelectorAll('.cart-item'
);
} else {
const updatedValue = parsedState.items[line - 1]
?
.quantity
;
message =
window.cartStrings.quantityError.replace('[quantity]', updatedValue);
let message = '';
}
}
if (items.length === parsedState.items.length && updatedValue !== parseInt(quantityElement.value)) {
this.updateLiveRegions(line, message);
message =
updatedValue ===
undefined
?
window.cartStrings.error
:
window.cartStrings.quantityError.replace('[quantity]', updatedValue);
}
this.updateLiveRegions(line, message);
복사
복사됨
복사
복사됨
// Focus trapping after DOM paints
requestAnimationFrame(() => {
const lineItem =
const lineItem =
document.getElementById(`CartItem-${line}`) || document.getElementById(`CartDrawer-Item-${line}`);
document.getElementById(`CartItem-${line}`) || document.getElementById(`CartDrawer-Item-${line}`);
복사
복사됨
복사
복사됨
if (lineItem
&& lineItem
.querySelector(`[name="${name}"]`)
) {
cartDrawerWrapper
if (lineItem
?
.querySelector(`[name="${name}"]`)
&&
cartDrawerWrapper
) {
?
trapFocus(cartDrawerWrapper, lineItem.querySelector(`[name="${name}"]`))
trapFocus(cartDrawerWrapper, lineItem.querySelector(`[name="${name}"]`))
;
: lineItem.querySelector(`[name="${name}"]`).focus()
;
} else if (
isEmpty
&& cartDrawerWrapper) {
} else if (
parsedState.item_count === 0
&& cartDrawerWrapper) {
const emptyFocus = cartDrawerWrapper.querySelector('.drawer__inner-empty a');
trapFocus(cartDrawerWrapper.querySelector('.drawer__inner-empty'),
cartDrawerWrapper.querySelector('a')
);
if (emptyFocus)
trapFocus(cartDrawerWrapper.querySelector('.drawer__inner-empty'),
emptyFocus
);
} else if (document.querySelector('.cart-item') && cartDrawerWrapper) {
} else if (document.querySelector('.cart-item') && cartDrawerWrapper) {
trapFocus(cartDrawerWrapper, document.querySelector('.cart-item__name'));
trapFocus(cartDrawerWrapper, document.querySelector('.cart-item__name'));
복사
복사됨
복사
복사됨
} else if (lineItem?.querySelector(`[name="${name}"]`)) {
lineItem.querySelector(`[name="${name}"]`).focus();
}
}
});
});
복사
복사됨
복사
복사됨
});
복사
복사됨
복사
복사됨
publish(PUB_SUB_EVENTS.cartUpdate, {
source: 'cart-items',
cartData: parsedState,
variantId: variantId
});
publish(PUB_SUB_EVENTS.cartUpdate, {
})
source: 'cart-items',
.catch(() => {
cartData: parsedState,
this.querySelectorAll('.loading__spinner').forEach((overlay) => overlay.classList.add('hidden'));
variantId: variantId
,
const errors = document.getElementById('cart-errors') || document.getElementById('CartDrawer-CartErrors');
errors.textContent = window.cartStrings.error;
})
.finally(() => {
this.disableLoading(line);
CartPerformance.measureFromMarker(`${eventTarget}:user-action`, cartPerformanceUpdateMarker);
});
});
복사
복사됨
복사
복사됨
}
})
.catch(() => {
this.querySelectorAll('.loading__spinner').forEach((overlay) => overlay.classList.add('hidden'));
const errors = document.getElementById('cart-errors') || document.getElementById('CartDrawer-CartErrors');
if (errors) errors.textContent = window.cartStrings.error;
})
.finally(() => {
this.disableLoading(line);
CartPerformance.measureFromMarker(`${eventTarget}:user-action`, cartPerformanceUpdateMarker);
});
}
updateLiveRegions(line, message) {
updateLiveRegions(line, message) {
const lineItemError =
const lineItemError =
document.getElementById(`Line-item-error-${line}`) || document.getElementById(`CartDrawer-LineItemError-${line}`);
document.getElementById(`Line-item-error-${line}`) || document.getElementById(`CartDrawer-LineItemError-${line}`);
if (lineItemError) lineItemError.querySelector('.cart-item__error-text').textContent = message;
if (lineItemError) lineItemError.querySelector('.cart-item__error-text').textContent = message;
this.lineItemStatusElement.setAttribute('aria-hidden', true);
this.lineItemStatusElement.setAttribute('aria-hidden', true);
const cartStatus =
const cartStatus =
document.getElementById('cart-live-region-text') || document.getElementById('CartDrawer-LiveRegionText');
document.getElementById('cart-live-region-text') || document.getElementById('CartDrawer-LiveRegionText');
cartStatus.setAttribute('aria-hidden', false);
cartStatus.setAttribute('aria-hidden', false);
setTimeout(() => {
setTimeout(() => {
cartStatus.setAttribute('aria-hidden', true);
cartStatus.setAttribute('aria-hidden', true);
}, 1000);
}, 1000);
}
}
getSectionInnerHTML(html, selector) {
getSectionInnerHTML(html, selector) {
return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML;
return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML;
}
}
enableLoading(line) {
enableLoading(line) {
const mainCartItems = document.getElementById('main-cart-items') || document.getElementById('CartDrawer-CartItems');
const mainCartItems = document.getElementById('main-cart-items') || document.getElementById('CartDrawer-CartItems');
mainCartItems.classList.add('cart__items--disabled');
mainCartItems.classList.add('cart__items--disabled');
const cartItemElements = this.querySelectorAll(`#CartItem-${line} .loading__spinner`);
const cartItemElements = this.querySelectorAll(`#CartItem-${line} .loading__spinner`);
const cartDrawerItemElements = this.querySelectorAll(`#CartDrawer-Item-${line} .loading__spinner`);
const cartDrawerItemElements = this.querySelectorAll(`#CartDrawer-Item-${line} .loading__spinner`);
[...cartItemElements, ...cartDrawerItemElements].forEach((overlay) => overlay.classList.remove('hidden'));
[...cartItemElements, ...cartDrawerItemElements].forEach((overlay) => overlay.classList.remove('hidden'));
document.activeElement.blur();
document.activeElement.blur();
this.lineItemStatusElement.setAttribute('aria-hidden', false);
this.lineItemStatusElement.setAttribute('aria-hidden', false);
}
}
disableLoading(line) {
disableLoading(line) {
const mainCartItems = document.getElementById('main-cart-items') || document.getElementById('CartDrawer-CartItems');
const mainCartItems = document.getElementById('main-cart-items') || document.getElementById('CartDrawer-CartItems');
mainCartItems.classList.remove('cart__items--disabled');
mainCartItems.classList.remove('cart__items--disabled');
const cartItemElements = this.querySelectorAll(`#CartItem-${line} .loading__spinner`);
const cartItemElements = this.querySelectorAll(`#CartItem-${line} .loading__spinner`);
const cartDrawerItemElements = this.querySelectorAll(`#CartDrawer-Item-${line} .loading__spinner`);
const cartDrawerItemElements = this.querySelectorAll(`#CartDrawer-Item-${line} .loading__spinner`);
cartItemElements.forEach((overlay) => overlay.classList.add('hidden'));
cartItemElements.forEach((overlay) => overlay.classList.add('hidden'));
cartDrawerItemElements.forEach((overlay) => overlay.classList.add('hidden'));
cartDrawerItemElements.forEach((overlay) => overlay.classList.add('hidden'));
}
}
}
}
customElements.define('cart-items', CartItems);
customElements.define('cart-items', CartItems);
if (!customElements.get('cart-note')) {
if (!customElements.get('cart-note')) {
customElements.define(
customElements.define(
'cart-note',
'cart-note',
class CartNote extends HTMLElement {
class CartNote extends HTMLElement {
constructor() {
constructor() {
super();
super();
this.addEventListener(
this.addEventListener(
'input',
'input',
debounce((event) => {
debounce((event) => {
const body = JSON.stringify({ note: event.target.value });
const body = JSON.stringify({ note: event.target.value });
fetch(`${routes.cart_update_url}`, { ...fetchConfig(), ...{ body } }).then(() =>
fetch(`${routes.cart_update_url}`, { ...fetchConfig(), ...{ body } }).then(() =>
CartPerformance.measureFromEvent('note-update:user-action', event)
CartPerformance.measureFromEvent('note-update:user-action', event)
);
);
}, ON_CHANGE_DEBOUNCE_TIMER)
}, ON_CHANGE_DEBOUNCE_TIMER)
);
);
}
}
}
}
);
);
}
}
복사
복사됨
복사
복사됨
저장된 비교 결과
원본
파일 열기
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, event); }); } } customElements.define('cart-remove-button', CartRemoveButton); class CartItems extends HTMLElement { constructor() { super(); this.lineItemStatusElement = document.getElementById('shopping-cart-line-item-status') || document.getElementById('CartDrawer-LineItemStatus'); const debouncedOnChange = debounce((event) => { this.onChange(event); }, ON_CHANGE_DEBOUNCE_TIMER); this.addEventListener('change', debouncedOnChange.bind(this)); } cartUpdateUnsubscriber = undefined; connectedCallback() { this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, (event) => { if (event.source === 'cart-items') { return; } return this.onCartUpdate(); }); } disconnectedCallback() { if (this.cartUpdateUnsubscriber) { this.cartUpdateUnsubscriber(); } } resetQuantityInput(id) { const input = this.querySelector(`#Quantity-${id}`); input.value = input.getAttribute('value'); this.isEnterPressed = false; } setValidity(event, index, message) { event.target.setCustomValidity(message); event.target.reportValidity(); this.resetQuantityInput(index); event.target.select(); } validateQuantity(event) { const inputValue = parseInt(event.target.value); const index = event.target.dataset.index; let message = ''; if (inputValue < event.target.dataset.min) { message = window.quickOrderListStrings.min_error.replace('[min]', event.target.dataset.min); } else if (inputValue > parseInt(event.target.max)) { message = window.quickOrderListStrings.max_error.replace('[max]', event.target.max); } else if (inputValue % parseInt(event.target.step) !== 0) { message = window.quickOrderListStrings.step_error.replace('[step]', event.target.step); } if (message) { this.setValidity(event, index, message); } else { event.target.setCustomValidity(''); event.target.reportValidity(); this.updateQuantity( index, inputValue, event, document.activeElement.getAttribute('name'), event.target.dataset.quantityVariantId ); } } onChange(event) { this.validateQuantity(event); } onCartUpdate() { if (this.tagName === 'CART-DRAWER-ITEMS') { return fetch(`${routes.cart_url}?section_id=cart-drawer`) .then((response) => response.text()) .then((responseText) => { const html = new DOMParser().parseFromString(responseText, 'text/html'); const selectors = ['cart-drawer-items', '.cart-drawer__footer']; for (const selector of selectors) { const targetElement = document.querySelector(selector); const sourceElement = html.querySelector(selector); if (targetElement && sourceElement) { targetElement.replaceWith(sourceElement); } } }) .catch((e) => { console.error(e); }); } else { return fetch(`${routes.cart_url}?section_id=main-cart-items`) .then((response) => response.text()) .then((responseText) => { const html = new DOMParser().parseFromString(responseText, 'text/html'); const sourceQty = html.querySelector('cart-items'); this.innerHTML = sourceQty.innerHTML; }) .catch((e) => { console.error(e); }); } } getSectionsToRender() { return [ { id: 'main-cart-items', section: document.getElementById('main-cart-items').dataset.id, selector: '.js-contents', }, { id: 'cart-icon-bubble', section: 'cart-icon-bubble', selector: '.shopify-section', }, { id: 'cart-live-region-text', section: 'cart-live-region-text', selector: '.shopify-section', }, { id: 'main-cart-footer', section: document.getElementById('main-cart-footer').dataset.id, selector: '.js-contents', }, ]; } updateQuantity(line, quantity, event, name, variantId) { const eventTarget = event.currentTarget instanceof CartRemoveButton ? 'clear' : 'change'; const cartPerformanceUpdateMarker = CartPerformance.createStartingMarker(`${eventTarget}:user-action`); this.enableLoading(line); const body = JSON.stringify({ line, quantity, sections: this.getSectionsToRender().map((section) => section.section), sections_url: window.location.pathname, }); fetch(`${routes.cart_change_url}`, { ...fetchConfig(), ...{ body } }) .then((response) => { return response.text(); }) .then((state) => { const parsedState = JSON.parse(state); CartPerformance.measure(`${eventTarget}:paint-updated-sections`, () => { const quantityElement = document.getElementById(`Quantity-${line}`) || document.getElementById(`Drawer-quantity-${line}`); const items = document.querySelectorAll('.cart-item'); if (parsedState.errors) { quantityElement.value = quantityElement.getAttribute('value'); this.updateLiveRegions(line, parsedState.errors); return; } this.classList.toggle('is-empty', parsedState.item_count === 0); const cartDrawerWrapper = document.querySelector('cart-drawer'); const cartFooter = document.getElementById('main-cart-footer'); if (cartFooter) cartFooter.classList.toggle('is-empty', parsedState.item_count === 0); if (cartDrawerWrapper) cartDrawerWrapper.classList.toggle('is-empty', parsedState.item_count === 0); this.getSectionsToRender().forEach((section) => { const elementToReplace = document.getElementById(section.id).querySelector(section.selector) || document.getElementById(section.id); elementToReplace.innerHTML = this.getSectionInnerHTML( parsedState.sections[section.section], section.selector ); }); const updatedValue = parsedState.items[line - 1] ? parsedState.items[line - 1].quantity : undefined; let message = ''; if (items.length === parsedState.items.length && updatedValue !== parseInt(quantityElement.value)) { if (typeof updatedValue === 'undefined') { message = window.cartStrings.error; } else { message = window.cartStrings.quantityError.replace('[quantity]', updatedValue); } } this.updateLiveRegions(line, message); const lineItem = document.getElementById(`CartItem-${line}`) || document.getElementById(`CartDrawer-Item-${line}`); if (lineItem && lineItem.querySelector(`[name="${name}"]`)) { cartDrawerWrapper ? trapFocus(cartDrawerWrapper, lineItem.querySelector(`[name="${name}"]`)) : lineItem.querySelector(`[name="${name}"]`).focus(); } else if (parsedState.item_count === 0 && cartDrawerWrapper) { trapFocus(cartDrawerWrapper.querySelector('.drawer__inner-empty'), cartDrawerWrapper.querySelector('a')); } else if (document.querySelector('.cart-item') && cartDrawerWrapper) { trapFocus(cartDrawerWrapper, document.querySelector('.cart-item__name')); } }); publish(PUB_SUB_EVENTS.cartUpdate, { source: 'cart-items', cartData: parsedState, variantId: variantId }); }) .catch(() => { this.querySelectorAll('.loading__spinner').forEach((overlay) => overlay.classList.add('hidden')); const errors = document.getElementById('cart-errors') || document.getElementById('CartDrawer-CartErrors'); errors.textContent = window.cartStrings.error; }) .finally(() => { this.disableLoading(line); CartPerformance.measureFromMarker(`${eventTarget}:user-action`, cartPerformanceUpdateMarker); }); } updateLiveRegions(line, message) { const lineItemError = document.getElementById(`Line-item-error-${line}`) || document.getElementById(`CartDrawer-LineItemError-${line}`); if (lineItemError) lineItemError.querySelector('.cart-item__error-text').textContent = message; this.lineItemStatusElement.setAttribute('aria-hidden', true); const cartStatus = document.getElementById('cart-live-region-text') || document.getElementById('CartDrawer-LiveRegionText'); cartStatus.setAttribute('aria-hidden', false); setTimeout(() => { cartStatus.setAttribute('aria-hidden', true); }, 1000); } getSectionInnerHTML(html, selector) { return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML; } enableLoading(line) { const mainCartItems = document.getElementById('main-cart-items') || document.getElementById('CartDrawer-CartItems'); mainCartItems.classList.add('cart__items--disabled'); const cartItemElements = this.querySelectorAll(`#CartItem-${line} .loading__spinner`); const cartDrawerItemElements = this.querySelectorAll(`#CartDrawer-Item-${line} .loading__spinner`); [...cartItemElements, ...cartDrawerItemElements].forEach((overlay) => overlay.classList.remove('hidden')); document.activeElement.blur(); this.lineItemStatusElement.setAttribute('aria-hidden', false); } disableLoading(line) { const mainCartItems = document.getElementById('main-cart-items') || document.getElementById('CartDrawer-CartItems'); mainCartItems.classList.remove('cart__items--disabled'); const cartItemElements = this.querySelectorAll(`#CartItem-${line} .loading__spinner`); const cartDrawerItemElements = this.querySelectorAll(`#CartDrawer-Item-${line} .loading__spinner`); cartItemElements.forEach((overlay) => overlay.classList.add('hidden')); cartDrawerItemElements.forEach((overlay) => overlay.classList.add('hidden')); } } customElements.define('cart-items', CartItems); if (!customElements.get('cart-note')) { customElements.define( 'cart-note', class CartNote extends HTMLElement { constructor() { super(); this.addEventListener( 'input', debounce((event) => { const body = JSON.stringify({ note: event.target.value }); fetch(`${routes.cart_update_url}`, { ...fetchConfig(), ...{ body } }).then(() => CartPerformance.measureFromEvent('note-update:user-action', event) ); }, ON_CHANGE_DEBOUNCE_TIMER) ); } } ); }
수정본
파일 열기
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, event); }); } } customElements.define('cart-remove-button', CartRemoveButton); class CartItems extends HTMLElement { constructor() { super(); this.lineItemStatusElement = document.getElementById('shopping-cart-line-item-status') || document.getElementById('CartDrawer-LineItemStatus'); const debouncedOnChange = debounce((event) => { this.onChange(event); }, ON_CHANGE_DEBOUNCE_TIMER); this.addEventListener('change', debouncedOnChange.bind(this)); } cartUpdateUnsubscriber = undefined; connectedCallback() { this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, (event) => { if (event.source === 'cart-items') { return; } return this.onCartUpdate(); }); } disconnectedCallback() { if (this.cartUpdateUnsubscriber) { this.cartUpdateUnsubscriber(); } } resetQuantityInput(id) { const input = this.querySelector(`#Quantity-${id}`); input.value = input.getAttribute('value'); this.isEnterPressed = false; } setValidity(event, index, message) { event.target.setCustomValidity(message); event.target.reportValidity(); this.resetQuantityInput(index); event.target.select(); } validateQuantity(event) { const inputValue = parseInt(event.target.value); const index = event.target.dataset.index; let message = ''; if (inputValue < event.target.dataset.min) { message = window.quickOrderListStrings.min_error.replace('[min]', event.target.dataset.min); } else if (inputValue > parseInt(event.target.max)) { message = window.quickOrderListStrings.max_error.replace('[max]', event.target.max); } else if (inputValue % parseInt(event.target.step) !== 0) { message = window.quickOrderListStrings.step_error.replace('[step]', event.target.step); } if (message) { this.setValidity(event, index, message); } else { event.target.setCustomValidity(''); event.target.reportValidity(); this.updateQuantity( index, inputValue, event, document.activeElement.getAttribute('name'), event.target.dataset.quantityVariantId ); } } onChange(event) { this.validateQuantity(event); } onCartUpdate() { if (this.tagName === 'CART-DRAWER-ITEMS') { return fetch(`${routes.cart_url}?section_id=cart-drawer`) .then((response) => response.text()) .then((responseText) => { const html = new DOMParser().parseFromString(responseText, 'text/html'); const selectors = ['cart-drawer-items', '.cart-drawer__footer']; for (const selector of selectors) { const targetElement = document.querySelector(selector); const sourceElement = html.querySelector(selector); if (targetElement && sourceElement) { targetElement.replaceWith(sourceElement); } } }) .catch((e) => { console.error(e); }); } else { return fetch(`${routes.cart_url}?section_id=main-cart-items`) .then((response) => response.text()) .then((responseText) => { const html = new DOMParser().parseFromString(responseText, 'text/html'); const sourceQty = html.querySelector('cart-items'); this.innerHTML = sourceQty.innerHTML; }) .catch((e) => { console.error(e); }); } } getSectionsToRender() { return [ { id: 'main-cart-items', section: document.getElementById('main-cart-items').dataset.id, selector: '.js-contents', }, { id: 'cart-icon-bubble', section: 'cart-icon-bubble', selector: '.cart-count-bubble', }, { id: 'cart-live-region-text', section: 'cart-live-region-text', selector: '.shopify-section', }, { id: 'main-cart-footer', section: document.getElementById('main-cart-footer').dataset.id, selector: '.js-contents', }, ]; } updateQuantity(line, quantity, event, name, variantId) { const eventTarget = event.currentTarget instanceof CartRemoveButton ? 'clear' : 'change'; const cartPerformanceUpdateMarker = CartPerformance.createStartingMarker(`${eventTarget}:user-action`); this.enableLoading(line); // Optimistic UI update const quantityElement = document.getElementById(`Quantity-${line}`) || document.getElementById(`Drawer-quantity-${line}`); if (quantityElement) quantityElement.value = quantity; const body = JSON.stringify({ line, quantity, sections: this.getSectionsToRender().map((section) => section.section), sections_url: window.location.pathname, }); fetch(`${routes.cart_change_url}`, { ...fetchConfig(), body }) .then((response) => response.text()) .then((state) => { const parsedState = JSON.parse(state); CartPerformance.measure(`${eventTarget}:paint-updated-sections`, () => { // Handle errors if (parsedState.errors) { if (quantityElement) quantityElement.value = quantityElement.getAttribute('value'); this.updateLiveRegions(line, parsedState.errors); return; } // Update empty state classes const cartDrawerWrapper = document.querySelector('cart-drawer'); const cartFooter = document.getElementById('main-cart-footer'); const isEmpty = parsedState.item_count === 0; this.classList.toggle('is-empty', isEmpty); if (cartFooter) cartFooter.classList.toggle('is-empty', isEmpty); if (cartDrawerWrapper) cartDrawerWrapper.classList.toggle('is-empty', isEmpty); // Update only existing sections this.getSectionsToRender().forEach((section) => { const sectionHTML = parsedState.sections?.[section.section]; if (!sectionHTML) return; const elementToReplace = document.querySelector(section.selector) || document.getElementById(section.id); if (!elementToReplace) return; elementToReplace.innerHTML = this.getSectionInnerHTML(sectionHTML, section.selector); }); // Update quantity messages const items = document.querySelectorAll('.cart-item'); const updatedValue = parsedState.items[line - 1]?.quantity; let message = ''; if (items.length === parsedState.items.length && updatedValue !== parseInt(quantityElement.value)) { message = updatedValue === undefined ? window.cartStrings.error : window.cartStrings.quantityError.replace('[quantity]', updatedValue); } this.updateLiveRegions(line, message); // Focus trapping after DOM paints requestAnimationFrame(() => { const lineItem = document.getElementById(`CartItem-${line}`) || document.getElementById(`CartDrawer-Item-${line}`); if (lineItem?.querySelector(`[name="${name}"]`) && cartDrawerWrapper) { trapFocus(cartDrawerWrapper, lineItem.querySelector(`[name="${name}"]`)); } else if (isEmpty && cartDrawerWrapper) { const emptyFocus = cartDrawerWrapper.querySelector('.drawer__inner-empty a'); if (emptyFocus) trapFocus(cartDrawerWrapper.querySelector('.drawer__inner-empty'), emptyFocus); } else if (document.querySelector('.cart-item') && cartDrawerWrapper) { trapFocus(cartDrawerWrapper, document.querySelector('.cart-item__name')); } else if (lineItem?.querySelector(`[name="${name}"]`)) { lineItem.querySelector(`[name="${name}"]`).focus(); } }); }); publish(PUB_SUB_EVENTS.cartUpdate, { source: 'cart-items', cartData: parsedState, variantId: variantId, }); }) .catch(() => { this.querySelectorAll('.loading__spinner').forEach((overlay) => overlay.classList.add('hidden')); const errors = document.getElementById('cart-errors') || document.getElementById('CartDrawer-CartErrors'); if (errors) errors.textContent = window.cartStrings.error; }) .finally(() => { this.disableLoading(line); CartPerformance.measureFromMarker(`${eventTarget}:user-action`, cartPerformanceUpdateMarker); }); } updateLiveRegions(line, message) { const lineItemError = document.getElementById(`Line-item-error-${line}`) || document.getElementById(`CartDrawer-LineItemError-${line}`); if (lineItemError) lineItemError.querySelector('.cart-item__error-text').textContent = message; this.lineItemStatusElement.setAttribute('aria-hidden', true); const cartStatus = document.getElementById('cart-live-region-text') || document.getElementById('CartDrawer-LiveRegionText'); cartStatus.setAttribute('aria-hidden', false); setTimeout(() => { cartStatus.setAttribute('aria-hidden', true); }, 1000); } getSectionInnerHTML(html, selector) { return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML; } enableLoading(line) { const mainCartItems = document.getElementById('main-cart-items') || document.getElementById('CartDrawer-CartItems'); mainCartItems.classList.add('cart__items--disabled'); const cartItemElements = this.querySelectorAll(`#CartItem-${line} .loading__spinner`); const cartDrawerItemElements = this.querySelectorAll(`#CartDrawer-Item-${line} .loading__spinner`); [...cartItemElements, ...cartDrawerItemElements].forEach((overlay) => overlay.classList.remove('hidden')); document.activeElement.blur(); this.lineItemStatusElement.setAttribute('aria-hidden', false); } disableLoading(line) { const mainCartItems = document.getElementById('main-cart-items') || document.getElementById('CartDrawer-CartItems'); mainCartItems.classList.remove('cart__items--disabled'); const cartItemElements = this.querySelectorAll(`#CartItem-${line} .loading__spinner`); const cartDrawerItemElements = this.querySelectorAll(`#CartDrawer-Item-${line} .loading__spinner`); cartItemElements.forEach((overlay) => overlay.classList.add('hidden')); cartDrawerItemElements.forEach((overlay) => overlay.classList.add('hidden')); } } customElements.define('cart-items', CartItems); if (!customElements.get('cart-note')) { customElements.define( 'cart-note', class CartNote extends HTMLElement { constructor() { super(); this.addEventListener( 'input', debounce((event) => { const body = JSON.stringify({ note: event.target.value }); fetch(`${routes.cart_update_url}`, { ...fetchConfig(), ...{ body } }).then(() => CartPerformance.measureFromEvent('note-update:user-action', event) ); }, ON_CHANGE_DEBOUNCE_TIMER) ); } } ); }
비교하기