web-dev-qa-db-fra.com

redimensionner la police pour tenir dans un div (sur une ligne)

Des questions similaires ont été posées, mais les solutions correspondent à ce que j'essaie de faire. En gros, j'ai un article avec un titre (<h1>). Je ne veux pas contrôler la longueur du titre, mais je ne veux pas non plus que le titre apparaisse sur plusieurs lignes. Est-il possible avec css ou jQuery de redimensionner le texte en fonction de la largeur d'une balise <div>?

Je sais ce que je ferais avec le pseudocode si je pouvais détecter le chevauchement du texte avec le bord du <div>:

var fontize = $("#title").css("font-size");
var i = /*remove unit from integer*/
while( /*text overlaps div*/ ){
    $("#title").css("font-size", --i+"pt");
}

S'il y a un attribut CSS que je peux définir, ce serait encore mieux, mais je n'arrive pas à en trouver un (le débordement ne fonctionnerait pas dans cette situation). 

58
TCCV

CSS non, Javascript oui

Il est impossible de faire cela en utilisant CSS, mais vous pouvez le faire en javascript/jQuery. Pour vous aider avec votre pseudo-code car vous savez déjà quoi faire. C'est juste que vous ne savez pas comment détecter l'excès de largeur.

Le meilleur moyen serait d'avoir une DIV avec le style suivant (au moins):

position: absolute;
visibility: hidden;
white-space: nowrap;
font-family: /* same as your title's */

copiez ensuite votre texte et définissez une taille de police de départ. Ensuite, vous pouvez parcourir la boucle while et vous arrêter lorsque la largeur de div est appropriée. Ensuite, définissez la taille de police calculée sur votre titre d'origine.

De cette façon, cette vérification sera masquée aux yeux de l'utilisateur et fonctionnera donc plus rapidement.

BTW: C’est la façon habituelle de travailler avec les scripts textarea à croissance automatique. Ils utilisent des div factices avec les mêmes paramètres de style que la zone de texte d'origine et ajustent la hauteur de la zone lorsque l'utilisateur tape du texte. Ainsi, la zone de texte peut être assez petite au début, mais si l'utilisateur tape beaucoup de texte, elle s'agrandira automatiquement pour s'adapter au contenu.

Optimisation de la boucle While

Vous pouvez optimiser votre boucle while afin de réduire considérablement le nombre d'itérations en procédant comme suit:

  1. Définir une taille de police de départ.
  2. obtenez la largeur de test DIV.
  3. calculer le facteur de largeur entre orig_div et test_div.
  4. ajuster la taille de la police par ce facteur plutôt que d’augmenter/diminuer d’une unité
  5. test test_div width
44
Robert Koritnik

J'ai eu un problème similaire, ce qui m'a fait écrire mon propre plugin pour cela. Jetez un coup d'œil à jquery-quickfit (qui est assez similaire à la solution de Robert Koritnik , que j'aime beaucoup). 

Pour éviter que le titre ne s'étende sur plusieurs lignes, ajoutez simplement un style CSS de:

white-space:nowrap;

à l'élément.

Après avoir inclus jquery et quickfit dans l'en-tête. Vous pouvez déclencher quickfit avec:

$('h1').quickfit();

Il mesure et calcule une taille invariante par taille pour chaque lettre du texte à adapter et l'utilise pour calculer la taille de police optimale suivante qui s'adapte au texte dans le conteneur.

Les calculs sont mis en cache, ce qui le rend très rapide lorsque vous devez ajuster plusieurs textes ou plusieurs fois, comme par exemple lors du redimensionnement d'une fenêtre (il n'y a presque aucune perte de performances lors du redimensionnement).

Démo pour une situation similaire à la vôtre

Une documentation complémentaire, des exemples et le code source se trouvent sur la page du projet.

22
chunks_n_bits

Voici 3 fonctions que j'utilise fréquemment pour obtenir la largeur et la hauteur du texte et ajuster la taille à la largeur du conteneur.

  • getTextWidth () vous renverra la largeur réelle du texte contenu dans l'initiateur.
  • getTextHeight (width) renverra la hauteur réelle du texte enveloppé contenu dans l'initiateur avec une certaine largeur spécifiée.
  • autoTextSize (minSize, maxSize, truncate) redimensionne le texte dans le conteneur afin qu'il s'adapte, en considérant une taille minimale et maximale.
  • autoTruncateText () n'affichera que les caractères que l'utilisateur peut réellement voir et termine le texte avec '...'.
(function ($) {
  $.fn.getTextWidth = function() {
    var spanText = $("BODY #spanCalculateTextWidth");

    if (spanText.size() <= 0) {
      spanText = $("<span id='spanCalculateTextWidth' style='filter: alpha(0);'></span>");
      spanText.appendTo("BODY");
    }

    var valu = this.val();
    if (!valu) valu = this.text();

    spanText.text(valu);

    spanText.css({
      "fontSize": this.css('fontSize'),
      "fontWeight": this.css('fontWeight'),
      "fontFamily": this.css('fontFamily'),
      "position": "absolute",
      "top": 0,
      "opacity": 0,
      "left": -2000
    });

    return spanText.outerWidth() + parseInt(this.css('paddingLeft')) + 'px';
  };

  $.fn.getTextHeight = function(width) {
    var spanText = $("BODY #spanCalculateTextHeight");

    if (spanText.size() <= 0) {
      spanText = $("<span id='spanCalculateTextHeight'></span>");
      spanText.appendTo("BODY");
    }

    var valu = this.val();
    if (!valu) valu = this.text();

    spanText.text(valu);

    spanText.css({
      "fontSize": this.css('fontSize'),
      "fontWeight": this.css('fontWeight'),
      "fontFamily": this.css('fontFamily'),
      "top": 0,
      "left": -1 * parseInt(width) + 'px',
      "position": 'absolute',
      "display": "inline-block",
      "width": width
    });

    return spanText.innerHeight() + 'px';
  };

  /**
   * Adjust the font-size of the text so it fits the container.
   *
   * @param minSize     Minimum font size?
   * @param maxSize     Maximum font size?
   * @param truncate    Truncate text after sizing to make sure it fits?
   */
  $.fn.autoTextSize = function(minSize, maxSize, truncate) {
    var _self = this,
        _width = _self.innerWidth(),
        _textWidth = parseInt(_self.getTextWidth()),
        _fontSize = parseInt(_self.css('font-size'));

    while (_width < _textWidth || (maxSize && _fontSize > parseInt(maxSize))) {
      if (minSize && _fontSize <= parseInt(minSize)) break;

      _fontSize--;
      _self.css('font-size', _fontSize + 'px');

      _textWidth = parseInt(_self.getTextWidth());
    }

    if (truncate) _self.autoTruncateText();
  };

  /**
   * Function that truncates the text inside a container according to the
   * width and height of that container. In other words, makes it fit by chopping
   * off characters and adding '...'.
   */
  $.fn.autoTruncateText = function() {
    var _self = this,
        _width = _self.outerWidth(),
        _textHeight = parseInt(_self.getTextHeight(_width)),
        _text = _self.text();

    // As long as the height of the text is higher than that
    // of the container, we'll keep removing a character.
    while (_textHeight > _self.outerHeight()) {
      _text = _text.slice(0,-1);
      _self.text(_text);
      _textHeight = parseInt(_self.getTextHeight(_width));
      _truncated = true;
    }

    // When we actually truncated the text, we'll remove the last
    // 3 characters and replace it with '...'.
    if (!_truncated) return;
    _text = _text.slice(0, -3);

    // Make sure there is no dot or space right in front of '...'.
    var lastChar = _text[_text.length - 1];
    if (lastChar == ' ' || lastChar == '.') _text = _text.slice(0, -1);
    _self.text(_text + '...');
  };
})(jQuery);
20
Clovis Six

@Clovis Six Merci pour votre réponse. Cela m'est très utile. Dommage que je ne puisse pas vous remercier plus qu'un vote.

Note: Je dois changer le "$ J (" pour "$ (" pour que cela fonctionne sur ma config.

evendo, cela sort du champ de cette question et non de l’utilisation de SO, j’ai étendu votre code pour le faire fonctionner pour une boîte multi-lignes à hauteur maximale.

  /**
   * Adjust the font-size of the text so it fits the container
   *
   * support multi-line, based on css 'max-height'.
   * 
   * @param minSize     Minimum font size?
   * @param maxSize     Maximum font size?
   */
  $.fn.autoTextSize_UseMaxHeight = function(minSize, maxSize) {
    var _self = this,
        _width = _self.innerWidth(),
        _boxHeight = parseInt(_self.css('max-height')),
        _textHeight = parseInt(_self.getTextHeight(_width)),
        _fontSize = parseInt(_self.css('font-size'));

    while (_boxHeight < _textHeight || (maxSize && _fontSize > parseInt(maxSize))) {
      if (minSize && _fontSize <= parseInt(minSize)) break;

      _fontSize--;
      _self.css('font-size', _fontSize + 'px');

      _textHeight = parseInt(_self.getTextHeight(_width));
    }
  };

PS: Je sais que cela devrait être un commentaire, mais les commentaires ne me permettent pas de poster le code correctement.

4
Christ

Aujourd'hui, j'ai créé un plugin jQuery appelé jQuery Responsive Headlines qui fait exactement ce que vous voulez: il ajuste la taille de la police d'un titre pour l'adapter à son élément conteneur (un div, le corps ou autre). Certaines options de Nice peuvent être définies pour personnaliser le comportement du plug-in. J'ai également pris la précaution d'ajouter le support de la fonctionnalité Ben Altmans Throttle/Debounce pour augmenter les performances et alléger la charge du navigateur/de l'ordinateur lorsque l'utilisateur redimensionne la page. la fenêtre. Dans son cas d'utilisation le plus simple, le code ressemblerait à ceci:

$('h1').responsiveHeadlines();

... qui transformerait tous les éléments h1 de la page en titres réactifs d’une ligne adaptant leur taille à l’élément parent/conteneur.

Vous trouverez le code source et une description plus longue du plugin sur cette page GitHub

Bonne chance!

4
tkahn

Très simple. Créez une étendue pour le texte, obtenez la largeur et réduisez la taille de la police jusqu'à ce que l'étendue ait la même largeur que le conteneur div:

while($("#container").find("span").width() > $("#container").width()) {
    var currentFontSize = parseInt($("#container").find("span").css("font-size")); 
    $("#container").find("span").css("font-size",currentFontSize-1); 
}
3
MeV

Il existe un plugin jQuery disponible sur github qui fait probablement ce que vous voulez. C'est ce qu'on appelle jquery-quickfit. Il utilise Jquery pour fournir une approche rapide et sale d’ajustement de texte dans son conteneur environnant.

HTML:

<div id="quickfit">Text to fit*</div>

Javascript:

<script src=".../jquery.min.js" type="text/javascript" />
<script src="../script/jquery.quickfit.js" type="text/javascript" />

<script type="text/javascript">
  $(function() {
    $('#quickfit').quickfit();
  });
</script>

Plus d'informations: https://github.com/chunksnbits/jquery-quickfit

1
Melicerte

Je n'aimais pas les autres solutions, alors en voici une autre. Il nécessite la balise que vous redimensionnez le texte à l'intérieur de:

  1. Être à hauteur fixe

  2. Ne soyez pas si long que cela dépasse les limites à 10 pixels, l’idée étant que vous ne voulez pas que le film tire au-dessous de cela et que le texte redimensionné devienne illisible. Ce n’est pas un problème dans notre cas d’utilisation, mais si cela est possible dans votre cas, vous voudrez bien réfléchir à la possibilité de tronquer séparément avant d’exécuter ceci.

Il utilise une recherche binaire pour trouver la taille optimale. Je suppose donc qu’il surpasse beaucoup d’autres solutions proposées ici et ailleurs qui ne font que réduire la taille de la police d’un pixel à plusieurs reprises. De nos jours, la plupart des navigateurs prennent également en charge les décimales dans les tailles de police. Ce script présente certains avantages, dans la mesure où la meilleure réponse, quelle qu’elle soit, restera à l’écart. Même s’il s’agit d’une décimale relativement longue. Vous pouvez facilement remplacer le max - fontSize < .1 par une autre valeur que .1 afin d'obtenir moins de précision pour une utilisation moindre du processeur.

