320 votes

Taille automatique du texte dynamique pour remplir un conteneur de taille fixe

J'ai besoin d'afficher le texte saisi par l'utilisateur dans un div de taille fixe. Ce que je veux, c'est que la taille de la police soit automatiquement ajustée pour que le texte remplisse la boîte autant que possible.

Ainsi, si le div fait 400px x 300px. Si quelqu'un entre ABC, la police sera très grande. S'il entre un paragraphe, la police sera minuscule.

Il faudrait probablement commencer par une taille de police maximale - peut-être 32px, et tant que le texte est trop grand pour entrer dans le conteneur, réduire la taille de la police jusqu'à ce qu'il rentre.

130 votes

C'est probablement la fonction la plus étonnante qui aurait dû être ajoutée à HTML5/CSS3 sans avoir besoin de JS.

0 votes

J'ai effectué quelques mesures en modifiant la longueur d'un texte dynamique et la taille du conteneur pour déterminer la taille de la police qui permettra au texte de s'adapter parfaitement. Et après avoir effectué une analyse de régression, j'ai mis au point une fonction mathématique simple qui génère automatiquement la meilleure taille de police.

2 votes

Il s'avère en fait que le graphique qui vous donne la meilleure taille de police est donné par f(x) = g(lettres) * (x / 1000)^n, où g(x) est une fonction simple, n varie en fonction de la police que vous utilisez. (Bien qu'elle puisse avoir une valeur standard pour toutes les polices, si vous ne voulez pas la modifier pour qu'elle soit absolument parfaite...). x est la taille en pixels carrés du conteneur.

171voto

GeekyMonkey Points 5036

Merci Attaque . Je voulais utiliser jQuery.

Vous m'avez orienté dans la bonne direction, et c'est ce que j'ai obtenu :

Voici un lien vers le plugin : https://plugins.jquery.com/textfill/
Et un lien vers la source : http://jquery-textfill.github.io/

;(function($) {
    $.fn.textfill = function(options) {
        var fontSize = options.maxFontPixels;
        var ourText = $('span:visible:first', this);
        var maxHeight = $(this).height();
        var maxWidth = $(this).width();
        var textHeight;
        var textWidth;
        do {
            ourText.css('font-size', fontSize);
            textHeight = ourText.height();
            textWidth = ourText.width();
            fontSize = fontSize - 1;
        } while ((textHeight > maxHeight || textWidth > maxWidth) && fontSize > 3);
        return this;
    }
})(jQuery);

$(document).ready(function() {
    $('.jtextfill').textfill({ maxFontPixels: 36 });
});

et mon html est comme ceci

<div class='jtextfill' style='width:100px;height:50px;'>
    <span>My Text Here</span>
</div>

C'est mon premier plugin jquery, donc il n'est probablement pas aussi bon qu'il devrait l'être. Les conseils sont les bienvenus.

8 votes

En fait, je viens juste de le nettoyer et de le packager en tant que plugin disponible sur jquery.com à l'adresse suivante plugins.jquery.com/projet/TextFill

3 votes

@GeekyMonkey, avez-vous retiré le plugin ? Je viens de suivre un lien vers cette page et j'ai pensé jeter un coup d'oeil, mais les liens de jQuery.com vers votre site reviennent. 404 .

0 votes

Note : J'ai constaté que pour une raison quelconque, ce plugin ne fonctionne que lorsque le div ($('.jtextfill') dans l'exemple ci-dessus) fait partie du document Root. Il semble que .width() renvoie zéro lorsque la div est intégrée dans d'autres divs.

53voto

Marcus Ekwall Points 14489

Je n'ai pas trouvé les solutions précédentes suffisamment adéquates en raison de leurs mauvaises performances, alors j'ai créé la mienne qui utilise de simples mathématiques au lieu de boucles. Elle devrait également fonctionner correctement dans tous les navigateurs.

Selon ce test de performance il est beaucoup plus rapide que les autres solutions trouvées ici.

(function($) {
    $.fn.textfill = function(maxFontSize) {
        maxFontSize = parseInt(maxFontSize, 10);
        return this.each(function(){
            var ourText = $("span", this),
                parent = ourText.parent(),
                maxHeight = parent.height(),
                maxWidth = parent.width(),
                fontSize = parseInt(ourText.css("fontSize"), 10),
                multiplier = maxWidth/ourText.width(),
                newSize = (fontSize*(multiplier-0.1));
            ourText.css(
                "fontSize", 
                (maxFontSize > 0 && newSize > maxFontSize) ? 
                    maxFontSize : 
                    newSize
            );
        });
    };
})(jQuery);

Si vous voulez contribuer, j'ai ajouté ceci à Gist .

0 votes

