Cross-browser multi-line text overflow with ellipsis appended within a fixed width and height

前端 未结 25 2801
栀梦
栀梦 2020-11-22 16:19

I made an image for this question to make it easier to understand.

Is it possible to create an ellipsis on a

with a fixed width and multiple
25条回答
  •  借酒劲吻你
    2020-11-22 16:52

    EDIT: Came across Shave which is JS plugin that does multi line text truncation based on a given max-height really well. It uses binary search to find the optimum break point. Definitely worth investigating.


    ORIGINAL ANSWER:

    I had to come up with a vanilla JS solution for this problem. In the case that I had worked on, I had to fit a long product name into limited width and over two lines; truncated by ellipsis if needed.

    I used answers from various SO posts to cook up something that fit my needs. The strategy is as follows:

    1. Calculate the average character width of the font variant for the desired font size.
    2. Calculate the width of the container
    3. Calculate number of characters which fit on one line in the container
    4. Calculate the number of characters to truncate the string to based on the number of characters that fit on a line and the number of lines the text is supposed to wrap over.
    5. Truncate the input text based on the previous calculation (factoring in for extra characters added by ellipsis) and append "..." to the end

    Code sample:

    /**
     * Helper to get the average width of a character in px
     * NOTE: Ensure this is used only AFTER font files are loaded (after page load)
     * @param {DOM element} parentElement 
     * @param {string} fontSize 
     */
    function getAverageCharacterWidth(parentElement, fontSize) {
        var textSample = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()";
        parentElement = parentElement || document.body;
        fontSize = fontSize || "1rem";
        var div = document.createElement('div');
        div.style.width = "auto";
        div.style.height = "auto";
        div.style.fontSize = fontSize;
        div.style.whiteSpace = "nowrap";
        div.style.position = "absolute";
        div.innerHTML = textSample;
        parentElement.appendChild(div);
    
        var pixels = Math.ceil((div.clientWidth + 1) / textSample.length);
        parentElement.removeChild(div);
        return pixels;
    }
    
    /**
     * Helper to truncate text to fit into a given width over a specified number of lines
     * @param {string} text Text to truncate
     * @param {string} oneChar Average width of one character in px
     * @param {number} pxWidth Width of the container (adjusted for padding)
     * @param {number} lineCount Number of lines to span over
     * @param {number} pad Adjust this to ensure optimum fit in containers. Use a negative value to Increase length of truncation, positive values to decrease it.
     */
    function truncateTextForDisplay(text, oneChar, pxWidth, lineCount, pad) {
        var ellipsisPadding = isNaN(pad) ? 0 : pad;
        var charsPerLine = Math.floor(pxWidth / oneChar);
        var allowedCount = (charsPerLine * (lineCount)) - ellipsisPadding;
        return text.substr(0, allowedCount) + "...";
    }
    
    
    //SAMPLE USAGE:
    var rawContainer = document.getElementById("raw");
    var clipContainer1 = document.getElementById("clip-container-1");
    var clipContainer2 = document.getElementById("clip-container-2");
    
    //Get the text to be truncated
    var text=rawContainer.innerHTML;
    
    //Find the average width of a character
    //Note: Ideally, call getAverageCharacterWidth only once and reuse the value for the same font and font size as this is an expensive DOM operation
    var oneChar = getAverageCharacterWidth();
    
    //Get the container width
    var pxWidth = clipContainer1.clientWidth;
    
    //Number of lines to span over
    var lineCount = 2;
    
    //Truncate without padding
    clipContainer1.innerHTML = truncateTextForDisplay(text, oneChar, pxWidth, lineCount);
    
    //Truncate with negative padding value to adjust for particular font and font size
    clipContainer2.innerHTML = truncateTextForDisplay(text, oneChar, pxWidth, lineCount,-10);
    .container{
      display: inline-block;
      width: 200px;
      overflow: hidden;
      height: auto;
      border: 1px dotted black;
      padding: 10px;
      }

    Untruncated

    This is super long text which needs to be clipped to the correct length with ellipsis spanning over two lines

    Truncated

    Truncated with Padding Tweak

    PS:

    1. If the truncation is to be on only one line, the pure CSS method of using text-overflow: ellipsis is neater
    2. Fonts which don't have a fixed width may cause the truncation to happen too early or too late (as different characters have different widths). Using the pad parameter helps mitigate this in some cases but will not be fool proof :)
    3. Will add in links and references to the original posts after I get laptop back (need history)

    PPS: Just realised this is very similar to the approach as suggested by @DanMan and @st.never. Checkout the code snippets for an implementation example.

提交回复
热议问题