CSS pur pour rendre la taille de police sensible en fonction de la quantité dynamique de caractères

298

Je sais que cela pourrait être résolu assez facilement avec Javascript, mais je ne suis intéressé que par une solution CSS pure.

Je veux un moyen de redimensionner dynamiquement le texte afin qu'il tienne toujours dans un div fixe. Voici l'exemple de balisage:

<div style="width: 200px; height: 1em; overflow: hidden;">
  <p>Some sample dynamic amount of text here</p>
</div>

Je pensais que cela pourrait être possible en spécifiant la largeur du conteneur en ems et en obtenant la taille de police pour hériter de cette valeur?

DMTintner
la source
1
Wow, attends. La spécification de la chose widthen ems va dans l'autre sens. C'est le widthqui dépend du font-size. @JosephSilber C'est exactement ce que je pensais.
Ana
1
Je suis curieux de cette question. Quel est le lecteur pour utiliser une solution CSS pure au lieu d'écrire une simple fonction javascript?
Austin Mullins
22
Le lecteur est simplement dû au fait que le problème existe et qu'une solution CSS pure serait incroyable. Pensez aux possibilités d'appliquer seulement quelques styles et sachez que votre contenu dynamique ne cassera jamais le design.
DMTintner
4
Juste au cas où quelqu'un tomberait sur cette question et ne se gênerait pas d'utiliser JS, voici un plugin pour le faire fittextjs.com
DMTintner
2
fitText a des limites. Par exemple, je ne veux l'exécuter que pour de petits écrans, au-delà d'une largeur de 500 pixels environ, je ne veux plus que mes titres explosent. Cela nécessite d'écrire plus de JavaScript. La séparation des préoccupations se décompose très rapidement lorsque vous utilisez JavaScript pour la mise en page. Ce n'est jamais juste une doublure rapide.
Costa

Réponses:

514

Je viens de découvrir que cela est possible en utilisant des unités VW. Ce sont les unités associées à la définition de la largeur de la fenêtre. Il existe certains inconvénients, tels que le manque de prise en charge des navigateurs hérités, mais c'est certainement quelque chose à considérer sérieusement. De plus, vous pouvez toujours fournir des solutions de rechange pour les navigateurs plus anciens, comme ceci:

p {
    font-size: 30px;
    font-size: 3.5vw;
}

http://css-tricks.com/viewport-sized-typography/ et https://medium.com/design-ux/66bddb327bb1

DMTintner
la source
30
Donnerait 2 ups pour celui-ci. Solutions CSS pour la victoire!
Mihkel L.
34
Conseil de pro: vous pouvez empêcher le texte de devenir trop petit et reprendre le contrôle en faisant font-size:calc(100% + 2vw);ou similaire. C'est un peu min-font-size. La prise en charge du navigateur pour calcest similaire à vw.
Prinzhorn
116
Surévalué, mais maintenant je me demande si cela répond réellement à la question d'origine. Ma compréhension est que la longueur de votre texte est dynamique et que vous souhaitez modifier la taille de la police pour qu'elle corresponde toujours à la largeur de votre div(200px). Comment cela résout-il ce problème?
Floremin
26
Je suis d'accord avec le sentiment de @ Floremin. Cela met à l'échelle TOUTES les tailles de police en fonction de la largeur / hauteur du port d'affichage, et non du conteneur de la largeur / hauteur du texte.
MickJuice
24
@Floremin a raison, cela ne résout pas la question. Cela calcule la taille de la police en fonction de la largeur du conteneur, et non en fonction de la longueur de la chaîne en ce qui concerne la largeur du conteneur
Henry
113

CSS3 prend en charge de nouvelles dimensions relatives au port d'affichage. Mais cela ne fonctionne pas dans Android <4.4

  1. 3,2 vw = 3,2% de la largeur de la fenêtre
  2. 3,2 vh = 3,2% de la hauteur de la fenêtre
  3. 3.2vmin = plus petit de 3.2vw ou 3.2vh
  4. 3.2vmax = plus grand de 3.2vw ou 3.2vh

    body
    {
        font-size: 3.2vw;
    }

voir css-tricks.com / .... et regardez aussi caniuse.com / ....

ou

Utilisez la requête multimédia. La manière la plus simple consiste à utiliser les dimensions en% ou en em. Modifiez simplement la taille de la police de base, tout changera.

@media (max-width: @screen-xs) {
    body{font-size: 10px;}
}

@media (max-width: @screen-sm) {
    body{font-size: 14px;}
}


h5{
    font-size: 1.4em;
}

Utilisez les dimensions en % ou em . Modifiez simplement la taille de la police de base, tout changera. Dans le précédent, vous pouviez simplement changer la police du corps et non h1 à chaque fois ou laisser la taille de la police de base à la valeur par défaut de l'appareil et rester tout en em

