Vanilla JS ellipsis for long text

I don’t always want to display the whole text somewhere on my page. You know it – long descriptions, flowery statements. On the other hand, there is a lot of things to display on the page and you don’t want to make it too long. There are various possibilities to take care of long texts, but I will focus on Vanilla JavaScript.

but there is also a CSS-based solution

In most modern browsers, there is an ability to use text-overflow: ellipsis; CSS property. As per March 2020, it is properly handled by almost 99% of browsers. It is elegant and allows you to achieve your goal quickly. On the other hand, customization options are limited and – last but not least – it allows the word to be displayed partially. Sometimes it leads to ellipsis in unexpected places.

more control using JavaScript

I decided to go with a JavaScript solution because I want to have more control over the ellipsis. In fact, the code is rather straight-forward and can be adjusted for your needs.

The proposed solution requires three elements: one CSS definition of class, class name on the affected div elements, and the JavaScript code itself.

The CSS is really simple, it is used to hide excessive text:

.force-hidden {
    display: none;
}

The div elements you want to take care of should have the class name added:

<div class="more">
     Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</div>

And now for the JavaScript part:

var charLimit = 25;
var moreText = "show more";
var lessText = "show less";

var elementsToAdjust = document.getElementsByClassName('more');

// limit text and display ellipsis
for (var i = 0; i < elementsToAdjust.length; i++) {
    var content = elementsToAdjust[i].innerHTML;
    if (content.length > charLimit) {

        var visible = content.substr(0, charLimit);
        var hidden = content.substr(charLimit, content.length - charLimit);
        // move the split point to the end of the word or sentence
        var split_on_space = hidden.split(' ');
        if (split_on_space.length > 1) {
            visible += split_on_space.shift();
            hidden = ' ' + split_on_space.join(' ');
        }

        var html = visible + '<span>...</span><span><span class="force-hidden">' + hidden + '</span><button class="more-btn">' + moreText + '</button></span>';

        elementsToAdjust[i].innerHTML = html;
    }
}

// attach event handler to the "more" buttons
var moreButtons = document.getElementsByClassName('more-btn');
for (var i = 0; i < moreButtons.length; i++) {
    moreButtons[i].addEventListener("click", function () {
        if (this.classList.contains("less")) {
            this.classList.remove("less");
            this.innerHTML = moreText;
        } else {
            this.classList.add("less");
            this.innerHTML = lessText;
        }
        this.previousSibling.classList.toggle('force-hidden');
        this.parentNode.previousSibling.classList.toggle('force-hidden');
    });
}

So, if you are still reading, you may be curious about how this works. The first part simply takes care of the parameters (text length limit, texts on the buttons):

var charLimit = 25;
var moreText = "show more";
var lessText = "show less";

The next part is handling the actual text length limit. It takes the contents of each affected div element and splits it into two parts – the visible part and the hidden part. There is also a piece of code added to make sure that the ellipsis will be displayed at the end of the word, not in the middle of it. I know that the HTML elements I’m adding (spans, buttons) can be added more elegant way by using document.createElement() but I left them as they are for the sake of simplicity of this code.

// find all affected elements
var elementsToAdjust = document.getElementsByClassName('more');

// limit text and display ellipsis
for (var i = 0; i < elementsToAdjust.length; i++) {
    var content = elementsToAdjust[i].innerHTML;

    // affect only text which is longer than limit
    if (content.length > charLimit) {

        // split into visible and hidden part
        var visible = content.substr(0, charLimit);
        var hidden = content.substr(charLimit, content.length - charLimit);

        // move the split point to the end of the word or sentence
        var split_on_space = hidden.split(' ');
        if (split_on_space.length > 1) {
            visible += split_on_space.shift();
            hidden = ' ' + split_on_space.join(' ');
        }

        // glue all things together again, add ellipsis and button
        var html = visible + 
            '<span>...</span><span><span class="force-hidden">' + 
            hidden + 
            '</span><button class="more-btn">' + 
            moreText + 
            '</button></span>';

        // place the contents back in the element
        elementsToAdjust[i].innerHTML = html;
    }
}

Now, once we have the text limited, we have to take care of the button click. The code below takes care of the click event. It changes the text on the button and shows or hides the proper part of the text.

// find all buttons created
var moreButtons = document.getElementsByClassName('more-btn');

for (var i = 0; i < moreButtons.length; i++) {
    // attach event handler to the "more" buttons
    moreButtons[i].addEventListener("click", function () {

        if (this.classList.contains("less")) {
            // hidden text is displayed, button is in "show less"
            // state so change to "show more"
            this.classList.remove("less");
            this.innerHTML = moreText;
        } else {
            // hidden text is hidden, button is in "show more"
            // state so change to "show less"
            this.classList.add("less");
            this.innerHTML = lessText;
        }

        // show what was hiddedn, hide what was shown
        this.previousSibling.classList.toggle('force-hidden');
        this.parentNode.previousSibling.classList.toggle('force-hidden');

    });
}

Limitations

You may notice that there are some limitations to this solution. I want to focus on the most important from my point of view: it can handle only the text which does not contain HTML tags. If there are any HTML tags in the text, the ellipsis can be placed in the middle of such tags, and it will most likely break your document.

One Reply to “Vanilla JS ellipsis for long text”

Comments are closed.