← back to Commercialrealestate
mls.html: fix "data flashes then blank body" — kill boot() location.reload()
58d7a29b5de394e3bc24c0da94cb7d3f85477bb2 · 2026-08-17 18:16:47 -0700 · Steve
Root cause (found via click-through agent + live Playwright): boot() called
location.reload() on every fresh tab for a logged-in user with saved settings.
The guard used tab-scoped sessionStorage, so it fired on each new tab, wiping the
first paint ("flash then blank"), and looped forever where sessionStorage does not
persist. The reload only existed to make saved view-keys (mlsCols/mlsColOrder/
mlsSortKey/mlsSortDir/crcp-theme2 — read into module vars only at parse time) take
effect.
Fix: rehydrateView() re-reads those keys into the live vars + repaints (global
render, defined outside the accounts IIFE so it is not shadowed by the account-bar
render of the same name). Replaces the reload; boot() no longer returns early.
Sign-in/sign-out reloads kept (auth-state changes should reload).
Also: an all-columns-hidden view now shows a legible empty-state instead of a
star-only "zero data" body — and does NOT destroy the user column choice (Cody gate).
Verified (isolated real-Chrome Playwright, 0 console/page errors): logged-out 200
rows/1 nav; logged-in+saved-sort 200 rows, navsOnReload=1 (was 2), saved sort applied
in-memory; all-hidden shows the empty-state msg, all views hidden, mlsCols preserved.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 58d7a29b5de394e3bc24c0da94cb7d3f85477bb2
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Aug 17 18:16:47 2026 -0700
mls.html: fix "data flashes then blank body" — kill boot() location.reload()
Root cause (found via click-through agent + live Playwright): boot() called
location.reload() on every fresh tab for a logged-in user with saved settings.
The guard used tab-scoped sessionStorage, so it fired on each new tab, wiping the
first paint ("flash then blank"), and looped forever where sessionStorage does not
persist. The reload only existed to make saved view-keys (mlsCols/mlsColOrder/
mlsSortKey/mlsSortDir/crcp-theme2 — read into module vars only at parse time) take
effect.
Fix: rehydrateView() re-reads those keys into the live vars + repaints (global
render, defined outside the accounts IIFE so it is not shadowed by the account-bar
render of the same name). Replaces the reload; boot() no longer returns early.
Sign-in/sign-out reloads kept (auth-state changes should reload).
Also: an all-columns-hidden view now shows a legible empty-state instead of a
star-only "zero data" body — and does NOT destroy the user column choice (Cody gate).
Verified (isolated real-Chrome Playwright, 0 console/page errors): logged-out 200
rows/1 nav; logged-in+saved-sort 200 rows, navsOnReload=1 (was 2), saved sort applied
in-memory; all-hidden shows the empty-state msg, all views hidden, mlsCols preserved.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
public/mls.html | 34 +++++++++++++++++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/public/mls.html b/public/mls.html
index 88b97d0..73997aa 100644
--- a/public/mls.html
+++ b/public/mls.html
@@ -368,6 +368,23 @@ function syncColOrder(){
}
function orderedCols(){ syncColOrder(); return COLORDER.map(k=>COLS.find(c=>c.k===k)).filter(Boolean); }
function visCols(){ return orderedCols().filter(c=>colVis(c.k)); }
+// Re-apply the account's saved view keys (columns / order / sort / theme) into the LIVE in-memory
+// state and repaint — the reload-free replacement for boot()'s old location.reload(). Those keys are
+// otherwise only read at script-parse time, which is why the original code reloaded the whole page to
+// make a freshly-synced view take effect; that reload wiped the first paint ("data flashes then blank")
+// and looped where sessionStorage doesn't persist. Defined at top level (NOT inside the accounts IIFE)
+// so `render` here resolves to the global TABLE render, not the IIFE's account-bar render of the same name.
+function rehydrateView(){
+ try{ const s=JSON.parse(localStorage.getItem('mlsCols')||'{}'); VISCOL=(s&&typeof s==='object')?s:{}; }catch(e){ VISCOL={}; }
+ try{ const a=JSON.parse(localStorage.getItem('mlsColOrder')||'[]'); COLORDER=Array.isArray(a)?a:[]; }catch(e){ COLORDER=[]; }
+ const sk=localStorage.getItem('mlsSortKey'); if(sk) sortKey=sk;
+ const sd=localStorage.getItem('mlsSortDir'); if(sd!==null&&sd!=='') sortDir=(+sd<0?-1:1);
+ const th=localStorage.getItem('crcp-theme2'); if(th) document.documentElement.setAttribute('data-theme', th==='dark'?'dark':'light');
+ lastColSig=''; // force column widths/resize-handles to re-init
+ try{ buildRail(); }catch(e){} // keep the left rail's field-toggles in sync with the restored columns
+ if(window.__syncSortSel) window.__syncSortSel();
+ render(); // global table render — reflects the restored view immediately
+}
function saveCols(){ try{localStorage.setItem('mlsCols',JSON.stringify(VISCOL));}catch(e){} }
function saveColOrder(){ try{localStorage.setItem('mlsColOrder',JSON.stringify(COLORDER));}catch(e){} }
// When the visible column SET or ORDER changes, hand the table back to col-resize.js
@@ -523,6 +540,16 @@ function render(){
return;
}
const rows=filtered(); const cols=visCols();
+ // If EVERY column is hidden (e.g. the field-panel "None" button, or a bad saved mlsCols), the old code
+ // painted a star-only body that reads as "zero data". Show a legible, non-blank empty state instead —
+ // WITHOUT destroying the user's column choice (a prior version reset VISCOL here, which silently undid
+ // a deliberate "None" click).
+ if(!cols.length){
+ $('#count').textContent='All columns are hidden — open the ⚙ field panel on the left to show columns';
+ ['#tableView','#gridView','#listView'].forEach(s=>$(s).classList.add('hide'));
+ $('#statsbar').innerHTML=''; const _mw=$('#moreWrap'); if(_mw) _mw.innerHTML='';
+ return;
+ }
renderStats(rows);
$('#count').textContent = anySelection()
? `${rows.length.toLocaleString()} of ${DATA.length.toLocaleString()} properties`
@@ -665,7 +692,12 @@ if($('#csv2')) $('#csv2').addEventListener('click',exportCSV);
if(ME&&ME.email){
try{ const r=await api('/api/settings'); SAVED_AT=r.savedAt;
if(r.settings && !sessionStorage.getItem('mls_settings_synced')){
- applyLocal(r.settings); sessionStorage.setItem('mls_settings_synced','1'); location.reload(); return;
+ // Apply the account's saved view IN-MEMORY (was location.reload → caused "data flashes ~1s
+ // then blank body": the reload fired on every fresh tab for a logged-in user because
+ // sessionStorage is tab-scoped, wiping the first paint, and looped forever where sessionStorage
+ // doesn't persist). rehydrateView() re-reads the just-written keys into the live vars + repaints;
+ // no return, so boot() falls through to render the account bar + arm save-on-exit.
+ applyLocal(r.settings); sessionStorage.setItem('mls_settings_synced','1'); rehydrateView();
}
}catch(e){}
sessionStorage.setItem('mls_settings_synced','1'); // load complete → arm save-on-exit (even with no prior saved view)
← 926630a CRCP listings: self-heal the blank-grid bug — clear any pers
·
back to Commercialrealestate
·
auto-data-snapshot: 2026-08-17T18:31:52 (3 data files) — dat 80e7660 →