クッキー詰め合わせ|巴裡 小川軒 新橋・目黒 オンラインショップ【公式通販】 – 巴裡小川軒
コンテンツにスキップ
直前
*/
(function () {
const cfg = window.__OGAWAKEN__ || {};
if (!cfg.hasHassouShuu) return; // 発送週オプションがある商品だけ
const CUSTOM_MSG = cfg.hassouMessage || "お歳暮商品は、同一発送週のお歳暮商品のみ同梱が可能です。";
window.__OGAWAKEN__ = cfg;
if (typeof cfg.muteErrors === 'undefined') cfg.muteErrors = false;
// あなたのテーマのエラー要素(スクショより)
const NATIVE_ERR_SEL = [
'.js-qty__error-message.errors.qty-error',
'.js-qty__error-message.qty-error',
'.qty-error[role="alert"]',
'[data-qty-error-message]'
].join(',');
// 1) ネイティブエラーを“この商品ページ内だけ”非表示にするためのスコープCSSを注入
(function injectScopedCSS(){
const style = document.createElement('style');
style.setAttribute('data-ogawaken', 'hide-native-error');
style.textContent = `
product-information ${NATIVE_ERR_SEL.replace(/,/g, ', product-information ')} {
display: none !important;
}
#ogawaken-error {
display: block;
margin-top: .5rem;
font-size: .9rem;
line-height: 1.5;
}
#ogawaken-error.hidden { display: none !important; }
/* 既存テーマとトーンを合わせたい場合はここで色等を微調整 */
#ogawaken-error.errors.qty-error { color: #b42318; } /* 例: 赤系 */
`;
document.head.appendChild(style);
})();
// 2) エラー表示用の専用ノードを、ネイティブエラーの直後に1回だけ作成
function ensureCustomErrorNode() {
// 最優先:qty-error のラッパー(スクショだと data-update-id="qty-error-buy_buttons")
const native = document.querySelector(NATIVE_ERR_SEL);
let anchor = native && native.parentElement ? native.parentElement : null;
// フォールバック:product-information 内
if (!anchor) anchor = document.querySelector('product-information') || document.body;
let node = document.getElementById('ogawaken-error');
if (!node) {
node = document.createElement('span');
node.id = 'ogawaken-error';
node.className = 'errors qty-error hidden';
node.setAttribute('role','alert');
node.setAttribute('aria-live','polite');
anchor.appendChild(node);
}
return node;
}
const errNode = ensureCustomErrorNode();
function showCustomError() {
if (!errNode) return;
errNode.textContent = CUSTOM_MSG;
errNode.classList.remove('hidden','active','error');
errNode.style.removeProperty('display');
errNode.removeAttribute('aria-hidden');
}
function clearCustomError() {
if (!errNode) return;
errNode.textContent = '';
errNode.classList.add('hidden');
}
// 3) バリアント変更 → いったんエラーを消す(ミュート短時間保持)
let muteTimer = null;
function engageMute() {
cfg.muteErrors = true;
clearTimeout(muteTimer);
muteTimer = setTimeout(() => { cfg.muteErrors = false; }, 400); // 必要なら 400→600ms 調整
clearCustomError();
}
function releaseMute() {
cfg.muteErrors = false;
clearTimeout(muteTimer);
}
// 4) “ネイティブ側にエラーが発生した”ことを検知して、専用ノードに表示
// (ネイティブは常に非表示なので見えないが、テキスト変化はフックできる)
const mo = new MutationObserver((muts) => {
if (!errNode) return;
// ネイティブ要素の追加や文字変更を見たら、ミュート中かで出し分け
muts.forEach(m => {
const nodes = [];
m.addedNodes && m.addedNodes.forEach(n => { if (n.nodeType===1) nodes.push(n); });
if (m.type === 'characterData' && m.target && m.target.parentElement) {
nodes.push(m.target.parentElement);
}
nodes.forEach(n => {
if (n.matches && (n.matches(NATIVE_ERR_SEL) || n.querySelector && n.querySelector(NATIVE_ERR_SEL))) {
if (cfg.muteErrors) clearCustomError(); else showCustomError();
}
});
});
});
mo.observe(document.documentElement, { childList: true, subtree: true, characterData: true });
// 初期にも、もしネイティブが内容を持っていたら専用ノードに反映
(function initMirrorFromNative(){
const native = document.querySelector(NATIVE_ERR_SEL);
if (!native) return;
const txt = (native.textContent || '').trim();
if (txt) { if (cfg.muteErrors) clearCustomError(); else showCustomError(); }
})();
// 5) 変更検出(select/radio/スウォッチ/独自イベント)
document.addEventListener('change', (e) => {
const t = e.target;
if (!t) return;
const name = (t.getAttribute('name') || '').toLowerCase();
if (name.startsWith('options[') || name === 'id' || t.classList.contains('single-option-selector')) {
engageMute();
}
}, true);
document.addEventListener('click', (e) => {
const el = e.target.closest('[data-product-options], .swatch, .product-options, [data-option-index]');
if (el) engageMute();
}, true);
window.addEventListener('variant:change', () => engageMute(), true);
document.addEventListener('product-form:variant-change', () => engageMute(), true);
// 6) 送信(Add to cart)検出でミュート解除 → 以降のエラーは専用ノードで表示
document.addEventListener('submit', (e) => {
const form = e.target;
if (form && form.action && form.action.indexOf('/cart/add') !== -1) releaseMute();
}, true);
document.addEventListener('click', (e) => {
const btn = e.target.closest('button, [role="button"], input[type="submit"], a');
if (!btn) return;
const name = (btn.getAttribute('name') || '').toLowerCase();
const type = (btn.getAttribute('type') || '').toLowerCase();
if (
name === 'add' ||
type === 'submit' ||
btn.classList.contains('product-form__submit') ||
btn.matches('[data-add-to-cart],[data-action="add-to-cart"],[data-product-form-submit]')
) {
releaseMute();
}
}, true);
document.addEventListener('keydown', (e) => { if (e.key === 'Enter') releaseMute(); }, true);
})();