I need to implement ellipsis ("..."
) in the middle of a text within a resizable element. Here is what it might look like. So,
"Lorem ipsum dolor sit amet. Ut ornare dignissim ligula sed commodo."
becomes
"Lorem ipsum dolor sit amet ... commodo."
When the element is stretched out to the width of the text, I want the ellipsis to disappear. How can this be done?
In the HTML, put the full value in a custom data-* attribute like
<span data-original="your string here"></span>
Then assign load
and resize
event listeners to a JavaScript function which will read the original data attribute and place it in the innerHTML
of your span tag. Here is an example of the ellipsis function:
function start_and_end(str) {
if (str.length > 35) {
return str.substr(0, 20) + '...' + str.substr(str.length-10, str.length);
}
return str;
}
Adjust the values, or if possible, make them dynamic, if necessary for different objects. If you have users from different browsers, you can steal a reference width from a text by the same font and size elsewhere in your dom. Then interpolate to an appropriate amount of characters to use.
A tip is also to have an abbr-tag on the ... or who message to make the user be able to get a tooltip with the full string.
<abbr title="simple tool tip">something</abbr>
I'd like to propose mine example of solving this problem.
The main idea is to split text in two even parts (or first is bigger, if the length is odd) one of which has ellipsis in the end and another aligned right with text-overflow: clip
.
So all you need to do with js, if you want to make it automatic/universal - is to split string and set attributes.
It has some disadvantages, though.
- No nice wrapping by words, or even letters (
text-overflow: ''
works only in FF at the moment) - If the split happens between words - space should be in the first part. Otherwise, it will be collapsed.
- End of the string should not have any exclamation marks, due to
direction: rtl
- they will be moved to the left of the string. I think, it is possible to fix this with putting right part of the word in the tag and exclamation mark in the::after
pseudo-element. But I haven't yet made it properly working.
But, with all of these, it looks really cool to me, especially when you dragging the border of the browser, which you can do on the jsfiddle page easily: https://jsfiddle.net/extempl/93ymy3oL/. Or just run the snippet with fixed max-width below.
Gif under the spoiler:
body {
max-width: 400px;
}
span::before, span::after {
display: inline-block;
max-width: 50%;
overflow: hidden;
white-space: pre;
}
span::before {
content: attr(data-content-start);
text-overflow: ellipsis;
}
span::after {
content: attr(data-content-end);
text-overflow: '';
direction: rtl;
}
<span data-content-start="Look deep into nature, and then you "
data-content-end= "will understand everything better"></span>
<br>
<span data-content-start="https://www.google.com.ua/images/branding/g"
data-content-end= "ooglelogo/2x/googlelogo_color_272x92dp.png"></span>
So my colleague came up with a solution that uses no extra dom elements. We check to see if the div overflows and add a data attribute of the last n characters. The rest is done in css.
Here is some HTML:
<div class="box">
<div class="ellipsis" data-tail="some">This is my text it is awesome</div>
</div>
<div class="box">
<div class="ellipsis">This is my text</div>
</div>
And the css:
.box {
width: 200px;
}
.ellipsis:before {
float: right;
content: attr(data-tail);
}
.ellipsis {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
Here is the obligatory jsfiddle for this: http://jsfiddle.net/r96vB/1/
The following Javascript function will do a middle truncation, like OS X:
function smartTrim(string, maxLength) {
if (!string) return string;
if (maxLength < 1) return string;
if (string.length <= maxLength) return string;
if (maxLength == 1) return string.substring(0,1) + '...';
var midpoint = Math.ceil(string.length / 2);
var toremove = string.length - maxLength;
var lstrip = Math.ceil(toremove/2);
var rstrip = toremove - lstrip;
return string.substring(0, midpoint-lstrip) + '...'
+ string.substring(midpoint+rstrip);
}
It will replace characters in the middle with ellipsis. My unit tests show:
var s = '1234567890';
assertEquals(smartTrim(s, -1), '1234567890');
assertEquals(smartTrim(s, 0), '1234567890');
assertEquals(smartTrim(s, 1), '1...');
assertEquals(smartTrim(s, 2), '1...0');
assertEquals(smartTrim(s, 3), '1...90');
assertEquals(smartTrim(s, 4), '12...90');
assertEquals(smartTrim(s, 5), '12...890');
assertEquals(smartTrim(s, 6), '123...890');
assertEquals(smartTrim(s, 7), '123...7890');
assertEquals(smartTrim(s, 8), '1234...7890');
assertEquals(smartTrim(s, 9), '1234...67890');
assertEquals(smartTrim(s, 10), '1234567890');
assertEquals(smartTrim(s, 11), '1234567890');
You can't do that with CSS. The problem is that HTML and CSS are supposed to work in a variety of browsers and fonts and it is almost impossible to calculate the width of a string in a consistent way. This is an idea that might help you. However, you would need to do that a number of times, until you find the string with the appropriate width.
This may be a bit late in the game, but I was looking to find a solution to this, and a colleague suggested a very elegant one, which I'll share. It requires some JS, but not a lot.
Imagine you have a div
of a size you need to put your label into:
<div style="width: 200px; overflow: hidden"></div>
Now, you have a function which will take two params: a string with the label, and a DOM element (this div
) to fit it into:
function setEllipsisLabel(div, label)
The first thing you do is create a span
with this label, and put it into the div
:
var span = document.createElement('span');
span.appendChild(document.createTextNode(label));
span.style.textOverflow = 'ellipsis';
span.style.display = 'inline-block';
div.appendChild(span);
We set the text-overflow
property to "ellipsis" so that as the text gets chopped off, a nice "..." is added at the end to illustrate this. We also set display
to be "inline-block" so that these elements have real pixel dimensions we can manipulate later. So far, nothing we could not have done with pure CSS.
But we want the ellipsis in the middle. First, we should find out if we need it at all... This can be done by comparing div.clientWidth
to span.clientWidth
- ellipsis is only needed if the span
is wider than the div
.
If we do need an ellipsis, let's start by saying that we want a fixed number of characters shown at the end of the word - say 10. So let's create a span containing only the last 10 characters of the label, and stick it into the div:
var endSpan = document.createElement('span');
endSpan.style.display = 'inline-block';
endspan.appendChild(document.createTextNode(label.substring(label.length - 10)));
div.appendChild(endSpan);
Now, let's override the width of the original span
to accommodate the new one:
span.style.width = (div.clientWidth - endSpan.clientWidth) + 'px';
As a result of this, we now have a DOM structure that looks something like this:
<div style="width: 200px; overflow: hidden">
<span style="display: inline-block; text-overflow: ellipsis; width: 100px">
A really long label is shown in this span
</span>
<span style="display: inline-block"> this span</span>
</div>
Because the first span
has text-overflow
set to "ellipsis", it will show "..." at the end, followed by the 10 characters of the second span, resulting in the ellipsis showing approximately in the middle of the div
.
You don't need to hardcode the 10 character length for the endSpan either: this can be approximated by calculating ratio of the span
's initial width to that of the div
, subtracting the appropriate proportion from the length of the label and dividing by two.
After some research on flex boxes I found this pure CSS solution which I believe is pretty cool.
<div style="width:100%;border:1px solid green;display:inline-flex;flex-wrap:nowrap;">
<div style="flex: 0 1 content;text-overflow: ellipsis;overflow:hidden;white-space:nowrap;"> Her comes very very very very very very very very very very very very very very very very very very very long </div>
<div style="flex: 1 0 content;white-space:nowrap;"> but flexible line</div>
</div>
Here's the shortest bit I could find which replaces 3 characters in the middle with ....
function shorten(s, max) {
return s.length > max ? s.substring(0, (max / 2) - 1) + '...' + s.substring(s.length - (max / 2) + 2, s.length) : s
}
This solution is a mix of the above solutions and puts the last whole word at the end of the shortened text. However in case the last word is longer then a third of the available space it is also shortend from the left. If a dash("-") is found, cut it of there, if not, cut it of anyway.
function truncate(text, textLimit) {
if (!text) return text;
if (textLimit < 1) return string;
if (text.length < textLimit) return text;
if (textLimit === 1) return text.substring(0,1) + '...';
/* extract the last word */
var lastPart = text.slice( string.lastIndexOf(' ')+1 );
/* if last word is longer then a third of the max available space
cut it from the left */
var lastPartLimit = Math.ceil(textLimit / 3);
if(lastPart.length > lastPartLimit) {
var truncatedLastPart = lastPart;
/* Try to find a dash and cut the last word there */
var lastDashPart = text.slice( text.lastIndexOf('-')+1 );
if(lastDashPart.length < lastPartLimit){
truncatedLastPart = lastDashPart;
}
/* If the last part is still to long or not available cut it anyway */
if(truncatedLastPart.length > lastPartLimit) {
var lastIndex = lastPart.length - lastPartLimit;
truncatedLastPart = lastPart.substring( lastIndex );
}
lastPart = truncatedLastPart;
}
var dots = '... ';
var firsPartLength = textLimit - lastPart.length - dots.length;
return text.substring(0, firstPartLength) + dots + lastPart;
}
console.log( truncate("New York City", 10) ); // Ne... City (max of 10 characters)
console.log( truncate("New York Kindergarden", 14) ); // Ne...ergarden (max of 14 characters, last word gets cut from the left by a third)
console.log( truncate("New York Kinder-garden", 14) ); // Ne...garden (max of 14 characters, last word gets cut by the dash from the left)
I just created a function that can trim at the middle, nearEnd and End but havent been tested yet because I finally was needing it at the server side
//position acceptable values : middle, end, closeEnd
function AddElipsis(input, maxChars, position) {
if (typeof input === 'undefined') {
return "";
}
else if (input.length <= maxChars) {
return input;
}
else {
if (position == 'middle') {
var midPos = Math.floor(maxChars / 2) - 2;
return input.substr(0, midPos) + '...' + input.substr(input.length - midPos, input.length);
}
else if (position == 'closeEnd') {
var firstPart = Math.floor(maxChars * 0.80) - 2;
var endPart = Math.floor(maxChars * 0.20) - 2;
return input.substr(0, firstPart) + '...' + input.substr(input.length - endPart, input.length);
}
else {
return input.substr(0, maxChars - 3) + '...';
}
}
}
Another stab:
function truncate( str, max, sep ) {
max = max || 10;
var len = str.length;
if(len > max){
sep = sep || "...";
var seplen = sep.length;
if(seplen > max) { return str.substr(len - max) }
var n = -0.5 * (max - len - seplen);
var center = len/2;
return str.substr(0, center - n) + sep + str.substr(len - center + n);
}
return str;
}
console.log( truncate("123456789abcde") ); // 123...bcde (using built-in defaults)
console.log( truncate("123456789abcde", 8) ); // 12...cde (max of 8 characters)
console.log( truncate("123456789abcde", 12, "_") ); // 12345_9abcde (customize the separator)
This will give you a little more control over the position of the ellipsis and the placeholder text:
function ellipsis(str, maxLength, ellipsisLocationPercentage,placeholder) {
/*
ARGUMENTS:
str - the string you want to maninpulate
maxLength - max number of characters allowed in return string
ellipsisLocationPercentage (optional) - How far (percentage wise) into the return string you want the ellipses to be placed
Examples:
.85 : This is a very long string. This is a very long string. This is a very long string. This is a ver[...]very long string.
.25 : This is a very long string. [...]g. This is a very long string. This is a very long string. This is a very long string.
placeholder (optional) - this will be used to replace the removed substring. Suggestions : '...', '[..]', '[ ... ]', etc....
*/
if(ellipsisLocationPercentage == null || isNaN(ellipsisLocationPercentage) || ellipsisLocationPercentage >= 1 || ellipsisLocationPercentage <= 0){
//we've got null or bad data.. default to something fun, like 85% (that's fun, right??)
ellipsisLocationPercentage = .85;
}
if(placeholder == null || placeholder ==""){
placeholder = "[...]";
}
if (str.length > (maxLength-placeholder.length)) {
//get the end of the string
var beginning = str.substr(0, (maxLength - placeholder.length)*ellipsisLocationPercentage );
var end = str.substr(str.length-(maxLength - placeholder.length) * (1-ellipsisLocationPercentage));
return beginning + placeholder + end;
}
return str;
}
You can call this function by calling:
ellipsis("This is a very long string. Be Scared!!!!", 8);//uses default values
ellipsis("This is a very long string. Be Scared!!!!", 8,.5);//puts ellipsis at half way point
ellipsis("This is a very long string. Be Scared!!!!", 8,.75,'<..>');//puts ellipsis at 75% of the way into the string and uses '<..>' as the placeholder
To make a clean cut and have a whole word at the end of the shortened text, I used the below function.
function prepareText(text){
var returnString = text;
var textLimit = 35;
if(text.length > textLimit){
var lastWord = text.slice( text.lastIndexOf(' ')+1 );
var indexFromEnd = lastWord.length;
var ellipsis = '... ';
returnString = text.slice(0, textLimit - indexFromEnd - ellipsis.length);
returnString = returnString + ellipsis + lastWord;
}
return returnString;
}
$('#ex1Modified').html( prepareText( $('#ex1').html() ) );
$('#ex2Modified').html( prepareText( $('#ex2').html() ) );
$('#ex3Modified').html( prepareText( $('#ex3').html() ) );
body{color:#777; font-family: sans-serif;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>Shortened Quotes from Albert Einstein</h2>
<div id="ex1">"The true sign of intelligence is not knowledge but imagination."</div>
<div id="ex1Modified"></div>
<br>
<div id="ex2">"Look deep into nature, and then you will understand everything better."</div>
<div id="ex2Modified"></div>
<br>
<div id="ex3">"You can't blame gravity for falling in love."</div>
<div id="ex3Modified"></div>
来源:https://stackoverflow.com/questions/831552/ellipsis-in-the-middle-of-a-text-mac-style