Reputation: 3123
Does anyone have a more sophisticated solution/library for truncating strings with JavaScript and putting an ellipsis on the end, than the obvious one:
if (string.length > 25) {
string = string.substring(0, 24) + "...";
}
Upvotes: 283
Views: 376825
Reputation: 12293
Re-implementation of Lodash truncate in modern JS + TypeScript :
// File truncate.ts
type Options = {
/**
* The maximum string length.
*
* Default: 30
*/
length?: number;
/**
* The string to indicate text is omitted.
*
* Also named [ellipsis](https://developer.mozilla.org/en-US/docs/Web/CSS/text-overflow)
*
* Default: "...", you might want to use "…" (… U+02026) instead
*/
omission?: string;
/**
* The separator pattern to truncate to.
*
* Default: none
*/
separator?: string;
};
/**
* Truncates a string if it's longer than the given maximum length.
* The last characters of the truncated string are replaced with the omission
* string which defaults to "...".
*
* @param str The string to truncate
* @param options The options object
* @returns The truncated string
*/
export function truncate(str: string, options?: Options) {
// https://stackoverflow.com/q/1199352
// https://github.com/Maggi64/moderndash/issues/155
// https://lodash.com/docs/4.17.15#truncate
const { length = 30, omission = '...', separator } = options ?? {};
if (str.length <= length) {
return str;
}
let maxLength = length - omission.length;
if (maxLength < 0) {
maxLength = 0;
}
const subString = str.slice(
0,
// FYI .slice() is OK if maxLength > text.length
maxLength
);
return (separator ? subString.slice(0, subString.lastIndexOf(separator)) : subString) + omission;
}
Passes all tests from Lodash:
// File truncate.test.ts
import { truncate } from './truncate';
// Copy-pasted and adapted from https://github.com/lodash/lodash/blob/c7c70a7da5172111b99bb45e45532ed034d7b5b9/test/truncate.spec.js
// See also https://github.com/lodash/lodash/pull/5815
const string = 'hi-diddly-ho there, neighborino';
it('should use a default `length` of `30`', () => {
expect(truncate(string)).toBe('hi-diddly-ho there, neighbo...');
});
it('should not truncate if `string` is <= `length`', () => {
expect(truncate(string, { length: string.length })).toBe(string);
expect(truncate(string, { length: string.length + 2 })).toBe(string);
});
it('should truncate string the given length', () => {
expect(truncate(string, { length: 24 })).toBe('hi-diddly-ho there, n...');
});
it('should support a `omission` option', () => {
expect(truncate(string, { omission: ' [...]' })).toBe('hi-diddly-ho there, neig [...]');
});
it('should support empty `omission` option', () => {
expect(truncate(string, { omission: '' })).toBe('hi-diddly-ho there, neighborin');
});
it('should support a `length` option', () => {
expect(truncate(string, { length: 4 })).toBe('h...');
});
it('should support a `separator` option', () => {
expect(truncate(string, { length: 24, separator: ' ' })).toBe('hi-diddly-ho there,...');
});
it('should treat negative `length` as `0`', () => {
[0, -2].forEach(length => {
expect(truncate(string, { length })).toBe('...');
});
});
it('should work as an iteratee for methods like `_.map`', () => {
const actual = [string, string, string].map(str => truncate(str));
const truncated = 'hi-diddly-ho there, neighbo...';
expect(actual).toEqual([truncated, truncated, truncated]);
});
You can retrieve this implementation in ModernDash library: https://github.com/Maggi64/moderndash/blob/moderndash%403.12.0/package/src/string/truncate.ts
Upvotes: 1
Reputation: 122906
Essentially, you check the length of the given string. If it's longer than a given length n
, clip it to length n
(substr
or slice
) and add html entity …
(…) to the clipped string.
Such a method looks like
function truncate(str, n){
return (str.length > n) ? str.slice(0, n-1) + '…' : str;
};
If by 'more sophisticated' you mean truncating at the last word boundary of a string then you need an extra check. First you clip the string to the desired length, next you clip the result of that to its last word boundary
function truncate( str, n, useWordBoundary ){
if (str.length <= n) { return str; }
const subString = str.slice(0, n-1); // the original check
return (useWordBoundary
? subString.slice(0, subString.lastIndexOf(" "))
: subString) + "…";
};
You can extend the native String
prototype with your function. In that case the str
parameter should be removed and str
within the function should be replaced with this
:
String.prototype.truncate = String.prototype.truncate ||
function ( n, useWordBoundary ){
if (this.length <= n) { return this; }
const subString = this.slice(0, n-1); // the original check
return (useWordBoundary
? subString.slice(0, subString.lastIndexOf(" "))
: subString) + "…";
};
More dogmatic developers may chide you strongly for that ("Don't modify objects you don't own". I wouldn't mind though). [edit 2023] A method to extend the String without tampering with its prototype may be to use a Proxy
. See this stackblitz snippet.
An approach without extending the String
prototype is to create
your own helper object, containing the (long) string you provide
and the beforementioned method to truncate it. That's what the snippet
below does.
const LongstringHelper = str => {
const sliceBoundary = str => str.substr(0, str.lastIndexOf(" "));
const truncate = (n, useWordBoundary) =>
str.length <= n ? str : `${ useWordBoundary
? sliceBoundary(str.slice(0, n - 1))
: str.slice(0, n - 1)}…`;
return { full: str, truncate };
};
const longStr = LongstringHelper(`Lorem ipsum dolor sit amet, consectetur
adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum`);
const plain = document.querySelector("#resultTruncatedPlain");
const lastWord = document.querySelector("#resultTruncatedBoundary");
plain.innerHTML =
longStr.truncate(+plain.dataset.truncateat, !!+plain.dataset.onword);
lastWord.innerHTML =
longStr.truncate(+lastWord.dataset.truncateat, !!+lastWord.dataset.onword);
document.querySelector("#resultFull").innerHTML = longStr.full;
body {
font: normal 12px/15px verdana, arial;
}
p {
width: 450px;
}
#resultTruncatedPlain:before {
content: 'Truncated (plain) n='attr(data-truncateat)': ';
color: green;
}
#resultTruncatedBoundary:before {
content: 'Truncated (last whole word) n='attr(data-truncateat)': ';
color: green;
}
#resultFull:before {
content: 'Full: ';
color: green;
}
<p id="resultTruncatedPlain" data-truncateat="120" data-onword="0"></p>
<p id="resultTruncatedBoundary" data-truncateat="120" data-onword="1"></p>
<p id="resultFull"></p>
Finally, you can use css only to truncate long strings in HTML nodes. It gives you less control, but may well be viable solution.
body {
font: normal 12px/15px verdana, arial;
margin: 2rem;
}
.truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 30vw;
}
.truncate:before{
content: attr(data-longstring);
}
.truncate:hover::before {
content: attr(data-longstring);
width: auto;
height: auto;
overflow: initial;
text-overflow: initial;
white-space: initial;
background-color: white;
display: inline-block;
}
<div class="truncate" data-longstring="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."></div>
Upvotes: 539
Reputation: 19
(JS) Using Slice and Template Literals.
${myString}.slice(0, 20) ...
Upvotes: 1
Reputation: 1720
I'm not sure if this qualifies as smart, but it's succinct and simple:
truncateStringToLength (string, length) {
return (string.length > length)
? `${string.substring(0, length)} …`
: string
}
… then:
truncateStringToLength('Lorem ipsum dolor sit amet, consetetur sadipscing', 20)
Upvotes: 1
Reputation: 986
If you want to do it with css instead of JavaScript;
.textShortDesc { /*Here we have determined the max number of lines.*/
display: block; /* or inline-block */
-o-text-overflow: ellipsis; /* Opera < 11*/
text-overflow: ellipsis; /* IE, Safari (WebKit), Opera >= 11, FF > 6 */
word-wrap: break-word;
overflow: hidden;
max-height: 2em; /*max-height/line-height=rowCount */
line-height: 1em;
}
Upvotes: 0
Reputation: 197
Here are my solutions with word boundary.
let s = "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat."
let s_split = s.split(/\s+/);
let word_count = 0;
let result = "";
//1
for(let i = 0; word_count < 100; i++){
word_count += s_split[i].length+1;
result += (s_split[i] + " ");
}
console.log(result);
// 2
word_count = 0;
result = s_split.reduce((x,y)=>{
word_count+=(y.length+1);
if(word_count>=100) return x;
else return x+" "+y;}, "").substring(1);
console.log(result);
Upvotes: 1
Reputation: 59
I always use the cuttr.js library to truncate strings and add custom ellipsis:
new Cuttr('.container', {
//options here
truncate: 'words',
length: 8,
ending: '... ►'
});
<script src="https://unpkg.com/[email protected]/dist/cuttr.min.js"></script>
<p class="container">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. </p>
This is bar far the easiest method (and doesn't have any dependencies) I know to cut strings with JS and its also available as jQuery plugin.
Upvotes: 3
Reputation: 479
Text-overflow: ellipsis is the property you need. With this and an overflow:hidden with a specific width, everything surpassing that will get the three period effect at the end ... Don't forget to add whitespace:nowrap or the text will be put in multiple lines.
.wrap{
text-overflow: ellipsis
white-space: nowrap;
overflow: hidden;
width:"your desired width";
}
<p class="wrap">The string to be cut</p>
Upvotes: 4
Reputation: 5386
('long text to be truncated').replace(/(.{250})..+/, "$1…");
Somehow above code was not working for some kind of copy pasted or written text in vuejs app. So I used lodash truncate and its now working fine.
_.truncate('long text to be truncated', { 'length': 250, 'separator': ' '});
Upvotes: 18
Reputation: 6912
Somewhere Smart :D
//My Huge Huge String
let tooHugeToHandle = `It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).`
//Trim Max Length
const maxValue = 50
// The barber.
const TrimMyString = (string, maxLength, start = 0) => {
//Note - `start` is if I want to start after some point of the string
if (string.length > maxLength) {
let trimmedString = string.substr(start, maxLength)
return (
trimmedString.substr(
start,
Math.min(trimmedString.length, trimmedString.lastIndexOf(' '))
) + ' ...'
)
}
return string
}
console.log(TrimMyString(tooHugeToHandle, maxValue))
Upvotes: 0
Reputation: 787
I like using .slice() The first argument is the starting index and the second is the ending index. Everything in between is what you get back.
var long = "hello there! Good day to ya."
// hello there! Good day to ya.
var short = long.slice(0, 5)
// hello
Upvotes: 8
Reputation: 4113
There are valid reasons people may wish to do this in JavaScript instead of CSS.
To truncate to 8 characters (including ellipsis) in JavaScript:
short = long.replace(/(.{7})..+/, "$1…");
or
short = long.replace(/(.{7})..+/, "$1…");
Upvotes: 45
Reputation: 2123
Best function I have found. Credit to text-ellipsis.
function textEllipsis(str, maxLength, { side = "end", ellipsis = "..." } = {}) {
if (str.length > maxLength) {
switch (side) {
case "start":
return ellipsis + str.slice(-(maxLength - ellipsis.length));
case "end":
default:
return str.slice(0, maxLength - ellipsis.length) + ellipsis;
}
}
return str;
}
Examples:
var short = textEllipsis('a very long text', 10);
console.log(short);
// "a very ..."
var short = textEllipsis('a very long text', 10, { side: 'start' });
console.log(short);
// "...ng text"
var short = textEllipsis('a very long text', 10, { textEllipsis: ' END' });
console.log(short);
// "a very END"
Upvotes: 14
Reputation: 471
Sometimes file names are numbered, where the index may be at the beginning or the end. So I wanted to shorten from the center of the string:
function stringTruncateFromCenter(str, maxLength) {
const midChar = "…"; // character to insert into the center of the result
var left, right;
if (str.length <= maxLength) return str;
// length of beginning part
left = Math.ceil(maxLength / 2);
// start index of ending part
right = str.length - Math.floor(maxLength / 2) + 1;
return str.substr(0, left) + midChar + str.substring(right);
}
Be aware that I used a fill character here with more than 1 byte in UTF-8.
Upvotes: 2
Reputation: 4095
I recently had to do this and ended up with:
/**
* Truncate a string over a given length and add ellipsis if necessary
* @param {string} str - string to be truncated
* @param {integer} limit - max length of the string before truncating
* @return {string} truncated string
*/
function truncate(str, limit) {
return (str.length < limit) ? str : str.substring(0, limit).replace(/\w{3}$/gi, '...');
}
Feels nice and clean to me :)
Upvotes: 0
Reputation: 7693
This function do the truncate space and words parts also.(ex: Mother into Moth...)
String.prototype.truc= function (length) {
return this.length>length ? this.substring(0, length) + '…' : this;
};
usage:
"this is long length text".trunc(10);
"1234567890".trunc(5);
output:
this is lo...
12345...
Upvotes: -1
Reputation: 1575
Use either lodash's truncate
_.truncate('hi-diddly-ho there, neighborino');
// → 'hi-diddly-ho there, neighbo…'
or underscore.string's truncate.
_('Hello world').truncate(5); => 'Hello...'
Upvotes: 36
Reputation: 5392
All modern browsers now support a simple CSS solution for automatically adding an ellipsis if a line of text exceeds the available width:
p {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
(Note that this requires the width of the element to be limited in some way in order to have any effect.)
Based on https://css-tricks.com/snippets/css/truncate-string-with-ellipsis/.
It should be noted that this approach does not limit based on the number of characters. It also does not work if you need to allow multiple lines of text.
Upvotes: 12
Reputation: 20401
Perhaps I missed an example of where someone is handling nulls, but 3 TOP answers did not work for me when I had nulls ( Sure I realize that error handling is and million other things is NOT the responsibility of the person answering the question, but since I had used an existing function along with one of the excellent truncation ellipsis answers I thought I would provide it for others.
e.g.
javascript:
news.comments
using truncation function
news.comments.trunc(20, true);
However, on news.comments being null this would "break"
Final
checkNull(news.comments).trunc(20, true)
trunc function courtesy of KooiInc
String.prototype.trunc =
function (n, useWordBoundary) {
console.log(this);
var isTooLong = this.length > n,
s_ = isTooLong ? this.substr(0, n - 1) : this;
s_ = (useWordBoundary && isTooLong) ? s_.substr(0, s_.lastIndexOf(' ')) : s_;
return isTooLong ? s_ + '…' : s_;
};
My simple null checker (checks for literal "null" thing too (this catches undefined, "", null, "null", etc..)
function checkNull(val) {
if (val) {
if (val === "null") {
return "";
} else {
return val;
}
} else {
return "";
}
}
Upvotes: 2
Reputation: 11371
With a quick Googling I found this... Does that work for you?
/**
* Truncate a string to the given length, breaking at word boundaries and adding an elipsis
* @param string str String to be truncated
* @param integer limit Max length of the string
* @return string
*/
var truncate = function (str, limit) {
var bits, i;
if (STR !== typeof str) {
return '';
}
bits = str.split('');
if (bits.length > limit) {
for (i = bits.length - 1; i > -1; --i) {
if (i > limit) {
bits.length = i;
}
else if (' ' === bits[i]) {
bits.length = i;
break;
}
}
bits.push('...');
}
return bits.join('');
};
// END: truncate
Upvotes: 0
Reputation: 121
Correcting Kooilnc's solution:
String.prototype.trunc = String.prototype.trunc ||
function(n){
return this.length>n ? this.substr(0,n-1)+'…' : this.toString();
};
This returns the string value instead of the String object if it doesn't need to be truncated.
Upvotes: 0
Reputation: 625
Use following code
function trancateTitle (title) {
var length = 10;
if (title.length > length) {
title = title.substring(0, length)+'...';
}
return title;
}
Upvotes: 1
Reputation: 1781
I upvoted Kooilnc's solution. Really nice compact solution. There's one small edge case that I would like to address. If someone enters a really long character sequence for whatever reason, it won't get truncated:
function truncate(str, n, useWordBoundary) {
var singular, tooLong = str.length > n;
useWordBoundary = useWordBoundary || true;
// Edge case where someone enters a ridiculously long string.
str = tooLong ? str.substr(0, n-1) : str;
singular = (str.search(/\s/) === -1) ? true : false;
if(!singular) {
str = useWordBoundary && tooLong ? str.substr(0, str.lastIndexOf(' ')) : str;
}
return tooLong ? str + '…' : str;
}
Upvotes: 1
Reputation: 4132
Note that this only needs to be done for Firefox.
All other browsers support a CSS solution (see support table):
p {
white-space: nowrap;
width: 100%; /* IE6 needs any width */
overflow: hidden; /* "overflow" value must be different from visible"*/
-o-text-overflow: ellipsis; /* Opera < 11*/
text-overflow: ellipsis; /* IE, Safari (WebKit), Opera >= 11, FF > 6 */
}
The irony is I got that code snippet from Mozilla MDC.
Upvotes: 80
Reputation: 9220
c_harm's answer is in my opinion the best. Please note that if you want to use
"My string".truncate(n)
you will have to use a regexp object constructor rather than a literal. Also you'll have to escape the \S
when converting it.
String.prototype.truncate =
function(n){
var p = new RegExp("^.{0," + n + "}[\\S]*", 'g');
var re = this.match(p);
var l = re[0].length;
var re = re[0].replace(/\s$/,'');
if (l < this.length) return re + '…';
};
Upvotes: 0
Reputation: 235
You can use the Ext.util.Format.ellipsis function if you are using Ext.js.
Upvotes: 1
Reputation:
Here's my solution, which has a few improvements over other suggestions:
String.prototype.truncate = function(){
var re = this.match(/^.{0,25}[\S]*/);
var l = re[0].length;
var re = re[0].replace(/\s$/,'');
if(l < this.length)
re = re + "…";
return re;
}
// "This is a short string".truncate();
"This is a short string"
// "Thisstringismuchlongerthan25characters".truncate();
"Thisstringismuchlongerthan25characters"
// "This string is much longer than 25 characters and has spaces".truncate();
"This string is much longer…"
It:
Upvotes: 7
Reputation: 4872
Most modern Javascript frameworks (JQuery, Prototype, etc...) have a utility function tacked on to String that handles this.
Here's an example in Prototype:
'Some random text'.truncate(10);
// -> 'Some ra...'
This seems like one of those functions you want someone else to deal with/maintain. I'd let the framework handle it, rather than writing more code.
Upvotes: 6