2008-11-12 20 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> 

對於我的目的,我刪除鏈接從......,因爲我有我的頁面上的另一個標籤保存完整文本。