<html>
<head>
<!-- By Warren E. Downs, copyright 2016. Based loosely on a single/multiline JQuery using example by Alex,
but optimized to avoid JQuery, to use binary search, to use CSS text-overflow: ellipsis for end,
and adding marquee option as well.
Credit: Marquee: http://jsfiddle.net/jonathansampson/xxuxd/
JQuery version: https://mcmap.net/q/37848/-insert-ellipsis-into-html-tag-if-content-too-wide
(by Alex, http://stackoverflow.com/users/71953/alex)
(Improved with Binary Search as suggested by StanleyH, http://stackoverflow.com/users/475848/stanleyh)
-->
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<style>
.single {
overflow:hidden;
white-space: nowrap;
width: 10em;
padding: 10px;
margin: 0 auto;
border: solid 1px blue;
}
.multiline {
overflow: hidden;
white-space: wrap;
width: 10em;
height: 4.5em;
padding: 10px;
margin: 0 auto;
border: solid 1px blue;
}
.marquee {
overflow: hidden;
width: 40em;
padding: 10px;
margin: 0 auto;
border: solid 1px blue;
}
</style>
<script>
var _marqueeNumber=0;
// mode=start,end,middle
function clipText(text, len, mode) {
if(!mode) { mode="end"; }
else { mode=mode.toLowerCase(); }
if(mode == "start") { return "…"+clipText(text,len,"_start"); }
if(mode == "_start") { return text.substr(text.length - len); }
if(mode == "middle") {
return clipText(text, len/2, "end") + clipText(text, len/2, "_start");
}
return text.substr(0, len) + "…";
}
function generateKeyframes(clsName, start, end) {
var sec=5;
var totalLen=parseFloat(start)-parseFloat(end);
if(start.indexOf('em') > -1) { sec=Math.round(totalLen/3); }
else if(start.indexOf('px') > -1) { sec=Math.round(totalLen/42); }
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = 'body {}';
document.getElementsByTagName('head')[0].appendChild(style);
this.stylesheet = document.styleSheets[document.styleSheets.length-1];
try {
this.stylesheet.insertRule('.'+clsName+' {\n'+
' animation: '+clsName+' '+sec+'s linear infinite;\n'+
'}\n', this.stylesheet.rules.length);
this.stylesheet.insertRule('.'+clsName+':hover {\n'+
' animation-play-state: paused\n'+
'}\n', this.stylesheet.rules.length);
this.stylesheet.insertRule('@keyframes '+clsName+' {\n'+
' 0% { text-indent: '+start+' }\n'+
' 100% { text-indent: '+end+' }\n'+
'}', this.stylesheet.rules.length);
} catch (e) {
console.log(e.message);
}
}
function addClone(el, multiline, estyle) {
if(!estyle) {
try { estyle=window.getComputedStyle(el); }
catch(e) { return null; }
}
var t = el.cloneNode(true);
var s=t.style;
//s.display='none';
s.visibility='hidden'; // WARNING: Infinite loop if this is not hidden (e.g. while testing)
s.display='inline-block';
s.background='black';
s.color='white';
s.position='absolute';
s.left=0;
s.top=0;
s.overflow='visible';
s.width=(multiline ? parseFloat(estyle.width) : 'auto');
s.height=(multiline ? 'auto' : parseFloat(estyle.height));
el.parentNode.insertBefore(t, el.nextSibling);
return t;
}
function getTextWidth(el, multiline) {
var t=addClone(el, multiline);
if(!t) { return null; }
var ts=window.getComputedStyle(t);
var w=ts.width;
if(multiline) {
var es=window.getComputedStyle(el);
var lines=Math.round(parseInt(ts.height)/parseInt(es.height))*2+0.5;
w=w+'';
var unit=''; // Extract unit
for(var xa=0; xa<w.length; xa++) {
var c=w[xa];
if(c <= '0' || c >= '9') { unit=w.substr(xa-1); }
}
w=parseFloat(w);
w*=lines; // Multiply by lines
w+=unit; // Append unit again
}
t.parentNode.removeChild(t);
return w;
}
// cls=class of element to ellipsize
// mode=start,end,middle,marq (scrolling marquee instead of clip)
function ellipsis(cls, mode) {
mode=mode.toLowerCase();
var elems=document.getElementsByClassName(cls);
for(xa in elems) {
var el=elems[xa];
var multiline = el.className ? el.className.indexOf('multiline') > -1 : true;
if(mode == "marq") {
var w=getTextWidth(el, multiline);
if(!w) { continue; }
var mCls="dsmarquee"+(_marqueeNumber++);
var es=window.getComputedStyle(el);
generateKeyframes(mCls,es.width, '-'+w);
el.className+=" "+mCls;
continue;
}
if(mode == "end" && !multiline) { el.style.textOverflow="ellipsis"; continue; }
var estyle=null;
try { estyle=window.getComputedStyle(el); }
catch(e) { continue; }
if(estyle.overflow == "hidden") {
var text = el.innerHTML;
var t=addClone(el, multiline, estyle);
function height() {
var ts=window.getComputedStyle(t);
var es=window.getComputedStyle(el);
return parseFloat(ts.height) - parseFloat(es.height);
}
function width() {
var ts=window.getComputedStyle(t);
var es=window.getComputedStyle(el);
return parseFloat(ts.width) - parseFloat(es.width);
}
var tooLong = multiline ? height : width;
var len=text.length;
var diff=1;
var olen=0;
var jump=len/2;
while (len > 0) {
var diff=tooLong();
if(diff > 0) { len-=jump; jump/=2; }
else if(diff < 0) { len+=jump; }
len=Math.round(len);
//alert('len='+len+';olen='+olen+';diff='+diff+';jump='+jump+';t='+JSON.stringify(t.innerHTML));
t.innerHTML=clipText(text, len, mode);
if(olen == len) { break; }
olen=len;
}
el.innerHTML=t.innerHTML;
t.parentNode.removeChild(t);
}
//break;
t.style.visibility='hidden';
}
}
function testHarness() {
ellipsis('ellipsis1', 'start');
ellipsis('ellipsis2', 'end');
ellipsis('ellipsis3', 'middle');
ellipsis('marquee', 'marq')
}
</script>
</head>
<body onload="testHarness()">
<div class="single ellipsis1" style="float:left">some long text that should be clipped left</div>
<div class="single ellipsis2" style="float:right">right clip long text that should be clipped</div>
<div class="single ellipsis3" style="float:center">some long text that should be clipped in the middle</div>
<br />
<p class="single marquee">Windows 8 and Windows RT are focused on your life—your friends and family, your apps, and your stuff. With new things like the <a href="http://windows.microsoft.com/en-US/windows-8/start-screen">Start screen</a>, <a href="http://windows.microsoft.com/en-US/windows-8/charms">charms</a>, and a <a href="http://windows.microsoft.com/en-US/windows-8/microsoft-account">Microsoft account</a>, you can spend less time searching and more time doing.</p>
<br />
<div class="multiline ellipsis1" style="float:left">Test test test test test test, some more long text, such as asdasdasdasdasd, that should be multiline clipped left(*)</div>
<div class="multiline ellipsis2" style="float:right">right clip multiline long text, such as Test test test test test test, and some more long text that should be multiline clipped right.</div>
<div class="multiline ellipsis3" style="float:center">Test test test test test test, some more long text, such as asdasdasdasdasd, that should be multiline clipped in the middle(*)</div>
<br />
<p class="multiline marquee">Multiline Marquee: Windows 8 and Windows RT are focused on your life—your friends and family, your apps, and your stuff. With new things like the <a href="http://windows.microsoft.com/en-US/windows-8/start-screen">Start screen</a>, <a href="http://windows.microsoft.com/en-US/windows-8/charms">charms</a>, and a <a href="http://windows.microsoft.com/en-US/windows-8/microsoft-account">Microsoft account</a>, you can spend less time searching and more time doing.</p>
</body>
</html>