Skip to content

Commit eccad83

Browse files
committed
Clamp clipped search-result cells via CSS instead of inline height
Building on the read/write split, reduce the per-cell work clipContent() does and simplify the markup it generates: - Read line-height once for the table rather than calling getComputedStyle() for every cell. - Apply the 5-line clamp in CSS (.clip { max-height: 5lh }) instead of measuring and setting an inline pixel height per cell, and make expand/collapse a class toggle (.unclipped) so the click handler does no layout reads. - Emit one wrapper div and a single a.clip-toggle button per clipped cell, replacing the clip-container/clip wrappers and the separate unclip/reclip anchors. The button swaps its own label on toggle. This trims the DOM built per clipped cell and removes the per-cell getComputedStyle calls, further cutting the client-side cost of rendering wide search results. Note: the clamp uses the CSS lh unit (Chromium/Edge 109+, Firefox 120+).
1 parent d122310 commit eccad83

3 files changed

Lines changed: 40 additions & 48 deletions

File tree

share/static/css/elevator/collection.css

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@
2020

2121
.collection-as-table div.clip {
2222
overflow-y: hidden;
23+
max-height: 5lh;
2324
}
2425

25-
.collection-as-table a.unclip,
26-
.collection-as-table a.reclip {
26+
.collection-as-table div.clip.unclipped {
27+
max-height: none;
28+
}
29+
30+
.collection-as-table a.clip-toggle {
2731
font-size: smaller;
2832
font-weight: normal;
2933
margin-top: 0.3rem;

share/static/js/init.js

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,19 +1055,12 @@ jQuery(document).on('click', 'a.jump-to-unread', function (evt) {
10551055
}
10561056
});
10571057

1058-
// Clip content
1059-
jQuery(document).on('click', 'a.unclip', function() {
1060-
jQuery(this).siblings('div.clip').css('height', 'auto');
1061-
jQuery(this).hide();
1062-
jQuery(this).siblings('a.reclip').show();
1063-
return false;
1064-
});
1065-
1066-
jQuery(document).on('click', 'a.reclip', function() {
1067-
var clip_div = jQuery(this).siblings('div.clip');
1068-
clip_div.height(clip_div.attr('clip-height'));
1069-
jQuery(this).siblings('a.unclip').show();
1070-
jQuery(this).hide();
1058+
// Clip content -- toggle a clamped cell open/closed. The clamp is pure CSS
1059+
// (.clip { max-height: 5lh }), so expanding is just adding/removing a class;
1060+
// no height reads, and the same button swaps its label.
1061+
jQuery(document).on('click', 'a.clip-toggle', function() {
1062+
var unclipped = jQuery(this).prev('div.clip').toggleClass('unclipped').hasClass('unclipped');
1063+
jQuery(this).text(loc_key(unclipped ? 'clip' : 'unclip'));
10711064
return false;
10721065
});
10731066

share/static/js/util.js

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,46 +1603,41 @@ function registerLoadListener(func) {
16031603
}
16041604

16051605
function clipContent(elt) {
1606-
// Two-phase to avoid layout thrashing: measure everything first (read
1607-
// phase, no DOM writes => the browser computes layout once), then perform
1608-
// all the wrapping (write phase). Reading element heights in between DOM
1609-
// mutations forces a synchronous reflow per element, which is very slow on
1610-
// wide search results (rows x columns cells). The DOM output and the
1611-
// a.unclip/a.reclip handlers (in init.js) are unchanged.
1606+
// Clamp tall search-result cells to 5 lines and add a Show all/Show less
1607+
// toggle. Two-phase (measure, then mutate) so we never read an element's
1608+
// height after a DOM write -- doing so forces a synchronous reflow per
1609+
// element, which is slow on wide search results (rows x columns cells).
1610+
//
1611+
// The clamp height itself is applied by CSS (.clip { max-height: 5lh }) and
1612+
// expand/collapse is a class toggle (see the a.clip-toggle handler in
1613+
// init.js), so each clipped cell only gains a wrapper div and one button --
1614+
// no inline height bookkeeping and no second hidden anchor.
1615+
var cells = jQuery(elt).find('td.collection-as-table').toArray();
1616+
if ( !cells.length ) return;
1617+
1618+
// line-height is uniform across the table, so read it once instead of
1619+
// calling getComputedStyle for every cell.
1620+
var maxHeight = parseFloat(getComputedStyle(cells[0]).lineHeight) * 5;
1621+
if ( !maxHeight ) return; // line-height 'normal' => NaN, skip (matches prior behavior)
1622+
1623+
// READ PHASE -- measure only, no DOM writes.
16121624
var toClip = [];
1613-
1614-
// READ PHASE -- measure only.
1615-
jQuery(elt).find('td.collection-as-table').each(function () {
1616-
if ( !this.children.length ) return;
1617-
var maxHeight = parseFloat(getComputedStyle(this).lineHeight) * 5;
1618-
if ( !maxHeight ) return; // line-height 'normal' => NaN, skip (matches prior behavior)
1619-
for ( var i = 0; i < this.children.length; i++ ) {
1620-
var child = this.children[i];
1621-
if ( child.offsetHeight > maxHeight ) {
1622-
toClip.push({ child: child, height: maxHeight + 'px' });
1623-
}
1625+
for ( var i = 0; i < cells.length; i++ ) {
1626+
var children = cells[i].children;
1627+
for ( var k = 0; k < children.length; k++ ) {
1628+
if ( children[k].offsetHeight > maxHeight ) toClip.push(children[k]);
16241629
}
1625-
});
1630+
}
16261631

1627-
// WRITE PHASE -- mutate the DOM in one batch, no height reads in between.
1632+
// WRITE PHASE -- wrap each clipped child and add one toggle button.
16281633
for ( var j = 0; j < toClip.length; j++ ) {
1629-
var child = toClip[j].child;
1630-
var height = toClip[j].height;
1631-
1632-
var container = document.createElement('div');
1633-
container.className = 'clip-container';
1634+
var child = toClip[j];
16341635
var clip = document.createElement('div');
16351636
clip.className = 'clip';
1636-
clip.setAttribute('clip-height', height);
1637-
clip.style.height = height;
1638-
1639-
child.parentNode.replaceChild(container, child);
1637+
child.parentNode.replaceChild(clip, child);
16401638
clip.appendChild(child);
1641-
container.appendChild(clip);
1642-
container.insertAdjacentHTML('beforeend',
1643-
'<a href="#" class="unclip button btn btn-primary">' + loc_key('unclip') + '</a>' +
1644-
'<a href="#" class="reclip button btn btn-primary" style="display: none;">' + loc_key('clip') + '</a>'
1645-
);
1639+
clip.insertAdjacentHTML('afterend',
1640+
'<a href="#" class="clip-toggle button btn btn-primary">' + loc_key('unclip') + '</a>');
16461641
}
16471642
}
16481643

0 commit comments

Comments
 (0)