Хорошо обрезать строку, чтобы она соответствовала заданной ширине пикселя - PullRequest
9 голосов
/ 12 ноября 2008

Иногда у вас есть строки, которые должны соответствовать определенной ширине пикселя. Эта функция пытается сделать это эффективно. Пожалуйста, оставьте свои предложения или рефакторинг ниже:)

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 ; не стесняйтесь раскошелиться и представить патчи:)

Ответы [ 4 ]

22 голосов
/ 12 ноября 2008

Есть несколько проблем с вашим кодом.

  • Почему / 5? Ширина символов зависит от font-family и font-size.
  • Вы должны экранировать str в заголовке abbr (иначе "сделает код недействительным).
  • diff не объявлен и попадает в глобальную область
  • substring не должен работать так. Какой браузер вы используете?
  • hidden не является допустимым значением style.display. Чтобы скрыть это, вы должны использовать значение none, но тогда браузер не вычисляет offsetWidth. Вместо этого используйте style.visibility="hidden".
  • Поиск нужной длины очень неэффективен.
  • Должен сбежать &lt;/abbr&gt; "

Я переписал его для вас и добавил className, чтобы вы могли использовать стиль для установки font-family и font-size. Мистер Фуз предложил использовать указатель мыши, чтобы показать всю строку. В этом нет необходимости, поскольку современные браузеры делают это за вас (протестировано с FF, IE, Opera и 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;
    }

Edit: Во время тестирования я обнаружил пару ошибок.

  • Я использовал Math.ceil вместо предназначен Math.floor (я виню в этом этот английский не мой родной язык)

  • Если во входной строке были html-теги тогда результат будет неопределенным (не хорошо обрезать тег в середина или оставить открытые метки)

Улучшения: * * тысяча пятьдесят-одна

  • Избегайте строки, скопированной в span во всех местах. Вы по-прежнему можете использовать 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;
}
3 голосов
/ 12 ноября 2008

На первый взгляд, это выглядит хорошо для меня. Вот несколько небольших предложений:

  • Используйте бинарный поиск, чтобы найти оптимальный размер вместо линейного.

  • (необязательно) добавьте указатель мыши, чтобы во всплывающей подсказке была указана полная строка.

2 голосов
/ 09 августа 2009

Вы можете написать ту же функцию, но для соответствия ширине и высоте div? У меня есть div с фиксированной шириной и высотой, где мне нужно поместить текст из базы данных. если текст слишком большой для div, я хочу вырезать его и объявление ... в конце? Возможный? Спасибо

EDIT: Я нашел решение 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>

Для своих целей я удалил ссылку из ..., поскольку на моей странице есть еще одна вкладка с полным текстом.

1 голос
/ 08 сентября 2010

К счастью, Переполнение текста в CSS3 должно в конечном итоге позаботиться об этом.

Если кто-то использует ASP.NET и заинтересован в серверном решении, проверьте этот пост в блоге:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...