Usage:

$('div.event').fitText();

Code:

(function() {
    function resizeLoop(testTag, checkSize) {
        var fontSize = 10;
        var min = 10;
        var max = 0;
        var exceeded = false;

        for(var i = 0; i < 30; i++) {
            testTag.css('font-size', fontSize);
            if (checkSize(testTag)) {
                max = fontSize;
                fontSize = (fontSize + min) / 2;
            } else {
                if (max == 0) {
                    // Start by growing exponentially
                    min = fontSize;
                    fontSize *= 2;
                } else {
                    // If we're within .1px of max anyway, call it a day
                    if (max - fontSize < .1)
                        break;

                    // If we've seen a max, move half way to it
                    min = fontSize;
                    fontSize = (fontSize + max) / 2;
                }
            }
        }

        return fontSize;
    }

    function sizeText(tag) {
        var width = tag.width();
        var height = tag.height();

        // Clone original tag and append to the same place so we keep its original styles, especially font
        var testTag = tag.clone(true)
        .appendTo(tag.parent())
        .css({
            position: 'absolute',
            left: 0, top: 0,
            width: 'auto', height: 'auto'
        });

        var fontSize;

        // TODO: This decision of 10 characters is arbitrary. Come up
        // with a smarter decision basis.
        if (tag.text().length < 10) {
            fontSize = resizeLoop(testTag, function(t) {
                return t.width() > width || t.height() > height;
            });
        } else {
            testTag.css('width', width);
            fontSize = resizeLoop(testTag, function(t) {
                return t.height() > height;
            });
        }

        testTag.remove();
        tag.css('font-size', fontSize);
    };

    $.fn.fitText = function() {
        this.each(function(i, tag) {
            sizeText($(tag));
        });
    };
})();

http://jsfiddle.net/b9chris/et6N6/1/

1
Chris Moschini

C’est peut-être excessif pour ce dont vous avez besoin, mais j’ai trouvé cette bibliothèque très utile:

http://fittextjs.com/

Cela ne vaut que pour les lignes simples, donc je ne suis pas sûr que cela corresponde à vos besoins.

1
Cory

J'ai créé très peu de choses jQuery pour ajuster la taille de la police de votre div. Cela fonctionne même avec plusieurs lignes, mais n'augmentera que la taille de la police (vous pouvez simplement définir une taille de police par défaut de 1px si vous le souhaitez) . Aucun autre truc de fantaisie, div caché ou autre. Ajoutez simplement la classe quickfit à votre div.

$(".quickfit").each(function() {
    var jThis=$(this);
    var fontSize=parseInt(jThis.css("font-size"));
    var originalLines=jThis.height()/fontSize;

    for(var i=0;originalLines>=jThis.height()/fontSize && i<30;i++)
    { jThis.css("font-size",""+(++fontSize)+"px"); }
    jThis.css("font-size",""+(fontSize-1)+"px");
});
0
Vincent

Découvrez cet exemple ici http://codepen.io/LukeXF/pen/yOQWNb , vous pouvez utiliser une fonction simple lors du chargement et du redimensionnement d'une page pour que la magie opère. Veillez toutefois à ne pas retarder le client par trop d'appels de fonction. (L'exemple a un délai).

function resize() {  
    $('.resize').each(function(i, obj) {
        $(this).css('font-size', '8em');

        while ($(this).width() > $(this).parent().width()) {
            $(this).css('font-size', (parseInt($(this).css('font-size')) - 1) + "px");
        }
    });
}
0
LukeXF

Ma solution, une extension jQuery basée sur La réponse de Robert Koritnik :

