From bfb4921ac063fbcf54e9cb8b2af119b9d23d92ac Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Tue, 26 May 2026 05:13:36 +0000 Subject: [PATCH] fix: virtual scroll flickering on inventory scroll - Batch scroll renders with requestAnimationFrame to avoid multiple DOM updates per frame - Add will-change, contain and content-visibility CSS for smoother compositing - Add cache-bust to virtual-scroll.js --- pos/static/css/inventory.css | 14 ++++++++++++++ pos/static/js/virtual-scroll.js | 34 ++++++++++++++++++++++----------- pos/templates/inventory.html | 4 ++-- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/pos/static/css/inventory.css b/pos/static/css/inventory.css index ac102c2..4aaa329 100644 --- a/pos/static/css/inventory.css +++ b/pos/static/css/inventory.css @@ -1333,6 +1333,20 @@ /* History table inside modal */ .inv-modal .data-table { width: 100%; } + /* ─── Virtual Scroll fixes ───────────────────────────────────────────── */ + .vs-container { + will-change: transform; + contain: layout paint; + -webkit-overflow-scrolling: touch; + } + .vs-container table { + will-change: transform; + } + .vs-container tbody tr { + content-visibility: auto; + contain-intrinsic-size: auto 48px; + } + /* ─── MercadoLibre Publish Modal Enhancements ────────────────────────── */ .meli-preview-card { display: grid; diff --git a/pos/static/js/virtual-scroll.js b/pos/static/js/virtual-scroll.js index 882e449..6ddaba6 100644 --- a/pos/static/js/virtual-scroll.js +++ b/pos/static/js/virtual-scroll.js @@ -24,6 +24,8 @@ this._scrollHandler = this._onScroll.bind(this); this._resizeHandler = this._onResize.bind(this); this._isTbody = this.container.tagName === 'TBODY'; + this._rafId = null; + this._pendingRender = false; this._init(); } @@ -58,11 +60,22 @@ }; VirtualScroll.prototype._onScroll = function() { - this._render(); + this._scheduleRender(); }; VirtualScroll.prototype._onResize = function() { - this._render(); + this._scheduleRender(); + }; + + VirtualScroll.prototype._scheduleRender = function() { + if (this._pendingRender) return; + this._pendingRender = true; + var self = this; + this._rafId = requestAnimationFrame(function() { + self._rafId = null; + self._pendingRender = false; + self._render(); + }); }; VirtualScroll.prototype._getScrollTop = function() { @@ -99,11 +112,7 @@ var buffer = this.buffer; if (!data.length) { - if (this._isTbody) { - this.container.innerHTML = this.emptyHtml; - } else { - this.container.innerHTML = this.emptyHtml; - } + this.container.innerHTML = this.emptyHtml; return; } @@ -112,20 +121,19 @@ var startIdx = Math.max(0, Math.floor(scrollTop / rowH) - buffer); var endIdx = Math.min(data.length, Math.ceil((scrollTop + containerHeight) / rowH) + buffer); + // Build new HTML var html = ''; if (this._isTbody) { - // Top spacer row var topSpacerHeight = startIdx * rowH; if (topSpacerHeight > 0) { - html += ''; + html += ''; } for (var i = startIdx; i < endIdx; i++) { html += this.renderRow(data[i], i); } - // Bottom spacer row var bottomSpacerHeight = (data.length - endIdx) * rowH; if (bottomSpacerHeight > 0) { - html += ''; + html += ''; } } else { for (var j = startIdx; j < endIdx; j++) { @@ -133,6 +141,10 @@ } } + // Use a DocumentFragment approach via innerHTML to avoid flicker: + // Setting innerHTML on tbody is the fastest way, but we can reduce + // perceived flicker by ensuring the container has contain: paint + // and by batching via rAF (done in _scheduleRender). this.container.innerHTML = html; }; diff --git a/pos/templates/inventory.html b/pos/templates/inventory.html index d25267f..832667c 100644 --- a/pos/templates/inventory.html +++ b/pos/templates/inventory.html @@ -13,7 +13,7 @@ - + @@ -907,7 +907,7 @@ - +