Joli script, mais je pense que l'OP cherche à remplir la totalité de la zone de texte, plutôt que de simplement adapter une seule ligne de texte horizontalement. Il semble que c'est ce que fait le Fiddle dans votre lien Gist.

1 votes

@Jon, merci ! Vous avez raison de dire que mon script ne fait pas de lignes multiples, mais encore une fois, le PO n'a pas spécifiquement demandé cela, donc votre hypothèse pourrait être fausse. En outre, ce type de comportement n'a pas beaucoup de sens imo. Je suppose que la meilleure façon d'ajouter le support multi-lignes serait de diviser la chaîne de caractères en fonction de la quantité de mots, puis de calculer chaque partie avec le script ci-dessus et ce serait très probablement plus rapide de toute façon.

0 votes

Oui, vous avez tout à fait raison, la question de l'OP est un peu ambiguë. Je n'avais pas l'intention de sauter à une conclusion aussi vite. Pour ceux qui cherchent un support multiligne, j'ai eu du succès avec le code de sandstrom ci-dessus. Sinon, pour du texte d'une seule ligne, c'est une excellente solution :)

36voto

attack Points 1085

Même si j'adore les votes positifs que j'obtiens de temps en temps pour cette réponse (merci !), ce n'est vraiment pas la meilleure façon d'aborder ce problème. Veuillez consulter d'autres réponses merveilleuses, en particulier celles qui ont trouvé des solutions sans bouclage.


Néanmoins, à titre de référence, voici mon première réponse :

<html>
<head>
<style type="text/css">
    #dynamicDiv
    {
    background: #CCCCCC;
    width: 300px;
    height: 100px;
    font-size: 64px;
    overflow: hidden;
    }
</style>

<script type="text/javascript">
    function shrink()
    {
        var textSpan = document.getElementById("dynamicSpan");
        var textDiv = document.getElementById("dynamicDiv");

        textSpan.style.fontSize = 64;

        while(textSpan.offsetHeight > textDiv.offsetHeight)
        {
            textSpan.style.fontSize = parseInt(textSpan.style.fontSize) - 1;
        }
    }
</script>

</head>
<body onload="shrink()">
    <div id="dynamicDiv"><span id="dynamicSpan">DYNAMIC FONT</span></div>
</body>
</html>

Et voici une version avec classes :

<html>
<head>
<style type="text/css">
.dynamicDiv
{
    background: #CCCCCC;
    width: 300px;
    height: 100px;
    font-size: 64px;
    overflow: hidden;
}
</style>

<script type="text/javascript">
    function shrink()
    {
        var textDivs = document.getElementsByClassName("dynamicDiv");
        var textDivsLength = textDivs.length;

        // Loop through all of the dynamic divs on the page
        for(var i=0; i<textDivsLength; i++) {

            var textDiv = textDivs[i];

            // Loop through all of the dynamic spans within the div
            var textSpan = textDiv.getElementsByClassName("dynamicSpan")[0];

            // Use the same looping logic as before
            textSpan.style.fontSize = 64;

            while(textSpan.offsetHeight > textDiv.offsetHeight)
            {
                textSpan.style.fontSize = parseInt(textSpan.style.fontSize) - 1;
            }

        }

    }
</script>

</head>
<body onload="shrink()">
    <div class="dynamicDiv"><span class="dynamicSpan">DYNAMIC FONT</span></div>
    <div class="dynamicDiv"><span class="dynamicSpan">ANOTHER DYNAMIC FONT</span></div>
    <div class="dynamicDiv"><span class="dynamicSpan">AND YET ANOTHER DYNAMIC FONT</span></div>
</body>
</html>

3 votes

J'ai trouvé que cela fonctionnait mieux avec offsetWidth J'ai également dû créer une variable pour la taille, puis ajouter les px. textSpan.style.fontSize = size+"px";

2 votes

Il est certain que '+"px"' est nécessaire.

0 votes

Merci, sauveur de vie ! Je ne connais pas jQuery, donc je m'en tiens à votre solution :)

32voto

Hoffmann Points 3585

La plupart des autres réponses utilisent une boucle pour réduire la taille de la police jusqu'à ce qu'elle tienne dans le div, ce qui est TRÈS lent puisque la page doit rendre à nouveau l'élément chaque fois que la police change de taille. J'ai finalement dû écrire mon propre algorithme pour que cet élément fonctionne d'une manière qui me permette de mettre à jour son contenu périodiquement sans geler le navigateur de l'utilisateur. J'ai ajouté quelques autres fonctionnalités (rotation du texte, ajout de rembourrage) et je l'ai packagé sous forme de plugin jQuery, que vous pouvez obtenir à l'adresse suivante :

https://github.com/DanielHoffmann/jquery-bigtext

appelez simplement

$("#text").bigText();

