[FIX] website_sale_aplicoop: prevent grid destruction on event listener attachment
The _attachEventListeners() function was cloning the products-grid element without its children (cloneNode(false)) to remove duplicate event listeners. This destroyed all loaded products every time the function was called. Solution: Use a flag (_delegationListenersAttached) to prevent adding duplicate event listeners instead of cloning and replacing the grid node. This fixes the issue where products would disappear ~1-2 seconds after page load.
This commit is contained in:
parent
b15e9bc977
commit
b07b7dc671
6 changed files with 521 additions and 218 deletions
|
|
@ -7,25 +7,105 @@
|
|||
|
||||
console.log("[INFINITE_SCROLL] Script loaded!");
|
||||
|
||||
// Visual indicator for debugging
|
||||
if (typeof document !== "undefined") {
|
||||
try {
|
||||
var debugDiv = document.createElement("div");
|
||||
debugDiv.innerHTML = "[INFINITE_SCROLL LOADED]";
|
||||
debugDiv.style.position = "fixed";
|
||||
debugDiv.style.top = "0";
|
||||
debugDiv.style.right = "0";
|
||||
debugDiv.style.backgroundColor = "#00ff00";
|
||||
debugDiv.style.color = "#000";
|
||||
debugDiv.style.padding = "5px 10px";
|
||||
debugDiv.style.fontSize = "12px";
|
||||
debugDiv.style.zIndex = "99999";
|
||||
debugDiv.id = "infinite-scroll-debug";
|
||||
document.body.appendChild(debugDiv);
|
||||
} catch (e) {
|
||||
console.error("[INFINITE_SCROLL] Error adding debug div:", e);
|
||||
// DEBUG: Add MutationObserver to detect WHO is clearing the products grid
|
||||
(function () {
|
||||
var setupGridObserver = function () {
|
||||
var grid = document.getElementById("products-grid");
|
||||
if (!grid) {
|
||||
console.log("[MUTATION_DEBUG] products-grid not found yet, will retry...");
|
||||
setTimeout(setupGridObserver, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("[MUTATION_DEBUG] 🔍 Setting up MutationObserver on products-grid");
|
||||
console.log("[MUTATION_DEBUG] Initial child count:", grid.children.length);
|
||||
console.log("[MUTATION_DEBUG] Grid innerHTML length:", grid.innerHTML.length);
|
||||
|
||||
// Watch the grid itself for child changes
|
||||
var gridObserver = new MutationObserver(function (mutations) {
|
||||
mutations.forEach(function (mutation) {
|
||||
if (mutation.type === "childList") {
|
||||
if (mutation.removedNodes.length > 0) {
|
||||
console.log("[MUTATION_DEBUG] ⚠️⚠️⚠️ PRODUCTS REMOVED FROM GRID!");
|
||||
console.log(
|
||||
"[MUTATION_DEBUG] Removed nodes count:",
|
||||
mutation.removedNodes.length
|
||||
);
|
||||
console.log("[MUTATION_DEBUG] Stack trace:");
|
||||
console.trace();
|
||||
}
|
||||
if (mutation.addedNodes.length > 0) {
|
||||
console.log("[MUTATION_DEBUG] Products added:", mutation.addedNodes.length);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
gridObserver.observe(grid, { childList: true, subtree: false });
|
||||
|
||||
// ALSO watch the parent for the grid element itself being replaced/removed
|
||||
var parent = grid.parentElement;
|
||||
if (parent) {
|
||||
console.log(
|
||||
"[MUTATION_DEBUG] 🔍 Also watching parent element:",
|
||||
parent.tagName,
|
||||
parent.className
|
||||
);
|
||||
var parentObserver = new MutationObserver(function (mutations) {
|
||||
mutations.forEach(function (mutation) {
|
||||
if (mutation.type === "childList") {
|
||||
mutation.removedNodes.forEach(function (node) {
|
||||
if (
|
||||
node.id === "products-grid" ||
|
||||
(node.querySelector && node.querySelector("#products-grid"))
|
||||
) {
|
||||
console.log(
|
||||
"[MUTATION_DEBUG] ⚠️⚠️⚠️ PRODUCTS-GRID ELEMENT ITSELF WAS REMOVED!"
|
||||
);
|
||||
console.log("[MUTATION_DEBUG] Stack trace:");
|
||||
console.trace();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
parentObserver.observe(parent, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
// Poll to detect innerHTML being cleared (as backup)
|
||||
var lastChildCount = grid.children.length;
|
||||
setInterval(function () {
|
||||
var currentGrid = document.getElementById("products-grid");
|
||||
if (!currentGrid) {
|
||||
console.log("[MUTATION_DEBUG] ⚠️⚠️⚠️ GRID ELEMENT NO LONGER EXISTS!");
|
||||
console.trace();
|
||||
return;
|
||||
}
|
||||
var currentChildCount = currentGrid.children.length;
|
||||
if (currentChildCount !== lastChildCount) {
|
||||
console.log(
|
||||
"[MUTATION_DEBUG] 📊 Child count changed: " +
|
||||
lastChildCount +
|
||||
" → " +
|
||||
currentChildCount
|
||||
);
|
||||
if (currentChildCount === 0 && lastChildCount > 0) {
|
||||
console.log("[MUTATION_DEBUG] ⚠️⚠️⚠️ GRID WAS EMPTIED!");
|
||||
console.trace();
|
||||
}
|
||||
lastChildCount = currentChildCount;
|
||||
}
|
||||
}, 100);
|
||||
|
||||
console.log("[MUTATION_DEBUG] ✅ Observers attached (grid + parent + polling)");
|
||||
};
|
||||
|
||||
// Start observing as soon as possible
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", setupGridObserver);
|
||||
} else {
|
||||
setupGridObserver();
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
|
@ -45,10 +125,16 @@ if (typeof document !== "undefined") {
|
|||
config: {},
|
||||
|
||||
init: function () {
|
||||
console.log("[INFINITE_SCROLL] 🔧 init() called");
|
||||
|
||||
// Get configuration from page data
|
||||
var configEl = document.getElementById("eskaera-config");
|
||||
console.log("[INFINITE_SCROLL] eskaera-config element:", configEl);
|
||||
|
||||
if (!configEl) {
|
||||
console.log("[INFINITE_SCROLL] No eskaera-config found, lazy loading disabled");
|
||||
console.error(
|
||||
"[INFINITE_SCROLL] ❌ No eskaera-config found, lazy loading disabled"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -58,15 +144,33 @@ if (typeof document !== "undefined") {
|
|||
this.perPage = parseInt(configEl.getAttribute("data-per-page")) || 20;
|
||||
this.currentPage = parseInt(configEl.getAttribute("data-current-page")) || 1;
|
||||
|
||||
console.log("[INFINITE_SCROLL] Config loaded:", {
|
||||
orderId: this.orderId,
|
||||
searchQuery: this.searchQuery,
|
||||
category: this.category,
|
||||
perPage: this.perPage,
|
||||
currentPage: this.currentPage,
|
||||
});
|
||||
|
||||
// Check if there are more products to load from data attribute
|
||||
var hasNextAttr = configEl.getAttribute("data-has-next");
|
||||
this.hasMore = hasNextAttr === "true" || hasNextAttr === "True";
|
||||
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] hasMore=" +
|
||||
this.hasMore +
|
||||
" (data-has-next=" +
|
||||
hasNextAttr +
|
||||
")"
|
||||
);
|
||||
|
||||
if (!this.hasMore) {
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] No more products to load (has_next=" + hasNextAttr + ")"
|
||||
"[INFINITE_SCROLL] ⚠️ No more pages available, but keeping initialized for filter handling (has_next=" +
|
||||
hasNextAttr +
|
||||
")"
|
||||
);
|
||||
return;
|
||||
// Don't return - we need to stay initialized so realtime_search can call resetWithFilters()
|
||||
}
|
||||
|
||||
console.log("[INFINITE_SCROLL] Initialized with:", {
|
||||
|
|
@ -77,36 +181,50 @@ if (typeof document !== "undefined") {
|
|||
currentPage: this.currentPage,
|
||||
});
|
||||
|
||||
this.attachScrollListener();
|
||||
// Also keep the button listener as fallback
|
||||
this.attachFallbackButtonListener();
|
||||
// Only attach scroll listener if there are more pages to load
|
||||
if (this.hasMore) {
|
||||
this.attachScrollListener();
|
||||
this.attachFallbackButtonListener();
|
||||
} else {
|
||||
console.log("[INFINITE_SCROLL] Skipping scroll listener (no more pages)");
|
||||
}
|
||||
},
|
||||
|
||||
attachScrollListener: function () {
|
||||
var self = this;
|
||||
var scrollThreshold = 0.8; // Load when 80% scrolled
|
||||
var scrollThreshold = 300; // Load when within 300px of the bottom of the grid
|
||||
|
||||
window.addEventListener("scroll", function () {
|
||||
if (self.isLoading || !self.hasMore) {
|
||||
return;
|
||||
}
|
||||
|
||||
var scrollHeight = document.documentElement.scrollHeight;
|
||||
var scrollTop = window.scrollY;
|
||||
var clientHeight = window.innerHeight;
|
||||
var scrollPercent = (scrollTop + clientHeight) / scrollHeight;
|
||||
var grid = document.getElementById("products-grid");
|
||||
if (!grid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (scrollPercent >= scrollThreshold) {
|
||||
// Calculate distance from bottom of grid to bottom of viewport
|
||||
var gridRect = grid.getBoundingClientRect();
|
||||
var gridBottom = gridRect.bottom;
|
||||
var viewportBottom = window.innerHeight;
|
||||
var distanceFromBottom = gridBottom - viewportBottom;
|
||||
|
||||
// Load more if we're within threshold pixels of the grid bottom
|
||||
if (distanceFromBottom <= scrollThreshold && distanceFromBottom > 0) {
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] Scroll threshold reached, loading next page"
|
||||
"[INFINITE_SCROLL] Near grid bottom (distance: " +
|
||||
Math.round(distanceFromBottom) +
|
||||
"px), loading next page"
|
||||
);
|
||||
self.loadNextPage();
|
||||
}
|
||||
});
|
||||
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] Scroll listener attached (threshold:",
|
||||
scrollThreshold * 100 + "%)"
|
||||
"[INFINITE_SCROLL] Scroll listener attached (threshold: " +
|
||||
scrollThreshold +
|
||||
"px from grid bottom)"
|
||||
);
|
||||
},
|
||||
|
||||
|
|
@ -134,20 +252,53 @@ if (typeof document !== "undefined") {
|
|||
/**
|
||||
* Reset infinite scroll to page 1 with new filters and reload products.
|
||||
* Called by realtime_search when filters change.
|
||||
*
|
||||
* WARNING: This clears the grid! Only call when filters actually change.
|
||||
*/
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] Resetting with filters: search=" +
|
||||
"[INFINITE_SCROLL] ⚠️⚠️⚠️ resetWithFilters CALLED - search=" +
|
||||
searchQuery +
|
||||
" category=" +
|
||||
categoryId
|
||||
);
|
||||
console.trace("[INFINITE_SCROLL] ⚠️⚠️⚠️ WHO CALLED resetWithFilters? Call stack:");
|
||||
|
||||
this.searchQuery = searchQuery || "";
|
||||
this.category = categoryId || "0";
|
||||
// Normalize values: empty string to "", null to "0" for category
|
||||
var newSearchQuery = (searchQuery || "").trim();
|
||||
var newCategory = (categoryId || "").trim() || "0";
|
||||
|
||||
// CHECK IF VALUES ACTUALLY CHANGED before clearing grid!
|
||||
if (newSearchQuery === this.searchQuery && newCategory === this.category) {
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] ✅ NO CHANGE - Skipping reset (values are identical)"
|
||||
);
|
||||
return; // Don't clear grid if nothing changed!
|
||||
}
|
||||
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] 🔥 VALUES CHANGED - Old: search=" +
|
||||
this.searchQuery +
|
||||
" category=" +
|
||||
this.category +
|
||||
" → New: search=" +
|
||||
newSearchQuery +
|
||||
" category=" +
|
||||
newCategory
|
||||
);
|
||||
|
||||
this.searchQuery = newSearchQuery;
|
||||
this.category = newCategory;
|
||||
this.currentPage = 0; // Set to 0 so loadNextPage() increments to 1
|
||||
this.isLoading = false;
|
||||
this.hasMore = true;
|
||||
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] After normalization: search=" +
|
||||
this.searchQuery +
|
||||
" category=" +
|
||||
this.category
|
||||
);
|
||||
|
||||
// Update the config element data attributes for consistency
|
||||
var configEl = document.getElementById("eskaera-config");
|
||||
if (configEl) {
|
||||
|
|
@ -155,26 +306,58 @@ if (typeof document !== "undefined") {
|
|||
configEl.setAttribute("data-category", this.category);
|
||||
configEl.setAttribute("data-current-page", "1");
|
||||
configEl.setAttribute("data-has-next", "true");
|
||||
console.log("[INFINITE_SCROLL] Updated eskaera-config attributes");
|
||||
}
|
||||
|
||||
// Clear the grid and reload from page 1
|
||||
var grid = document.getElementById("products-grid");
|
||||
if (grid) {
|
||||
console.log("[INFINITE_SCROLL] 🗑️ CLEARING GRID NOW!");
|
||||
grid.innerHTML = "";
|
||||
console.log("[INFINITE_SCROLL] Grid cleared");
|
||||
}
|
||||
|
||||
// Load first page with new filters
|
||||
console.log("[INFINITE_SCROLL] Calling loadNextPage()...");
|
||||
this.loadNextPage();
|
||||
},
|
||||
|
||||
loadNextPage: function () {
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] 🚀 loadNextPage() CALLED - currentPage=" +
|
||||
this.currentPage +
|
||||
" isLoading=" +
|
||||
this.isLoading +
|
||||
" hasMore=" +
|
||||
this.hasMore
|
||||
);
|
||||
|
||||
if (this.isLoading || !this.hasMore) {
|
||||
console.log("[INFINITE_SCROLL] ❌ ABORTING - already loading or no more pages");
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.isLoading = true;
|
||||
this.currentPage += 1;
|
||||
|
||||
// Only increment if we're not loading first page (currentPage will be 0 after reset)
|
||||
if (this.currentPage === 0) {
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] ✅ Incrementing from 0 to 1 (first page after reset)"
|
||||
);
|
||||
this.currentPage = 1;
|
||||
} else {
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] ✅ Incrementing page " +
|
||||
this.currentPage +
|
||||
" → " +
|
||||
(this.currentPage + 1)
|
||||
);
|
||||
this.currentPage += 1;
|
||||
}
|
||||
|
||||
console.log(
|
||||
"[INFINITE_SCROLL] Loading page",
|
||||
"[INFINITE_SCROLL] 📡 About to fetch page",
|
||||
this.currentPage,
|
||||
"for order",
|
||||
this.orderId
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue