Изменение размера шрифта в зависимости от длины строки

У меня есть вертикальное меню, и я хочу сделать его локализуемым, но локализованные строки в элементах меню часто выходят за край.
Поэтому вопрос заключается в том, как изменить размер шрифта в зависимости от длины строки в CSS. И если возможно, без JavaScript.
Спасибо!

UPD: JQuery неприемлемо. В любом случае в Pure JS?

Ответ 1

Вы должны ознакомиться с использованием плагинов, они экономят много времени и, конечно же, они очень надежны (они написаны опытными сценаристами/программистами и были протестированы сообществом). Однако похоже, что вы хотите получить чистое решение JS. Я только что сделал этот код для вас. Он работает нормально (хотя я не уверен, насколько он хорош, как некоторые плагины). Единственным требованием является элемент (который вы хотите скорректировать размер шрифта в соответствии с длиной текста) должен содержать обычный текст, а не некоторый HTML-код.

Идея реализовать его с использованием чистого JS проста, вам нужен какой-то фиктивный элемент, созданный с помощью script, этот фиктивный элемент используется для измерения размера текста. Нам нужно настроить размер шрифта фиктивного элемента до тех пор, пока размер текста (как и фиктивный элемент) не будет ограничен размером элемента (размер шрифта которого вы хотите настроить). Я сделал код очень четко, надеюсь, что вы поймете это лучше после прочтения кода:

//we just need 1 dummy element for the whole page.
var dummy = document.createElement('div');
dummy.className = 'dummy';
var inSingleLineMode, inMultilineMode;    
//function used to adjust the font-size of the element
//so that the width is fixed (single-line mode) or both the width and height are 
//fixed (multi-line mode), of course the text should be contained within 
//the fixed width and height.
function adjustFontSize(element, singleLine){
  if(!element.innerHTML) return;
  var elementStyle = getComputedStyle(element);        
  dummy.style.font = elementStyle.font;
  initMode(singleLine, function(){ dummy.style.width = elementStyle.width });
  dummy.style.padding = elementStyle.padding;
  dummy.style.boxSizing = elementStyle.boxSizing;
  dummy.innerHTML = element.innerHTML;
  document.body.appendChild(dummy);
  var dummyStyle = getComputedStyle(dummy);          
  while(singleLine ? parseInt(dummyStyle.width) < parseInt(elementStyle.width) :
                     parseInt(dummyStyle.height) < parseInt(elementStyle.height)){
    dummy.style.fontSize = parseFloat(dummyStyle.fontSize) + 1 + 'px';
    dummyStyle = getComputedStyle(dummy);
  }
  while(singleLine ? parseInt(dummyStyle.width) > parseInt(elementStyle.width) :
                     parseInt(dummyStyle.height) > parseInt(elementStyle.height)){
    dummy.style.fontSize = parseFloat(dummyStyle.fontSize) - 1 + 'px';
    dummyStyle = getComputedStyle(dummy);
  }    
  element.style.fontSize = dummyStyle.fontSize;
  document.body.removeChild(dummy);
}
function initMode(singleLine, callback){
  if(!dummy) return;
  if(singleLine&&!inSingleLineMode) {
    dummy.style.whiteSpace = 'nowrap';
    dummy.style.width = 'auto';
    dummy.style.display = "inline-block";
    inSingleLineMode = true;
    inMultiLineMode = false;
  } else if(!singleLine&&!inMultilineMode) {
    if(callback) callback();
    dummy.style.whiteSpace = 'initial';
    dummy.style.display = "block";
    dummy.style.wordWrap = 'break-word';
    inMultilineMode = true;
    inSingleLineMode = false;
  }
}

Демоверсия

В демо вы увидите, что первое меню #menu1 - это вьетнамское слово, означающее хризантему, а второе меню #menu2 - это, конечно, английское слово хризантема. Они имеют разную длину, однако обе должны иметь фиксированную ширину 100px, поэтому второе меню #menu2 должно иметь меньший размер шрифта, чтобы соответствовать пространству.

Ответ 2

Вы можете использовать jQuery Text Fill, как это.

  • Загрузите плагин: <script src="jquery.textfill.js" ></script>

  • Поместите id <input type="text" id="dyntext" value="e=mc²"></input>

  • Используйте код для создания магии. Предпочтительно поместите это в теги <script>:

Конечный результат будет выглядеть примерно так:

 function update() {
  var size = parseInt($('#maxsize').val(), 10);
  if (!isNaN(size)) {
    $('.dyntextval').html($('#dyntext').val());
    $('.jtextfill').textfill({debug: true, maxFontPixels: size});
  }
}

$(function () {
  $('#maxsize').keyup(update);
  $('#dyntext').keyup(update);
  update()
});

enter image description here

Ответ 3

Это невозможно без Javascript. Используя Javascript, вы можете использовать одну из многих библиотек, например FitText.

