2008-11-12 67 views
9

有时候,您必须将字符串放在某个像素宽度内。该功能试图有效地这样做。请发表您的建议或重构低于:)很好地截断一个字符串以适合给定的像素宽度

function fitStringToSize(str,len) { 
    var shortStr = str; 
    var f = document.createElement("span"); 
    f.style.display = 'hidden'; 
    f.style.padding = '0px'; 
    document.body.appendChild(f); 

    // on first run, check if string fits into the length already. 
    f.innerHTML = str; 
    diff = f.offsetWidth - len; 

    // if string is too long, shorten it by the approximate 
    // difference in characters (to make for fewer iterations). 
    while(diff > 0) 
    { 
     shortStr = substring(str,0,(str.length - Math.ceil(diff/5))) + '…'; 
     f.innerHTML = shortStr; 
     diff = f.offsetWidth - len; 
    } 

    while(f.lastChild) { 
     f.removeChild(f.lastChild); 
    } 
    document.body.removeChild(f); 

    // if the string was too long, put the original string 
    // in the title element of the abbr, and append an ellipsis 
    if(shortStr.length < str.length) 
    { 
     return '<abbr title="' + str + '">' + shortStr + '</abbr>'; 
    } 
    // if the string was short enough in the first place, just return it. 
    else 
    { 
     return str; 
    } 
} 

UPDATE: @下面一些的解决方案要好得多;请使用它。

更新2: 现在的代码发布为gist;随时分叉和提交补丁:)

回答

22

您的代码有几个问题。

  • 为什么/ 5?字符的宽度取决于font-familyfont-size
  • 必须逃脱的缩写标题str(或者是“将使代码无效)。
  • diff未声明,并在全球范围内
  • substring是不应该这样的工作结束了。您使用的是什么浏览器?
  • hidden不是style.display有效值。要隐藏它,你应该使用的价值none但随后的浏览器不计算offsetWidth,使用style.visibility="hidden"代替。
  • 的权利的搜索长度是ver y效率低下。
  • 必须躲避&lt;/abbr&gt;

我重写它为你添加className这样你就可以使用样式设置font-familyfont-size。福兹先生建议您使用鼠标可显示整个字符串。这是没有必要的,因为现代的浏览器为你做的

function fitStringToSize(str,len,className) { 
    var result = str; // set the result to the whole string as default 
    var span = document.createElement("span"); 
    span.className=className; //Allow a classname to be set to get the right font-size. 
    span.style.visibility = 'hidden'; 
    span.style.padding = '0px'; 
    document.body.appendChild(span); 


    // check if the string don't fit 
    span.innerHTML = result; 
    if (span.offsetWidth > len) { 
     var posStart = 0, posMid, posEnd = str.length; 
     while (true) { 
      // Calculate the middle position 
      posMid = posStart + Math.ceil((posEnd - posStart)/2); 
      // Break the loop if this is the last round 
      if (posMid==posEnd || posMid==posStart) break; 

      span.innerHTML = str.substring(0,posMid) + '&hellip;'; 

      // Test if the width at the middle position is 
      // too wide (set new end) or too narrow (set new start). 
      if (span.offsetWidth > len) posEnd = posMid; else posStart=posMid; 
     } 
     //Escape 
     var title = str.replace("\"","&#34;"); 
     //Escape <and> 
     var body = str.substring(0,posStart).replace("<","&lt;").replace(">","&gt;"); 
     result = '<abbr title="' + title + '">' + body + '&hellip;<\/abbr>'; 
    } 
    document.body.removeChild(span); 
    return result; 
    } 

编辑(与FF,IE,Opera和Chrome浏览器测试):在测试多一点 我发现一对夫妇的错误。

  • 我用Math.ceil代替 预期Math.floor(我责怪这对 英语不是我的母语 语言)

  • 如果输入字符串有HTML标签 那么结果会未定义 (这不是很好的截断标签在 中间或平仓离场标签)

改进:

  • 逃脱被复制到所有的地方跨度的字符串。你仍然可以使用HTML实体,但没有标签被允许(<>将显示)
  • 改写了while语句来(这是一个 快一点,但最主要的原因是 摆脱错误的是 造成的额外回合,以获得突破语句去掉 )
  • 改名功能fitStringToWidth

版本2:

function fitStringToWidth(str,width,className) { 
    // str A string where html-entities are allowed but no tags. 
    // width The maximum allowed width in pixels 
    // className A CSS class name with the desired font-name and font-size. (optional) 
    // ---- 
    // _escTag is a helper to escape 'less than' and 'greater than' 
    function _escTag(s){ return s.replace("<","&lt;").replace(">","&gt;");} 

    //Create a span element that will be used to get the width 
    var span = document.createElement("span"); 
    //Allow a classname to be set to get the right font-size. 
    if (className) span.className=className; 
    span.style.display='inline'; 
    span.style.visibility = 'hidden'; 
    span.style.padding = '0px'; 
    document.body.appendChild(span); 

    var result = _escTag(str); // default to the whole string 
    span.innerHTML = result; 
    // Check if the string will fit in the allowed width. NOTE: if the width 
    // can't be determined (offsetWidth==0) the whole string will be returned. 
    if (span.offsetWidth > width) { 
    var posStart = 0, posMid, posEnd = str.length, posLength; 
    // Calculate (posEnd - posStart) integer division by 2 and 
    // assign it to posLength. Repeat until posLength is zero. 
    while (posLength = (posEnd - posStart) >> 1) { 
     posMid = posStart + posLength; 
     //Get the string from the beginning up to posMid; 
     span.innerHTML = _escTag(str.substring(0,posMid)) + '&hellip;'; 

     // Check if the current width is too wide (set new end) 
     // or too narrow (set new start) 
     if (span.offsetWidth > width) posEnd = posMid; else posStart=posMid; 
    } 

    result = '<abbr title="' + 
     str.replace("\"","&quot;") + '">' + 
     _escTag(str.substring(0,posStart)) + 
     '&hellip;<\/abbr>'; 
    } 
    document.body.removeChild(span); 
    return result; 
} 
+0

非常好,谢谢。/5是基于在我的应用程序中使用眼球来选择的(m是10px,f是3px)。这实际上是一种垃圾,因为字体大小会根据字符串将放置的div的样式而变化,所以理想情况下我们不会追加到body,而是添加到div。 – Aeon 2008-11-12 18:59:06

3

快速一瞥,它看起来不错。这里有一些小的建议:

  • 使用二进制搜索以找到最佳的大小,而不是线性的。

  • (可选)添加鼠标悬停,以便工具提示可以显示完整的字符串。

+0

title属性将在所有现代浏览器给鼠标悬停提示:) 当你说二进制搜索时,你是什么意思?现在它试图使字符串缩短估计的字符长度,所以它应该只有1-3次迭代,无论字符串多长时间 – Aeon 2008-11-12 18:54:15

2

你能写出相同的功能,但适合宽度和高度的div?我有一个固定宽度和高度的div,我需要从数据库中放置文本。如果文字对于div来说太大了,我想剪切它并在最后加上...可能?谢谢

编辑: 我发现我的问题解决JS:

<p id="truncateMe">Lorem ipsum dolor sit amet, consectetuer adipiscing 
elit. Aenean consectetuer. Etiam venenatis. Sed ultricies, pede sit 
amet aliquet lobortis, nisi ante sagittis sapien, in rhoncus lectus 
mauris quis massa. Integer porttitor, mi sit amet viverra faucibus, 
urna libero viverra nibh, sed dictum nisi mi et diam. Nulla nunc eros, 
convallis sed, varius ac, commodo et, magna. Proin vel 
risus. Vestibulum eu urna. Maecenas lobortis, pede ac dictum pulvinar, 
nibh ante vestibulum tortor, eget fermentum urna ipsum ac neque. Nam 
urna nulla, mollis blandit, pretium id, tristique vitae, neque. Etiam 
id tellus. Sed pharetra enim non nisl.</p> 

<script type="text/javascript"> 

var len = 100; 
var p = document.getElementById('truncateMe'); 
if (p) { 

    var trunc = p.innerHTML; 
    if (trunc.length > len) { 

    /* Truncate the content of the P, then go back to the end of the 
     previous word to ensure that we don't truncate in the middle of 
     a word */ 
    trunc = trunc.substring(0, len); 
    trunc = trunc.replace(/\w+$/, ''); 

    /* Add an ellipses to the end and make it a link that expands 
     the paragraph back to its original size */ 
    trunc += '<a href="#" ' + 
     'onclick="this.parentNode.innerHTML=' + 
     'unescape(\''+escape(p.innerHTML)+'\');return false;">' + 
     '...<\/a>'; 
    p.innerHTML = trunc; 
    } 
} 

</script> 

对于我的目的,我删除链接从......,因为我有我的页面上的另一个标签保存完整文本。