voir kyleschaeffer.com / .... pour plus d'informations sur em, px et%

aWebDeveloper
la source
12
La question concernait la mise à l'échelle par rapport au conteneur, et non à la fenêtre entière.
Arnaud Weil
6
... et les questions portaient sur la mise à l'échelle en fonction du nombre de caractères
Bravo
14

La seule façon serait probablement de définir différentes largeurs pour différentes tailles d'écran, mais cette approche est assez imprécise et vous devriez utiliser une solution js.

h1 {
    font-size: 20px;
}

@media all and (max-device-width: 720px){
    h1 {
        font-size: 18px;
    }
}

@media all and (max-device-width: 640px){
    h1 {
        font-size: 16px;
    }
}

@media all and (max-device-width: 320px){
    h1 {
        font-size: 12px;
    }
}
Sven Rojek
la source
12

Je sais que je ressuscite une question morte depuis longtemps, mais j'avais la même question et je voulais ajouter quelque chose. S'il vous plaît ne m'interdisez pas pour cela, je pensais que c'était assez important pour justifier cette réponse, je supprimerai si nécessaire. @Joseph Silber a tort, coder toutes les possibilités est en fait un moyen viable de le faire. La raison en est qu'il n'y a en fait pas de possibilités infinies. Eh bien, techniquement, il y en a, mais 99% de vos visiteurs utiliseront une résolution standard. Cela est doublement vrai pour les mobiles (la principale raison de la conception Web réactive) car la plupart des systèmes d'exploitation mobiles exécutent des applications en plein écran sans redimensionnement de fenêtre.