Таким образом, вы можете использовать Javascript-библиотеку для этого, но это также означает, что разные метки имеют разные размеры шрифта. Я думаю, что лучшим подходом было бы стиль меню таким образом, чтобы он грациозно обрабатывал многострочные подписи. Таким образом, длина не имеет большого значения.

Потому что какой-то язык "длиннее", чем другие (например, французские метки в avarage в 1,5-2 раза больше, чем на английском языке, рекомендуется протестировать ваш интерфейс с одним из этих языков.

И для размера шрифта вы можете добавить модификатор на стороне сервера, если вы знаете, что текущий язык является французским, вы можете добавить класс 'gui-subtions-very-long' в тег html и примените свой CSS на основе этого класса. Таким образом, у вас может быть универсальный модификатор, который вы можете настроить для каждого языка. Я думаю, что лучшее решение, чем создание всех меток, соответствует одной строке.

Имейте в виду, что более мелкие размеры труднее читать. Вы не можете просто сделать шрифты на половину размера, если текст в два раза длиннее. Вам нужно будет настроить свой дизайн (или его реализацию), чтобы сделать возможными более длинные тексты.

Ответ 4

Я действительно искал это в прошлом, тогда нашел ответ, который действительно помогло, но не мог вспомнить точно...:( Но так как он отвечает на вопрос, используя чистый javascript и нет плагинов/библиотек (некоторые из них были оптимизированы), вот оно! (с рабочим примером):

// First we add a new function to the String prototype,
// this will be used to get the length of current string according
// to its font-family and font-size
String.prototype.textWidth = function(fontFamily, fontSize) {
  var container = document.createElement('div');
  container.style.visibility = 'hidden';
  container.style.fontFamily = fontFamily;
  container.style.fontSize = fontSize + 'px';
  container.style.display = 'inline';
  document.body.appendChild(container);
  container.innerHTML = this;
  var pxLength = container.offsetWidth;
  container.parentNode.removeChild(container);
  return pxLength;
};

// this is the function that will resize our text if it too long
// classNameTarget (String) = the className of the element we need to resize e a 
//    tag or an id but you'll need to make modification to this function!
// maxWidth (int) = the max width (in px) of your final string
// fontFamily (String) = the family currently used by your string(wrong one might lead
//    to wrong result!)
// fontSize (int) = the initial font-size of your string
var testWidth = function(classNameTarget, maxWidth, fontFamily, fontSize) {
    maxWidth = maxWidth || 100;
    // first we get all targets
    var containers = document.getElementsByClassName(classNameTarget);
    for (var i = 0; i < containers.length; i++) {
    // for each of them we fetch their current length
        var length = containers[i].innerHTML.textWidth(fontFamily, fontSize);
        if (length > maxWidth){
        // if the current length is bigger then we resize it by using a span styling with
        // the new font-size
            containers[i].innerHTML = "<span style=\"font-size:" + Math.floor(parseInt(fontSize) / (length / maxWidth)) + "px;\">" + containers[i].innerHTML + "</span>";
        }
    }
};

// we want our cell to have text no longer than 75px while having them starting at 50px 
// font-size in arial
testWidth("firstname", 75, "arial", 50);
testWidth("lastname", 75, "arial", 50);
<table>
  <thead>
    <tr>
      <th>firstname</th>
      <th>lastname</th>
    </tr>
  </thead>
  <tbody style="font-size: 50px; font-family: arial;">
    <tr>
      <td class="firstname">Bob</td>
      <td class="lastname">Tomysonorubia</td>
    </tr>
    <tr>
      <td class="firstname">John</td>
      <td class="lastname">Doe</td>
    </tr>
    <tr>
      <td class="firstname">François-Xavier</td>
      <td class="lastname">De la nouvelle Orléan</td>
    </tr>
  </tbody>
</table>

Ответ 5

Я предлагаю крошечный пример. На чистом JavaScript.

https://gist.github.com/dejurin/9bef02be6876e068ee276bee31cb3bcb

    "use strict";
    (function(w, d) {
        var fit = d.getElementById("fit");
        var wrap = d.getElementById("wrap");
        fontFitResize(fit, wrap);
        
            
    function fontFitResize(fit, wrap, step = 0.5) {
        var currentSize;
        while(fit.offsetWidth < wrap.offsetWidth) {
            currentSize = parseFloat(w.getComputedStyle(wrap, null).getPropertyValue('font-size'));
            wrap.style.fontSize = (currentSize - step) + 'px';
            console.log(wrap.style.fontSize);
        }
    }
        
    })(window, document);
.fit {
border: 1px solid #ff0000;
white-space:nowrap;
font-size:24px;
width:200px;
}
<div id="fit" class="fit">
  <span id="wrap">Resize font depending on string length</span>
</div>