Ładnie obcinaj łańcuch, aby pasował do danej szerokości piksela

Czasami masz ciągi, które muszą mieścić się w określonej szerokości piksela. Ta funkcja stara się to zrobić skutecznie. Proszę zamieszczać swoje sugestie lub refaktoringi poniżej:)

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: @some ' S rozwiązanie poniżej jest znacznie lepsze; Proszę użyć tego.

Update 2: Kod teraz opublikowany jako gist ; zapraszam do rozwidlania i składania łatek:)

Author: Aeon, 2008-11-12

4 answers

Jest kilka problemów z Twoim kodem.

  • dlaczego / 5? Szerokość znaków zależy od font-family i font-size.
  • musisz uciec str w tytule abbr (w przeciwnym razie " spowoduje nieważność kodu).
  • diff nie jest zadeklarowana i kończy się w zasięgu globalnym
  • substring nie powinno tak działać. Jakiej przeglądarki używasz?
  • {[8] } nie jest poprawną wartością style.display. Aby go ukryć należy użyć wartości none, ale wtedy przeglądarka nie oblicza offsetWidth. Zamiast tego użyj style.visibility="hidden".
  • poszukiwanie odpowiedniej długości jest bardzo nieefektywne.
  • musi uciec &lt;/abbr&gt; "

Przepisałem go dla Ciebie i dodałem className, abyś mógł użyć stylu, aby ustawić font-family i font-size. Pan Fooz zasugerował, aby użyć mouseover, aby pokazać cały ciąg. Nie jest to konieczne, ponieważ nowoczesne przeglądarki robią to za Ciebie (testowane z FF, IE, Opera i Chrome) {]}

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

Edytuj: Podczas testowania trochę więcej znalazłem kilka robaków.

  • Użyłem Math.ceil zamiast intended Math.floor (I blame this on ten angielski nie jest moim ojczystym język)

  • Jeśli łańcuch wejściowy ma znaczniki html wtedy wynik byłby nieokreślony (nie jest dobrze obcinać znacznik w środek lub zostawić otwarte znaczniki)

Ulepszenia:

  • Ucieczka ciągu, który jest kopiowany do zakresu we wszystkich miejscach. Możesz nadal używać encji html, ale nie wolno używać znaczników (< i > zostanie wyświetlony)
  • przepisać while-oświadczenie (jest to trochę szybciej, ale głównym powodem było pozbyć się robaka, który spowodowało dodatkowe rundy i pozbyć się Oświadczenie o zerwaniu)
  • zmieniono nazwę funkcji na fitStringToWidth

Wersja 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;
}
 22
Author: some,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 11:02:32

Na szybki rzut oka wygląda mi to dobrze. Oto kilka drobnych sugestii:

  • Użyj wyszukiwania binarnego, aby znaleźć optymalny rozmiar zamiast liniowego.

  • (opcjonalnie) dodaj mouseover tak, aby podpowiedź dała pełny ciąg znaków.

 3
Author: Mr Fooz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-12 01:54:27

Czy możesz napisać tę samą funkcję, ale aby pasowała do szerokości i wysokości div? Mam div o stałej szerokości i wysokości, gdzie muszę umieścić tekst z bazy danych. jeśli tekst jest zbyt duży dla div, chcę go wyciąć i reklamę ... na końcu? Możliwe? Dziękuję

Edytuj: Znalazłem rozwiązanie JS dla mojego pytania:

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

W moim celu usunąłem link z ..., ponieważ mam inną zakładkę na mojej stronie, która zawiera pełny tekst.

 2
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-09 15:37:39

Na szczęście, CSS3 text-overflow w końcu powinno się tym zająć.

Jeśli ktoś używa ASP.NET i jest zainteresowany rozwiązaniem po stronie serwera, sprawdź ten wpis na blogu:

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

 1
Author: Waleed Eissa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-08 11:50:13