De plus, la hauteur est à peu près hors de propos en raison de la barre de défilement (à un point, je laisserais immédiatement une page Web de plus de 4 ou 5 pieds de long, mais cela reste vrai pour la plupart), vous n'avez donc qu'à vous soucier de la largeur. Et vraiment, les seules largeurs pour lesquelles vous devez coder sont les suivantes: 240, 320, 480 (pour les iThings plus anciens), 640, 800, 1024, 1280, 1440, 1600, 1920, 2048, 2560. Ne vous embêtez même pas pour 4k, cela gonflera trop vos images et la taille 2560 étirée à 100% de largeur semble très bien sur un moniteur 4k (j'ai testé cela). Aussi, ne vous embêtez pas avec 720 (720x480) comme l'a suggéré l'affiche précédente. C'est une résolution utilisée presque exclusivement par les appareils photo numériques, et même alors, c'est très rare.

Si quelqu'un utilise une résolution exotique, presque tous les moteurs de rendu réalisés au cours des 15 dernières années seront arrondis, donc si la largeur d'écran de quelqu'un est, disons. 1100, sa va charger la règle CSS 1024, votre site ne devrait pas casser. Cela rend inutile la prise en compte des résolutions exotiques en essayant de créer une règle réactive, et l'idée que vous devez coder pour chaque configuration possible pixel par pixel est ridicule, sauf si quelqu'un utilise un navigateur Web si obsolète que votre site ne se chargera probablement pas à de toute façon.

user281058
la source
1
Et maintenant 375 et 414 pour iPhone
6/6
Potentiellement encore plus facile si vous utilisez SASS ou Compass et accédez aux fonctions. Une fonction pourrait faire tout ce travail CSS pour vous; écrire une fois utiliser plusieurs.
cjbarth
la hauteur est à peu près hors de propos jusqu'à ce que vous tournez l'appareil de 90 ° sur le côté, c'est-à-dire
Carvo Loco
Cela n'a rien à voir avec la largeur d'un conteneur - dont il existe des possibilités facilement infinies.
mcheah
8

Pour référence, une solution non CSS:

Vous trouverez ci-dessous certains JS qui redimensionnent une police en fonction de la longueur du texte dans un conteneur.

Codepen avec du code légèrement modifié, mais même idée que ci-dessous:

function scaleFontSize(element) {
    var container = document.getElementById(element);

    // Reset font-size to 100% to begin
    container.style.fontSize = "100%";

    // Check if the text is wider than its container,
    // if so then reduce font-size
    if (container.scrollWidth > container.clientWidth) {
        container.style.fontSize = "70%";
    }
}

Pour moi, j'appelle cette fonction lorsqu'un utilisateur fait une sélection dans une liste déroulante, puis un div dans mon menu est rempli (c'est là que j'ai du texte dynamique).

    scaleFontSize("my_container_div");

De plus, j'utilise également des ellipses CSS ("...") pour tronquer un texte encore plus long , comme ceci:

#my_container_div {
    width: 200px; /* width required for text-overflow to work */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

Donc, finalement:

  • Texte court: par exemple "POMMES"

    Entièrement rendu, belles grosses lettres.

  • Texte long: par exemple "POMMES ET ORANGES"

    Obtient une réduction de 70%, via la fonction de mise à l'échelle JS ci-dessus.

  • Texte super long: par exemple "POMMES & ORANGES & BANAN ..."

    Obtient une réduction de 70% ET est tronqué avec des ellipses "...", via la fonction de mise à l'échelle JS ci-dessus avec la règle CSS.

Vous pouvez également explorer la possibilité d'utiliser l'espacement des lettres CSS pour affiner le texte tout en conservant la même taille de police.

MarsAndBack
la source
voté contre car l'utilisateur demandait une solution CSS pure
AnotherLongUsername
2

Comme beaucoup l'ont mentionné dans les commentaires du post de @ DMTinter, l'OP demandait quel était le nombre ("quantité") de caractères changés. Il posait également des questions sur CSS, mais comme @Alexander l'a indiqué, "ce n'est pas possible avec seulement CSS". Pour autant que je sache, cela semble être vrai en ce moment, il semble donc également logique que les gens voudraient connaître la meilleure chose suivante.

Je ne suis pas particulièrement fier de cela, mais cela fonctionne. Semble comme une quantité excessive de code pour y parvenir. C'est le noyau:

function fitText(el){
  var text = el.text();
  var fsize = parseInt(el.css('font-size'));
  var measured = measureText(text, fsize);

  if (measured.width > el.width()){
    console.log('reducing');
    while(true){
      fsize = parseInt(el.css('font-size'));
      var m = measureText(text, fsize );
      if(m.width > el.width()){
        el.css('font-size', --fsize + 'px');
      }
      else{
        break;
      }
    }
  }
  else if (measured.width < el.width()){
    console.log('increasing');
    while(true){
      fsize = parseInt(el.css('font-size'));
      var m = measureText(text, fsize);
      if(m.width < el.width()-4){ // not sure why -4 is needed (often)
        el.css('font-size', ++fsize + 'px');
      }
      else{
        break;
      }
    }
  }
}

Voici un Bin JS: http://jsbin.com/pidavon/edit?html,css,js,console,output
Veuillez suggérer des améliorations possibles (je ne suis pas vraiment intéressé par l'utilisation de toile pour mesurer le texte ... semble comme trop de frais généraux (?)).

Merci à @Pete pour la fonction MeasureText: https://stackoverflow.com/a/4032497/442665

jbobbins
la source
2

Cette solution pourrait également aider:

$(document).ready(function () {
    $(window).resize(function() {
        if ($(window).width() < 600) {
            $('body').css('font-size', '2.8vw' );
        } else if ($(window).width() >= 600 && $(window).width() < 750) {
            $('body').css('font-size', '2.4vw');
        } 
         // and so on... (according to our needs)
        } else if ($(window).width() >= 1200) {
            $('body').css('font-size', '1.2vw');
        }
    }); 
  });

Cela a bien fonctionné pour moi!

Gaurav Krishn
la source
Comment cela prend-il en compte le retour à la ligne?
Leif Neland
2
calc(42px + (60 - 42) * (100vw - 768px) / (1440 - 768));

utilisez cette équation.

Pour tout élément supérieur ou inférieur à 1440 et 768, vous pouvez lui attribuer une valeur statique ou appliquer la même approche.

L'inconvénient de la solution vw est que vous ne pouvez pas définir un rapport d'échelle, disons qu'un 5vw à la résolution d'écran 1440 peut finir par avoir une taille de police de 60 pixels, la taille de police de votre idée, mais lorsque vous réduisez la largeur de la fenêtre à 768, elle peut finir étant 12px, pas le minimum que vous voulez. Avec cette approche, vous pouvez définir vos limites supérieure et inférieure et la police se mettra à l'échelle entre les deux.

Shuwei
la source
-2

Créez une table de recherche qui calcule la taille de police en fonction de la longueur de la chaîne à l'intérieur de votre <div>.

const fontSizeLookupTable = () => {
  // lookup table looks like: [ '72px', ..., '32px', ..., '16px', ..., ]
  let a = [];
  // adjust this based on how many characters you expect in your <div>
  a.length = 32;
  // adjust the following ranges empirically
  a.fill( '72px' ,     );
  a.fill( '32px' , 4 , );
  a.fill( '16px' , 8 , );
  // add more ranges as necessary
  return a;
}

const computeFontSize = stringLength => {
  const table = fontSizeLookupTable();
  return stringLength < table.length ? table[stringLength] : '16px';
}

Ajustez et ajustez tous les paramètres par test empirique.

Laissez-moi y penser
la source
Le test empirique des paramètres est essentiellement une recherche binaire avec une complexité temporelle: O (log n).
Let Me Tink About It
Vous avez en fait oublié d'appeler cette fonction, la longueur du tableau doit également être accessible avec sa propriété.
lukk
@lukk: Merci pour la rétroaction. Fixé maintenant.
Let Me Tink About It