$.fn.fitToWidth=function(){
    $(this).wrapInner("<span style='display:inline;font:inherit;white-space:inherit;'></span>").each(function(){
        var $t=$(this);
        var a=$t.outerWidth(),
            $s=$t.children("span"),
            f=parseFloat($t.css("font-size"));
        while($t.children("span").outerWidth() > a) $t.css("font-size",--f);
        $t.html($s.html());
    });
}

Cela crée en fait un intervalle temporaire héritant de propriétés importantes et compare la différence de largeur. Certes, la boucle While doit être optimisée (réduction d'un pourcentage de différence calculée entre les deux tailles).

Exemple d'utilisation:

$(function(){
    $("h1").fitToWidth();
});
0
SEoF

Vous pouvez accomplir cela dans css dans certaines circonstances, mais cela dépend du contexte et de la fragilité.

css font-tailles peut accepter des tailles relatives à l'écran. SI l'élément contenant votre en-tête est dimensionné par rapport à la taille de l'écran, vous pouvez le faire en définissant simplement font-size: 3vw ou une taille appropriée pour que le texte tienne sur une ligne. Si l'écran est redimensionné, le texte suivra. Notez que la largeur dépendra du contenu exact du texte. Si vous ajoutez des caractères à du texte qui ne s’insère que sur une seule ligne, vous devrez peut-être ajuster votre taille de police.

Voir ce violon: https://jsfiddle.net/mguk8v27/

0
Scott

Je comprends que cette question semble avoir reçu une réponse assez complète, mais il y a eu des cas où des solutions pourraient causer d’autres problèmes. Par exemple, la bibliothèque de tkahn semblait très utile, mais elle modifiait l'affichage de l'élément auquel elle était attachée, ce qui pourrait poser problème. Dans mon cas, cela m'a empêché de centrer le texte verticalement et horizontalement. Après quelques essais et expérimentations, j'ai mis au point une méthode simple faisant appel à jQuery pour adapter le texte sur une ligne sans avoir à modifier aucun attribut de l'élément parent. Notez que dans ce code, j'ai utilisé la suggestion de Robert Koritnik pour optimiser la boucle while. Pour utiliser ce code, ajoutez simplement la classe "font_fix" à tout div contenant du texte devant l’être ajusté sur une ligne. Pour un en-tête, cela peut nécessiter un div supplémentaire autour de l'en-tête. Ensuite, appelez cette fonction une fois pour un div de taille fixe ou définissez-la sur un écouteur de redimensionnement et/ou d’orientation pour différentes tailles.

function fixFontSize(minimumSize){
  var x = document.getElementsByClassName("font_fix");
  for(var i = 0; i < x.length; i++){
    x[i].innerHTML = '<div class="font_fix_inner" style="white-space: nowrap; display: inline-block;">' + x[i].innerHTML + '</div>';

    var y = x[i].getElementsByClassName("font_fix_inner")[0];
    var size = parseInt($("#" + x[i].id).css("font-size"));

    size *= x[i].clientWidth / y.clientWidth;
    while(y.clientWidth > x[i].clientWidth){
      size--;
      if(size <= minimumSize){
        size = minimumSize;
        y.style.maxWidth = "100%";
        y.style.overflow = "hidden";
        y.style.textOverflow = "Ellipsis";
        $("#" + x[i].id).css("font-size", size + "px");
        break;
      }
      $("#" + x[i].id).css("font-size", size + "px");
    }
  }
}

Maintenant, j'ai ajouté un cas supplémentaire où le texte est coupé s'il devient trop petit (en dessous du seuil minimal défini dans la fonction) pour plus de commodité. Une autre chose qui se produit lorsque ce seuil est atteint et que vous ne souhaitez pas ou ne pas souhaiter est le changement de la largeur maximale à 100%. Cela devrait être changé pour le scénario de chaque utilisateur. Enfin, le but de publier cette réponse en tant que substitut est sa capacité à centrer le contenu dans la division parent. Cela peut être facilement fait en ajoutant des attributs css à la classe div interne comme suit:

.font_fix_inner {
  position: relative;
  float: center;
  top: 50%;
  -webkit-transform: translateY(-50%);
  transform: translateY(-50%);
}

J'espère que cela a aidé quelqu'un!

0
Arix Zajicek