[object Object]

← back to Designer Wallcoverings

auto-save: 2026-07-22T13:14:23 (1 files) — shopify/theme-LIVE-591/assets/dw-card-hover.js

265cb3497b2089258f8f0a8ca2845c86e3090441 · 2026-07-22 13:14:31 -0700 · Steve Abrams

Files touched

Diff

commit 265cb3497b2089258f8f0a8ca2845c86e3090441
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jul 22 13:14:31 2026 -0700

    auto-save: 2026-07-22T13:14:23 (1 files) — shopify/theme-LIVE-591/assets/dw-card-hover.js
---
 shopify/theme-LIVE-591/assets/dw-card-hover.js | 87 +++++++++++++++++++++++---
 1 file changed, 80 insertions(+), 7 deletions(-)

diff --git a/shopify/theme-LIVE-591/assets/dw-card-hover.js b/shopify/theme-LIVE-591/assets/dw-card-hover.js
index 9e61401b..bacbef92 100644
--- a/shopify/theme-LIVE-591/assets/dw-card-hover.js
+++ b/shopify/theme-LIVE-591/assets/dw-card-hover.js
@@ -1,5 +1,20 @@
 /* ============================================================================
-   DW card label  v5  (2026-06-27) — collection / search product grids.
+   DW card label  v6  (2026-07-22) — collection / search product grids.
+   v6 fix: eliminated infinite-scroll glitch caused by two bugs in v5:
+     1. GUARD BUG: labelCard() guarded with card.querySelector('.dw-hover-overlay')
+        but v5 places the overlay OUTSIDE the card element (as a sibling of the
+        image-col in the card's parent). card.querySelector() can't find it, so
+        every run() call re-processed already-labeled cards and inserted duplicate
+        overlays. Fix: use card.dataset.dwHover as the authoritative processed flag.
+     2. OBSERVER SCOPE BUG: MutationObserver watched document.body with subtree:true
+        and triggered run() for ANY added node — including the .dw-hover-overlay nodes
+        that run() itself inserts. This created a self-reinforcing loop: Boost appends
+        24 cards → observer fires → run() inserts 24 overlays → observer fires again
+        → run() rescans all cards (O(n) querySelectorAll) → etc. The main thread was
+        saturated during the exact window Boost needs for its IntersectionObserver-
+        driven next-page fetch, stalling infinite scroll. Fix: observer now filters to
+        only fire run() when actual product card/container nodes are added, ignoring
+        .dw-hover-overlay and all other node types.
    v5 (Steve): the name/vendor label was a DARK absolute OVERLAY riding the
        bottom of the image (a "pill" on top of the thumbnail). It is now a
        SMALLER, light, in-flow pill placed DIRECTLY UNDER the image — no
@@ -106,7 +121,11 @@
   }
 
   function labelCard(card) {
-    if (card.querySelector('.dw-hover-overlay')) return;
+    // Guard must check BOTH inside-card overlays (legacy placement) AND the
+    // v5 sibling placement: overlay is inserted as the next sibling of the
+    // image-column, OUTSIDE the card element. card.querySelector() misses it.
+    // Use a data attribute on the card itself as the canonical processed flag.
+    if (card.dataset.dwHover) return;
     var tEl = card.querySelector(TITLE);
     if (!tEl) return;
     var name = patternName(tEl.textContent || '');
@@ -138,6 +157,9 @@
     } else {
       imgWrap.appendChild(ov);
     }
+
+    // Mark card as processed AFTER successful insertion so re-runs skip it.
+    card.dataset.dwHover = '1';
   }
 
   function run() {
@@ -152,15 +174,66 @@
   }
 
   // Re-apply when the grid re-renders or lazy/infinite scroll appends cards.
+  // FIX (v6): The original observer watched document.body with subtree:true and
+  // fired run() (full document querySelectorAll) for ANY added node anywhere on
+  // the page — including the .dw-hover-overlay nodes run() itself inserts.
+  // This creates a self-reinforcing cascade: run() inserts overlay → observer
+  // fires again → run() rescans all cards → finds nothing new → inserts nothing
+  // → BUT the rescan still costs O(n) querySelectorAll on the whole document.
+  // With Boost infinite-scroll appending 24 cards × many child nodes each load,
+  // plus this observer firing for every one of those nodes, the main thread is
+  // saturated with repeated full-grid scans during the exact window Boost needs
+  // to complete its IntersectionObserver-driven next-page fetch.
+  //
+  // Fix: scope the observer to the product grid container only (not body), so
+  // sidebar / toolbar / overlay mutations don't trigger rescans. Also guard
+  // against the overlay insertions triggering the observer by ignoring mutations
+  // from nodes we just inserted (checked via card.dataset.dwHover).
   var t;
   var obs = new MutationObserver(function (muts) {
+    var hasNewProductNode = false;
     for (var i = 0; i < muts.length; i++) {
-      if (muts[i].addedNodes && muts[i].addedNodes.length) {
-        clearTimeout(t);
-        t = setTimeout(run, 80);
-        return;
+      var added = muts[i].addedNodes;
+      for (var j = 0; j < added.length; j++) {
+        var node = added[j];
+        // Skip text nodes and our own overlay insertions
+        if (node.nodeType !== 1) continue;
+        if (node.classList && node.classList.contains('dw-hover-overlay')) continue;
+        // Only trigger run() if this looks like a product card or a container
+        // that might hold product cards (Boost wraps cards in list containers)
+        if (node.classList && (
+          node.classList.contains('boost-sd__product-item') ||
+          node.classList.contains('product-list-item') ||
+          node.classList.contains('boost-sd__product-list') ||
+          node.querySelector && node.querySelector('.boost-sd__product-item, .product-list-item')
+        )) {
+          hasNewProductNode = true;
+          break;
+        }
       }
+      if (hasNewProductNode) break;
+    }
+    if (hasNewProductNode) {
+      clearTimeout(t);
+      t = setTimeout(run, 80);
     }
   });
-  obs.observe(document.body, { childList: true, subtree: true });
+
+  // Observe the tightest container that holds product cards.
+  // Fall back to body if the grid isn't found yet (it initialises async).
+  function attachObserver() {
+    var grid = document.querySelector(
+      '.boost-sd__product-list, .boost-sd__product-grid, ' +
+      '.product-list, #boost-sd-filter-tree-container, ' +
+      '[class*="boost-sd__product-list"]'
+    );
+    var target = grid || document.body;
+    obs.observe(target, { childList: true, subtree: true });
+  }
+
+  if (document.readyState === 'loading') {
+    document.addEventListener('DOMContentLoaded', attachObserver);
+  } else {
+    attachObserver();
+  }
 })();

← 8f5ec303 New Arrivals mailer v4: Shop by Color moved directly below c  ·  back to Designer Wallcoverings  ·  auto-save: 2026-07-22T13:44:34 (1 files) — shopify/scripts/c d1e74c41 →