From 6bfdcae127dcbdd56b787ecd0419443f2530d3ed Mon Sep 17 00:00:00 2001 From: Tobias Schmidt Date: Tue, 18 Aug 2026 11:47:01 -0700 Subject: [PATCH] keep the query column visible when scrolling the details table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The detailed comparison table is wider than the screen, so scrolling right used to move the query checkbox and the query number out of view, leaving no way to tell which query a row belongs to. Make that column sticky. The checkbox and the query number now share a single cell so a plain `left: 0` is enough — with two cells the second one would need its offset measured at runtime. Two details keep the scrolled cells from showing through around the sticky one: an outline covers the table's 1px border-spacing, and a pseudo-element covers the body padding to its left. The query number is padded to a fixed width so the checkboxes line up for Q0..Q9 as well, and it keeps its own element so the tooltip still hangs off the name rather than off the whole cell. Toggling a query used to re-render only the summary, leaving the detailed table sorted by the previous ranking; re-render both. Co-Authored-By: Claude Opus 5 --- index.html | 61 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/index.html b/index.html index 3f64e1bde9..e8361c254f 100644 --- a/index.html +++ b/index.html @@ -299,6 +299,29 @@ padding: 0.1rem 0.5rem 0.1rem 0.5rem; } + /* The query checkbox and number stay put while the table is scrolled + horizontally, so it is always clear which query a row belongs to. + The outline covers the table's 1px border-spacing and the + pseudo-element covers the body padding left of the table — without + them the cells scrolling underneath shine through around it. */ + #details .details-query { + position: sticky; + left: 0; + z-index: 1; + background-color: var(--background-color); + outline: 1px solid var(--background-color); + } + + #details .details-query::before { + content: ''; + position: absolute; + top: -1px; + bottom: -1px; + right: 100%; + width: 100vw; + background-color: var(--background-color); + } + .shadow:hover { box-shadow: 0 0 1rem var(--shadow-color); position: relative; @@ -339,7 +362,6 @@ .tooltip-query { left: 0; width: 40rem; - margin-left: -3rem; } .note:hover .tooltip, .note:active .tooltip { @@ -361,8 +383,7 @@ } .tooltip-query::after { - left: 3rem; - margin-left: 0.5rem; + left: 0.5rem; } .nowrap { @@ -1164,16 +1185,15 @@

Detailed Comparison

let th_checkbox = document.createElement('th'); let checkbox = document.createElement('input'); checkbox.type = 'checkbox'; - checkbox.checked = true; + checkbox.checked = selectors.queries.every(selected => selected); checkbox.addEventListener('change', e => { - [...document.querySelectorAll('.query-checkbox')].map(elem => { elem.checked = e.target.checked }); - selectors.queries.map((_, i) => { selectors.queries[i] = e.target.checked }); - renderSummary(filtered_data); + selectors.queries.fill(e.target.checked); + render(); updateHistory(); }); + th_checkbox.className = 'details-query'; th_checkbox.appendChild(checkbox); details_head.appendChild(th_checkbox); - details_head.appendChild(document.createElement('th')); } /// Table header @@ -1194,7 +1214,7 @@

Detailed Comparison

tr.className = 'shadow'; let td_title = document.createElement('td'); - td_title.colSpan = 2; + td_title.className = 'details-query'; td_title.appendChild(document.createTextNode('Load time: ')); tr.appendChild(td_title); @@ -1219,7 +1239,7 @@

Detailed Comparison

tr.className = 'shadow'; let td_title = document.createElement('td'); - td_title.colSpan = 2; + td_title.className = 'details-query'; td_title.appendChild(document.createTextNode('Data size: ')); tr.appendChild(td_title); @@ -1245,29 +1265,32 @@

Detailed Comparison

let tr = document.createElement('tr'); tr.className = 'shadow'; - let td_checkbox = document.createElement('td'); + let td_query = document.createElement('td'); + td_query.className = 'details-query'; + let checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = 'query-checkbox'; checkbox.checked = selectors.queries[query_num]; checkbox.addEventListener('change', e => { selectors.queries[query_num] = e.target.checked; - renderSummary(filtered_data); + render(); updateHistory(); }); - td_checkbox.appendChild(checkbox); - tr.appendChild(td_checkbox); + td_query.appendChild(checkbox); - let td_query_num = document.createElement('td'); - td_query_num.className = 'note'; - td_query_num.appendChild(document.createTextNode(`Q${query_num}. `)); + /// Padded to a fixed width so the checkboxes of Q0..Q9 line up with the rest. + let query_name = document.createElement('span'); + query_name.className = 'note'; + query_name.appendChild(document.createTextNode(` ${`Q${query_num}.`.padStart(4)}`)); let tooltip = document.createElement('span'); tooltip.className = 'tooltip tooltip-query'; tooltip.appendChild(document.createTextNode(`Query ${query_num}: ${queries[query_num]}`)); - td_query_num.appendChild(tooltip); + query_name.appendChild(tooltip); - tr.appendChild(td_query_num); + td_query.appendChild(query_name); + tr.appendChild(td_query); sorted_indices.map(idx => { const curr_timing = selectRun(filtered_data[idx].result[query_num], selectors.metric);