et il s'adaptera parfaitement à votre conteneur.

Voyez-le en action ici :

http://danielhoffmann.github.io/jquery-bigtext/

Pour l'instant, il présente quelques limitations : la division doit avoir une hauteur et une largeur fixes et il ne permet pas d'envelopper le texte sur plusieurs lignes.

Je vais m'efforcer d'obtenir une option permettant de définir la taille maximale des caractères.

Edit : J'ai trouvé d'autres problèmes avec le plugin, il ne gère pas d'autres modèles de boîtes que le modèle standard et le div ne peut pas avoir de marges ou de bordures. Je vais travailler dessus.

Edit2 : J'ai maintenant corrigé ces problèmes et limitations et ajouté plus d'options. Vous pouvez définir une taille de police maximale et vous pouvez également choisir de limiter la taille de la police en utilisant la largeur, la hauteur ou les deux. Je vais travailler sur l'acceptation des valeurs max-width et max-height dans l'élément wrapper.

Edit3 : J'ai mis à jour le plugin à la version 1.2.0. Nettoyage majeur du code et nouvelles options (verticalAlign, horizontalAlign, textAlign) et support pour les éléments internes à l'intérieur de la balise span (comme les sauts de ligne ou les icônes font-awesome).

1 votes

Je me demande pourquoi l'habillage du texte en plusieurs lignes n'est pas supporté ?

1 votes

@ManishSapariya C'est possible, mais vous devez ajouter les sauts de ligne (balises br) manuellement. La raison pour laquelle je ne prends pas en charge l'habillage automatique du texte est que pour le rendre rapide (il suffit de changer la taille de la police deux fois au lieu de plusieurs), je dois partir du principe que le texte ne s'enroulera pas entre les mots. La façon dont mon plugin fonctionne est de définir la taille de la police à 1000px puis de voir le facteur de la taille du texte par rapport au conteneur, je réduis alors la taille de la police par le même facteur. Pour prendre en charge l'habillage normal du texte, je devrais utiliser l'approche lente (réduire la taille de la police plusieurs fois), ce qui est très lent.

0 votes

Hey ! Comme il n'y a pas de messagerie privée ici, sur StackOverflow, je vais devoir vous demander en commentant votre réponse. J'adore votre plugin jQuery, mais je n'arrive pas à le faire fonctionner pour moi. J'ai inclus la bonne bibliothèque jQuery, téléchargé votre plugin et l'ai inclus. Maintenant, lorsque j'essaie de l'utiliser, la console indique "Uncaught TypeError : undefined is not a function". Est-ce un problème que vous connaissez ? Savez-vous comment le corriger ? Merci

9voto

sandstrom Points 2420

Ceci est basé sur ce que GeekyMonkey a posté ci-dessus, avec quelques modifications.

; (function($) {
/**
* Resize inner element to fit the outer element
* @author Some modifications by Sandstrom
* @author Code based on earlier works by Russ Painter (WebDesign@GeekyMonkey.com)
* @version 0.2
*/
$.fn.textfill = function(options) {

    options = jQuery.extend({
        maxFontSize: null,
        minFontSize: 8,
        step: 1
    }, options);

    return this.each(function() {

        var innerElements = $(this).children(':visible'),
            fontSize = options.maxFontSize || innerElements.css("font-size"), // use current font-size by default
            maxHeight = $(this).height(),
            maxWidth = $(this).width(),
            innerHeight,
            innerWidth;

        do {

            innerElements.css('font-size', fontSize);

            // use the combined height of all children, eg. multiple <p> elements.
            innerHeight = $.map(innerElements, function(e) {
                return $(e).outerHeight();
            }).reduce(function(p, c) {
                return p + c;
            }, 0);

            innerWidth = innerElements.outerWidth(); // assumes that all inner elements have the same width
            fontSize = fontSize - options.step;

        } while ((innerHeight > maxHeight || innerWidth > maxWidth) && fontSize > options.minFontSize);

    });

};

})(jQuery);

0 votes

La différence est qu'il peut prendre plusieurs éléments enfants, et qu'il prend en compte le remplissage. Utilise font-size comme taille maximale par défaut, pour éviter de mélanger javascript et css.

5 votes

C'est génial, mais comment l'utiliser ? Je fais $('.outer').textfill() ; et je n'obtiens aucun changement.

3 votes

Merci, c'est une très belle mise en œuvre. Une chose que j'ai rencontrée : si vous avez affaire à de très longues chaînes de texte et à des conteneurs très étroits, la chaîne de texte dépassera du conteneur, mais la largeur extérieure sera toujours calculée comme si elle ne dépassait pas. Ajoutez "word-wrap : break-word ;" dans votre CSS pour ce conteneur, cela résoudra ce problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X