14

I'm trying to make a JS function that cuts a string after n characters - that works. The problem is if it's in the middle of a word it looks bad, so I need your help making it cut the whole word if it's the middle of it.

My code so far:

if($('#desc').text().length > 505){
  str = $("#desc").text();
  $('#desc').text(str.substring(0, 505)).append('...');
}

P.S

  • #desc is the div that contains my string.
  • you can use jQuery.
dda
  • 6,030
  • 2
  • 25
  • 34
Dan Barzilay
  • 4,974
  • 5
  • 27
  • 39
  • See also the related duplicate: ["javascript shorten string without cutting words"](http://stackoverflow.com/q/5454235/331508). – Brock Adams Nov 25 '14 at 05:41

7 Answers7

19
function cut(n) {
    return function textCutter(i, text) {
        var short = text.substr(0, n);
        if (/^\S/.test(text.substr(n)))
            return short.replace(/\s+\S*$/, "");
        return short;
    };
}
$('#desc').text(cut(505));
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I must be missing something because this does not work for me. In my experience if just removes the final word in the results. If the last word is 'currently' and the length cuts it at 'cur', it removes 'cur'. However, if the length cuts it at the full word 'currently' with no space at the end, it STILL removes that whole word. – jkupczak Feb 22 '17 at 19:41
  • @jkupczak What comes after the word 'currently' in your example? Can you provide a complete example call (as code) that turns up unexpected results, please? – Bergi Feb 22 '17 at 19:55
  • 1
    My apologies, I did the math wrong! This actually works great. – jkupczak Feb 22 '17 at 20:03
7

The lastIndexOf method can find the last space character in a string,

and passing a second argument sets an upper limit.

var cutat= string.lastIndexOf(' ',505);
if(cutat!=-1)string=string.substring(0,cutat)+'...';
//else the string is shorter than 505 (or has no spaces...)
kennebec
  • 102,654
  • 32
  • 106
  • 127
  • The second argument of String.lastIndexOf() is the index from which to begin the search, not the upper limit. https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/lastIndexOf – Will Mar 04 '13 at 15:41
  • 2
    The same source states:The calling string is searched backward, starting at the index argument. – kennebec Mar 04 '13 at 17:17
  • @kennebec is right! The direction is **backwards** and therefore `lastIndexOf` is most suitable for the job. To clarify: `var longString = "Not that long but still needs cutting";` `console.log(longString.length, longString.lastIndexOf(' ', 15));` `// prints 37 13` – Knörpeltäng Jun 08 '15 at 14:35
2

It's a combination of a for loop, charAt, and a means of testing the character against ones you consider to be word delimiters. I'll use a regular expression for that:

function splitString(str, index) {
  var delim = /\s|[,\.]/; // Put any other character you consider
                          // a non-word char in the brackets.
                          // The initial \s is any whitespace, so
                          // space, tab, newline, etc.
  var ch;
  var i;

  // Loop until we find a matching delimiter or we run out of string    
  for (i = index;
       i >= 0 && !delim.test(str.charAt(i));
       --i) {
    // No body
  }
  if (i < 0) {
    // No break before, split word in middle
    return index;
  }
  return i + 1;
}

Live example | source

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

You may want to have a look at Cutter.js

Cutter.js is a library used for truncating HTML code to limit its length, by word number, without losing the markup.

epoch
  • 16,396
  • 4
  • 43
  • 71
0

This simple function will work in any situation, plus adding 3 dots if needed :

function shortenString(source_string, max_length) {
    var short = source_string.substr(0, max_length);
    if (/^\S/.test(source_string.substr(max_length)))
        return short.replace(/\s+\S*$/, "") + '...';
    return short;
};

Example:

var title = "This function will work in any situation";
var short = shortenString(title, 30);
jDelforge
  • 129
  • 4
  • 15
0

  
    var texte = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc eu magna at justo bibendum accumsan. Aliquam quam metus, hendrerit eget commodo at, sagittis eu lectus. Nunc quis purus urna. Etiam sollicitudin aliquam dui, vel rutrum ligula tincidunt id. In elementum ultricies ex ut bibendum. Proin ac purus id lorem pharetra commodo. Curabitur euismod commodo eleifend. Proin porttitor aliquet massa eu dapibus. Phasellus vitae tempor nibh. Donec venenatis ligula dui, at eleifend urna facilisis sed. Proin sollicitudin vehicula mi aliquam interdum. Quisque in erat purus. Ut ut ipsum nec odio mollis maximus. Vivamus nec ultricies mi, ut posuere augue.`;
    
    function cut(n,text) {
        if(n<text.length){
          while(text[n] != " " && n>0){
            n--;
          }
          return text.substr(0,n);
        }else{
          return text;
        }

  }
    
    document.getElementById("result").innerHTML = cut(5,texte);
<p id="result"></p>
Azizi
  • 1
  • 1
-1
function cutAt(text, n) {
    if(text.length > n){
        for (; " .,".indexOf(text[n]) !== 0; n--){
        }
        return text.substr(0, n) + '...';
    }
    return text;
}
$('#desc').text(cutAt($('#desc').text(), 505));
Gavriel
  • 18,880
  • 12
  • 68
  • 105
  • Thanks for answering but unfortenatly its not working, why is the for loop empty? – Dan Barzilay May 25 '12 at 08:41
  • 3
    The for loop has to be `for (; " .,".indexOf(text[n]) !== 0; n--);`. Currently its an endless loop. – GodLesZ May 25 '12 at 08:52
  • @GodLesZ, your loop is identical to mine, the only difference is syntactic sugar, whether you use ";" or "{}". It's a bad practice to have only ";", especially in the same line, 'cause it's easy to "read" it by mistake that the next line is in the loop, that's why I'm used to have the "{}" and in 2 lines. – Gavriel May 20 '14 at 06:57
  • @Gavriel strange, I could swear the loop header was different and caused an endless loop.. Anyways, it's correct now, ideed! – GodLesZ Nov 13 '14 at 12:23