简体   繁体   中英

Adding a binary search optimization to a multi-line text clamping function

I need a solution to dynamically trim multi-line text in a container and have it end with ellipses instead.

CSS non-solution

Ideally CSS (line clamp) would handle this, but the Browser Support is just not there (85.65%). - Also it's currently an "extremely fragile [...] half-baked non-standardized propert[y]" ( source ).

JS: Pop until it fits (current solution)

I currently have a working JavaScript solution (measure & pop, until it fits), but it scales poorly. It's affected by the difference between text length and clamped length as well as the number of clamped text elements.

 var trimTextAddEllipses = function (targetElementClassName) { var elArray = document.getElementsByClassName(targetElementClassName); Array.from(elArray).forEach(function (el) { // create a cache of full text in data-attribute to be non-destructive if (!el.hasAttribute('data-text-cache')) { el.setAttribute('data-text-cache',el.textContent); } // reset var words = el.textContent = el.getAttribute('data-text-cache'); // turn into array to pop off items until it fits. var wordArray = words.split(' '); while (el.scrollHeight > el.offsetHeight) { wordArray.pop(); el.innerHTML = wordArray.join(' ') + ' …'; } }); }; 

For resizes I call it as callback to a requestAnimationFrame call to limit the number of times the clamping is done.

JS: Binary search (goal)

The words are in order (ie sorted), so a binary search approach would work to make the code more effective.

I found this this binarySearch function but can't come up with a comparison function to make this work properly.

I'd like help coming up with either a comparison function to use with the linked binary search function - or a different binary search solution that works for this problem.

Note

From the comments I'm aware that there's room for further optimization past binary search, but that it would require a lot of JS work (ie estimating rather than measuring, thus avoiding rendering this over and over) - but that seems too difficult.

You can easily let the loop use binary search:

let end = wordArray.length, distance = end;

while(distance > 0) {
   distance = Math.floor(distance / 2);
   el.innerHTML = wordArray.slice(0, end).join(' ') + '...';
   end += el.scrollHeight > el.offsetHeight ? -distance : distance;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM