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;
    }
}

更新:以下の@someのソリューションははるかに優れています。それを使ってください。

更新 2: コードがgistとして投稿されるようになりました。気軽にフォークしてパッチを提出してください:)

4

4 に答える 4

22

コードにはいくつかの問題があります。

  • なぜ/ 5ですか?文字の幅は と によって異なりfont-familyますfont-size
  • str略語のタイトルをエスケープする必要があります(エスケープしないと、" によってコードが無効になります)。
  • diffは宣言されておらず、グローバル スコープで終了します
  • はそのsubstringように動作するはずがありません。どのブラウザを使用していますか?
  • hiddenの有効な値ではありませんstyle.display。それを非表示にするには、値を使用する必要がありますnoneが、ブラウザはoffsetWidth. style.visibility="hidden"代わりに使用してください。
  • 適切な長さの検索は非常に非効率的です。
  • 逃げなければならない&lt;/abbr&gt;

スタイルを使用してandclassNameを設定できるように、書き直して追加しました。Mr Fooz は、文字列全体を表示するためにマウスオーバーを使用することを提案しました。最新のブラウザはあなたのためにそれを行うので、それは必要ありません(FF、IE、Opera、およびChromeでテスト済み)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;
    }

編集:もう少しテスト中に、いくつかのバグが見つかりました。

  • Math.ceil意図したのではなく使用しましたMath.floor(これは、英語が母国語ではないことのせいです)

  • 入力文字列に html タグが含まれている場合、結果は未定義になります (途中でタグを切り詰めたり、開いたタグを残したりするのはよくありません)。

改良点:

  • すべての場所でスパンにコピーされた文字列をエスケープします。引き続き html エンティティを使用できますが、タグは使用できません (が表示さ<>ます)。
  • -ステートメントを書き直しましたwhile(少し高速ですが、主な理由は、余分なラウンドを引き起こしたバグを取り除き、breakステートメントを取り除くことでした)
  • 関数の名前を に変更しました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;
}
于 2008-11-12T13:55:02.330 に答える
3

ぱっと見、いい感じです。ここにいくつかのマイナーな提案があります:

  • バイナリ検索を使用して、線形の代わりに最適なサイズを見つけます。

  • (オプション) マウスオーバーを追加して、ツールチップに完全な文字列が表示されるようにします。

于 2008-11-12T01:54:27.090 に答える
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>

私の目的のために、... からリンクを削除しました。これは、ページに完全なテキストを保持する別のタブがあるためです。

于 2009-08-09T15:24:54.057 に答える
1

幸いなことに、最終的にはCSS3 の text-overflowがそれを処理するはずです。

ASP.NET を使用していて、サーバー側のソリューションに関心がある場合は、次のブログ投稿を確認してください。

http://waldev.blogspot.com/2010/09/truncate-text-string-aspnet-fit-width.html

于 2010-09-08T08:04:40.240 に答える