← back to Commercialrealestate
perf(index): paginated DOM rendering (DTD verdict D, UNANIMOUS 3/3 Codex+Qwen+Claude) — grid renders 60 cards / list 200 rows per page with a 'Show N more' button; count + stats strip + facet counts still computed over the FULL filtered set (only the DOM is windowed). Cuts a 1823-card Non-QM render to 60. Contrarian-gated, 0 errors.
793d08ba119b5a583b1be874c4d11ee8db6795c9 · 2026-07-02 13:21:08 -0700 · Steve Abrams
Officer: vp-engineering
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Files touched
Diff
commit 793d08ba119b5a583b1be874c4d11ee8db6795c9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 2 13:21:08 2026 -0700
perf(index): paginated DOM rendering (DTD verdict D, UNANIMOUS 3/3 Codex+Qwen+Claude) — grid renders 60 cards / list 200 rows per page with a 'Show N more' button; count + stats strip + facet counts still computed over the FULL filtered set (only the DOM is windowed). Cuts a 1823-card Non-QM render to 60. Contrarian-gated, 0 errors.
Officer: vp-engineering
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
public/index.html | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/public/index.html b/public/index.html
index 98bfb11..a0c3726 100644
--- a/public/index.html
+++ b/public/index.html
@@ -195,6 +195,9 @@
.heatlegend { display:none; align-items:center; gap:5px; font-size:11px; color:var(--mut); }
.heatlegend.on { display:inline-flex; }
.heatlegend .bar { width:56px; height:8px; border-radius:4px; background:linear-gradient(90deg,hsl(0,70%,45%),hsl(60,70%,45%),hsl(120,70%,45%)); }
+ .showmore-wrap { grid-column:1/-1; text-align:center; padding:20px 0 6px; }
+ .showmore { background:var(--card); border:1px solid var(--line); color:var(--blue); border-radius:10px; padding:9px 20px; font-size:13px; cursor:pointer; }
+ .showmore:hover { border-color:var(--blue); }
/* compare overlay */
#cmpov { display:none; position:fixed; inset:0; background:rgba(0,0,0,.6); z-index:60; align-items:flex-start; justify-content:center; padding:40px 14px; overflow:auto; }
#cmpov.on { display:flex; }
@@ -1085,6 +1088,17 @@ function openCompare(){
body.innerHTML=`<table class="cmptbl"><thead>${head}</thead><tbody>${rows}</tbody></table>`;
ov.classList.add('on');
}
+// ---- paginated DOM (perf): render in pages, "Show more" appends ----
+let _rows=[], _shown=0;
+const PAGE=()=> view==='list'?200:60;
+function moreBtn(){ return _shown<_rows.length ? `<div class="showmore-wrap"><button class="showmore" id="showmore">Show ${Math.min(PAGE(),_rows.length-_shown)} more · showing ${_shown} of ${_rows.length}</button></div>` : ''; }
+function paintPage(){
+ const g=$('#grid'); const r=_rows; const slice=r.slice(0,_shown);
+ if(!r.length){ g.className='grid'; g.innerHTML=`<div class="empty">No properties match these filters.<br><span class="small">Loosen a filter or hit “Reset all”.</span></div>`; return; }
+ if(view==='list'){ g.className='listhost'; g.innerHTML=renderList(slice)+moreBtn(); }
+ else { g.className='grid'; g.innerHTML=slice.map(card).join('')+moreBtn(); enrichAssessor(); }
+ applyHeat(slice);
+}
function median(a){ if(!a.length) return null; const s=a.slice().sort((x,y)=>x-y); const m=Math.floor(s.length/2); return s.length%2?s[m]:(s[m-1]+s[m])/2; }
function renderStats(r){
const sb=$('#statsbar'); if(!sb) return;
@@ -1120,10 +1134,8 @@ function render(){
let r = applyFilters(DATA.ranked.slice());
sortRows(r);
renderStats(r);
- if(!r.length){ g.className='grid'; g.innerHTML=`<div class="empty">No properties match these filters.<br><span class="small">Loosen a filter or hit “Reset all”.</span></div>`; }
- else if(view==='list'){ g.className='listhost'; g.innerHTML=renderList(r); }
- else { g.className='grid'; g.innerHTML=r.map(card).join(''); enrichAssessor(); }
- if(r.length) applyHeat(r);
+ _rows=r; _shown=Math.min(PAGE(), r.length); // paginate the DOM; stats/count/facets still use the full set
+ paintPage();
$('#count').innerHTML = `<b>${r.length}</b> of ${DATA.ranked.length} properties${fc?` · ${fc} filter${fc>1?'s':''} active`:''}`;
$('#foot').textContent = `${scenario==='cash'?'all-cash':SCEN[scenario].downPct+'% down @ '+SCEN[scenario].ratePct+'%'} · * →X% = projected cap after value-add (not in-place). Cap rates broker-stated — verify NOI in DD.`;
saveState();
@@ -1369,7 +1381,7 @@ fetch('data/ranked.json').then(r=>r.json()).then(async d=>{
$('#examples').innerHTML = EX.map(x=>`<button class="ex">${x}</button>`).join('');
$('#examples').onclick=e=>{ if(e.target.classList.contains('ex')){ $('#q').value=e.target.textContent; $('#q').focus(); } };
$('#chatform').onsubmit=e=>{ e.preventDefault(); const t=$('#q').value.trim(); if(!t) return; $('#q').value=''; ask(t); };
- $('#grid').onclick=e=>{ const st=e.target.closest('[data-star]'); if(st){ toggleStar(st.dataset.star); st.classList.toggle('on',SHORT.has(st.dataset.star)); const sb=$('#shortbtn'); if(sb) sb.textContent='⭐ Shortlist ('+SHORT.size+')'; if(F.shortlist) render(); return; } const th=e.target.closest('.listtbl th[data-sortk]'); if(th){ const k=th.dataset.sortk; if(colSort&&colSort.key===k) colSort.dir*=-1; else colSort={key:k,dir:['type','firm','warr','broker','status','zip','addr','rec'].includes(k)?1:-1}; render(); return; } const b=e.target.closest('.lcbtn'); if(b){ fetchLiveComps(b.dataset.id); return; } const h=e.target.closest('.histbtn'); if(h) fetchHistory(h); };
+ $('#grid').onclick=e=>{ const more=e.target.closest('#showmore'); if(more){ _shown=Math.min(_shown+PAGE(), _rows.length); paintPage(); return; } const st=e.target.closest('[data-star]'); if(st){ toggleStar(st.dataset.star); st.classList.toggle('on',SHORT.has(st.dataset.star)); const sb=$('#shortbtn'); if(sb) sb.textContent='⭐ Shortlist ('+SHORT.size+')'; if(F.shortlist) render(); return; } const th=e.target.closest('.listtbl th[data-sortk]'); if(th){ const k=th.dataset.sortk; if(colSort&&colSort.key===k) colSort.dir*=-1; else colSort={key:k,dir:['type','firm','warr','broker','status','zip','addr','rec'].includes(k)?1:-1}; render(); return; } const b=e.target.closest('.lcbtn'); if(b){ fetchLiveComps(b.dataset.id); return; } const h=e.target.closest('.histbtn'); if(h) fetchHistory(h); };
$('#shortbtn').onclick=()=>{ F.shortlist=!F.shortlist; render(); };
$('#cmpbtn').onclick=openCompare;
$('#heat').value=heat; $('#heat').onchange=()=>{ heat=$('#heat').value; localStorage.setItem('cre_heat',heat); render(); };
← bf0b600 afternoon CRE update 2026-07-02
·
back to Commercialrealestate
·
feat(index): empty-state quick-start chips (DTD verdict D, 2 ac724c3 →