web-dev-qa-db-fra.com

Comment compter les lignes de texte à l'intérieur d'un élément DOM? Puis-je?

Je me demande s'il existe un moyen de compter les lignes à l'intérieur d'une div, par exemple. Disons que nous avons une div comme ça:

<div id="content">hello how are you?</div>

En fonction de nombreux facteurs, la div peut avoir une, deux, voire quatre lignes de texte. Y at-il un moyen pour le script de savoir?

En d'autres termes, les interruptions automatiques sont-elles représentées dans le DOM?

109
buti-oxa

Je suis convaincu que c'est impossible maintenant. C'était pourtant.

L’implémentation de getClientRects dans IE7 a fait exactement ce que je veux. Ouvrez cette page dans IE8, essayez d’actualiser la largeur de la fenêtre et de voir comment le nombre de lignes du premier élément change en conséquence. Voici les lignes principales du javascript de cette page:

var rects = elementList[i].getClientRects();
var p = document.createElement('p');
p.appendChild(document.createTextNode('\'' + elementList[i].tagName + '\' element has ' + rects.length + ' line(s).'));

Malheureusement pour moi, Firefox retourne toujours un rectangle client par élément, et IE8 fait de même. (La page de Martin Honnen fonctionne aujourd’hui car IE le restitue sous la forme IE vue compat; appuyez sur F12 dans IE8 pour jouer avec différents modes.)

C'est triste. Il semble une fois de plus que l’implémentation littérale mais inutile de Firefox de la spécification l'emporte sur l'utile de Microsoft. Ou dois-je rater une situation dans laquelle de nouveaux getClientRects peuvent aider un développeur?

6
buti-oxa

Si la taille de la div dépend du contenu (ce que je suppose être le cas de votre description), vous pouvez récupérer la hauteur de la div en utilisant:

var divHeight = document.getElementById('content').offsetHeight;

Et divisez par la hauteur de la ligne de police:

document.getElementById('content').style.lineHeight;

Ou pour obtenir la hauteur de ligne si elle n'a pas été explicitement définie:

var element = document.getElementById('content');
document.defaultView.getComputedStyle(element, null).getPropertyValue("lineHeight");

Vous devrez également prendre en compte le remplissage et l’espacement entre les lignes.

EDIT

Test entièrement autonome, définissant explicitement line-height:

function countLines() {
   var el = document.getElementById('content');
   var divHeight = el.offsetHeight
   var lineHeight = parseInt(el.style.lineHeight);
   var lines = divHeight / lineHeight;
   alert("Lines: " + lines);
}
<body onload="countLines();">
  <div id="content" style="width: 80px; line-height: 20px">
    hello how are you? hello how are you? hello how are you? hello how are you?
  </div>
</body>
74
Pool

Une solution consiste à inclure chaque mot dans une balise span à l'aide d'un script. Ensuite, si la dimension Y d'une balise span donnée est inférieure à celle de son prédécesseur immédiat, un saut de ligne s'est produit.

18
Bob Brunius

Découvrez la fonction getClientRects () qui peut être utilisée pour compter le nombre de lignes d'un élément. Voici un exemple d'utilisation.

var message_lines = $("#message_container")[0].getClientRects();

Il retourne un objet DOM javascript. La quantité de lignes peut être connue en faisant ceci:

var amount_of_lines = message_lines.length;

Il peut retourner la hauteur de chaque ligne, et plus encore. Découvrez tout ce qu’il peut faire en ajoutant ceci à votre script, puis en consultant le journal de votre console.

console.log("");
console.log("message_lines");
console.log(".............................................");
console.dir(message_lines);
console.log("");

Quelques points à noter sont que cela ne fonctionne que si l'élément contenant est en ligne, mais vous pouvez entourer l'élément en ligne contenant d'un élément de bloc pour contrôler la largeur de la manière suivante:

<div style="width:300px;" id="block_message_container">
<div style="display:inline;" id="message_container">
..Text of the post..
</div>
</div>

Bien que je ne recommande pas de coder en dur le style comme ça. C'est juste à titre d'exemple.

14
Digital-Christie

Je n'ai pas été satisfait des réponses ici et sur d'autres questions. La réponse la mieux notée ne prend pas en compte padding ou border, et ignore donc évidemment box-sizing ainsi que. Ma réponse combine certaines techniques ici et sur d'autres threads pour obtenir une solution qui fonctionne à ma satisfaction.

Ce n'est pas parfait: lorsqu'aucune valeur numérique n'a pu être récupérée pour le line-height (normal ou inherit), il utilise simplement le font-size multiplié par 1.2. Peut-être que quelqu'un d'autre peut suggérer un moyen fiable de détecter la valeur de pixel dans ces cas.

Autre que cela, il a été capable de gérer correctement la plupart des styles et des cas que je lui ai jeté.

jsFiddle pour jouer et tester. Également en ligne ci-dessous.

function countLines(target) {
  var style = window.getComputedStyle(target, null);
  var height = parseInt(style.getPropertyValue("height"));
  var font_size = parseInt(style.getPropertyValue("font-size"));
  var line_height = parseInt(style.getPropertyValue("line-height"));
  var box_sizing = style.getPropertyValue("box-sizing");
  
  if(isNaN(line_height)) line_height = font_size * 1.2;
 
  if(box_sizing=='border-box')
  {
    var padding_top = parseInt(style.getPropertyValue("padding-top"));
    var padding_bottom = parseInt(style.getPropertyValue("padding-bottom"));
    var border_top = parseInt(style.getPropertyValue("border-top-width"));
    var border_bottom = parseInt(style.getPropertyValue("border-bottom-width"));
    height = height - padding_top - padding_bottom - border_top - border_bottom
  }
  var lines = Math.ceil(height / line_height);
  alert("Lines: " + lines);
  return lines;
}
countLines(document.getElementById("foo"));
div
{
  padding:100px 0 10% 0;
  background: pink;
  box-sizing: border-box;
  border:30px solid red;
}
<div id="foo">
x<br>
x<br>
x<br>
x<br>
</div>
11
Jeff

Clonez l'objet conteneur, écrivez 2 lettres et calculez la hauteur. Cela retourne la hauteur réelle avec tous les styles appliqués, hauteur de ligne, etc. Maintenant, calculez l'objet height/la taille d'une lettre. Dans JQuery, la hauteur excelude le rembourrage, la marge et la bordure, il est bon de calculer la hauteur réelle de chaque ligne:

other = obj.clone();
other.html('a<br>b').hide().appendTo('body');
size = other.height() / 2;
other.remove();
lines = obj.height() /  size;

Si vous utilisez une police rare avec une hauteur différente de chaque lettre, cela ne fonctionne pas. Mais fonctionne avec toutes les polices normales, comme Arial, mono, bandes dessinées, Verdana, etc. Testez avec votre police.

Exemple:

<div id="content" style="width: 100px">hello how are you? hello how are you? hello how are you?</div>
<script type="text/javascript">
$(document).ready(function(){

  calculate = function(obj){
    other = obj.clone();
    other.html('a<br>b').hide().appendTo('body');
    size = other.height() / 2;
    other.remove();
    return obj.height() /  size;
  }

  n = calculate($('#content'));
  alert(n + ' lines');
});
</script>

Résultat: 6 Lines

Fonctionne dans tous les navigateurs sans fonctions rares hors normes.

Vérifier: https://jsfiddle.net/gzceamtr/

8
e-info128

Pour ceux qui utilisent jQuery http://jsfiddle.net/EppA2/3/

function getRows(selector) {
    var height = $(selector).height();
    var line_height = $(selector).css('line-height');
    line_height = parseFloat(line_height)
    var rows = height / line_height;
    return Math.round(rows);
}
6
penkovsky

basé sur la réponse de GuyPaddock d'en haut, cela semble fonctionner pour moi

function getLinesCount(element) {
  var prevLH = element.style.lineHeight;
  var factor = 1000;
  element.style.lineHeight = factor + 'px';

  var height = element.getBoundingClientRect().height;
  element.style.lineHeight = prevLH;

  return Math.floor(height / factor);
}

le truc ici est d’augmenter la hauteur de ligne au point d’avaler toutes les différences entre les navigateurs et les systèmes d’exploitation dans le rendu des polices.

Vérifié avec différents styles et différentes tailles de police/familles, chose à ne pas prendre en compte (car dans mon cas, cela n’était pas important), c’est le rembourrage - qui peut facilement être ajouté à la solution.

3
Maor Yosef

Non, pas de manière fiable. Il y a tout simplement trop de variables inconnues

  1. Quel OS (différents DPI, variations de police, etc ...)?
  2. Ont-ils une taille de police plus grande parce qu'ils sont pratiquement aveugles?
  3. Heck, dans les navigateurs Webkit, vous pouvez réellement redimensionner les zones de texte à votre guise.

La liste continue. J'espère qu'un jour, il y aura une telle méthode pour accomplir cela de manière fiable avec JavaScript, mais jusqu'à ce que ce jour vienne, vous n'avez pas de chance.

Je déteste ce genre de réponses et j'espère que quelqu'un pourra me prouver le contraire.

2
KyleFarris

Vous devriez pouvoir séparer ('\ n'). Length et obtenir les sauts de ligne.

mise à jour: cela fonctionne sur FF/Chrome mais pas sur IE.

<html>
<head>
<script src="jquery-1.3.2.min.js"></script>
<script>
    $(document).ready(function() {
        var arr = $("div").text().split('\n');
        for (var i = 0; i < arr.length; i++)
            $("div").after(i + '=' + arr[i] + '<br/>');
    });
</script>
</head>
<body>
<div>One
Two
Three</div>
</body>
</html>
2
Chad Grant

Essayez cette solution:

function calculateLineCount(element) {
  var lineHeightBefore = element.css("line-height"),
      boxSizing        = element.css("box-sizing"),
      height,
      lineCount;

  // Force the line height to a known value
  element.css("line-height", "1px");

  // Take a snapshot of the height
  height = parseFloat(element.css("height"));

  // Reset the line height
  element.css("line-height", lineHeightBefore);

  if (boxSizing == "border-box") {
    // With "border-box", padding cuts into the content, so we have to subtract
    // it out
    var paddingTop    = parseFloat(element.css("padding-top")),
        paddingBottom = parseFloat(element.css("padding-bottom"));

    height -= (paddingTop + paddingBottom);
  }

  // The height is the line count
  lineCount = height;

  return lineCount;
}

Vous pouvez le voir en action ici: https://jsfiddle.net/u0r6avnt/

Essayez de redimensionner les panneaux de la page (pour élargir ou raccourcir le côté droit de la page), puis exécutez-le à nouveau pour vérifier qu'il peut indiquer de manière fiable le nombre de lignes.

Ce problème est plus difficile qu'il n'y paraît, mais la plupart des difficultés proviennent de deux sources:

  1. Le rendu du texte est trop bas dans les navigateurs pour pouvoir être interrogé directement à partir de JavaScript. Même le pseudo-sélecteur CSS :: first-line ne se comporte pas exactement comme les autres sélecteurs (vous ne pouvez pas l'inverser, par exemple, pour appliquer un style à tous mais à la première ligne).

  2. Le contexte joue un rôle important dans la manière dont vous calculez le nombre de lignes. Par exemple, si line-height n'est pas explicitement défini dans la hiérarchie de l'élément cible, vous pouvez obtenir une valeur "normale" comme hauteur de ligne. De plus, l'élément peut utiliser box-sizing: border-box et donc être sujet au rembourrage.

Mon approche minimise n ° 2 en prenant le contrôle de la hauteur de ligne directement et en prenant en compte la méthode de dimensionnement de la boîte, ce qui conduit à un résultat plus déterministe.

1
GuyPaddock

getClientRects renvoie les résultats du client tels que this et si vous voulez obtenir les lignes, utilisez la fonction suivante comme this

function getRowRects(element) {
    var rects = [],
        clientRects = element.getClientRects(),
        len = clientRects.length,
        clientRect, top, rectsLen, rect, i;

    for(i=0; i<len; i++) {
        has = false;
        rectsLen = rects.length;
        clientRect = clientRects[i];
        top = clientRect.top;
        while(rectsLen--) {
            rect = rects[rectsLen];
            if (rect.top == top) {
                has = true;
                break;
            }
        }
        if(has) {
            rect.right = rect.right > clientRect.right ? rect.right : clientRect.right;
            rect.width = rect.right - rect.left;
        }
        else {
            rects.Push({
                top: clientRect.top,
                right: clientRect.right,
                bottom: clientRect.bottom,
                left: clientRect.left,
                width: clientRect.width,
                height: clientRect.height
            });
        }
    }
    return rects;
}
1
Defims

Dans certains cas, comme un lien recouvrant plusieurs lignes dans un texte non justifié, vous pouvez obtenir le nombre de lignes et toutes les coordonnées de chaque ligne lorsque vous utilisez ceci:

var rectCollection = object.getClientRects();

https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects

Cela fonctionne parce que chaque ligne serait différente même légèrement. Tant qu'ils sont présents, ils sont dessinés sous la forme d'un "rectangle" différent par le rendu.

0
Firsh - LetsWP.io

Vous pouvez comparer la hauteur et la hauteur d’élément avec line-height: 0

function lineCount(Elm) {
  const style = Elm.getAttribute('style')
  Elm.style.marginTop = 0
  Elm.style.marginBottom = 0
  Elm.style.paddingTop = 0
  Elm.style.paddingBottom = 0
  const heightAllLine = Elm.offsetHeight
  Elm.style.lineHeight = 0
  const height1line = Elm.offsetHeight
  const lineCount = Math.round(heightAllLine / height1line)
  Elm.setAttribute('style', style)
  if (isNaN(lineCount)) return 0
  return lineCount
}
0
Yukulélé

Suite à la suggestion de @BobBrunius 2010, j’ai créé cela avec jQuery. Nul doute que cela pourrait être amélioré, mais cela pourrait aider certains.

$(document).ready(function() {

  alert("Number of lines: " + getTextLinesNum($("#textbox")));

});

function getTextLinesNum($element) {

  var originalHtml = $element.html();
  var words = originalHtml.split(" ");
  var linePositions = [];
        
  // Wrap words in spans
  for (var i in words) {
    words[i] = "<span>" + words[i] + "</span>";
  }
        
  // Temporarily replace element content with spans. Layout should be identical.
  $element.html(words.join(" "));
        
  // Iterate through words and collect positions of text lines
  $element.children("span").each(function () {
    var lp = $(this).position().top;
    if (linePositions.indexOf(lp) == -1) linePositions.Push(lp);
  });
        
  // Revert to original html content
  $element.html(originalHtml);
        
  // Return number of text lines
  return linePositions.length;

}
#textbox {
  width: 200px;
  text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="textbox">Lorem ipsum dolor sit amet, consectetuer adipiscing elit,
  <br>sed diam nonummy</div>
0
josef

J'ai trouvé un moyen de calculer le numéro de ligne lorsque je développe un éditeur HTML. La méthode principale est la suivante:

  1. Dans IE vous pouvez appeler getBoundingClientRects, il renvoie chaque ligne sous forme de rectangle

  2. Dans webkit ou dans un nouveau moteur html standard, il renvoie les rectangles clients de chaque élément ou noeud, dans ce cas, vous pouvez comparer chaque rectangle. il y a un rectangle au-dessus de celui-ci et en bas au-dessus, la condition est vraie.)

voyons le résultat du test:

enter image description here

Le rectangle vert est le plus grand rectangle de chaque ligne

Le rectangle rouge est la limite de sélection

Le rectangle bleu est la limite du début à la sélection après l’agrandissement. Nous voyons qu’il peut être plus grand que le rectangle rouge; nous devons donc vérifier le fond de chaque rectangle pour le limiter au plus petit que celui du rectangle rouge.

        var lineCount = "?";
        var rects;
        if (window.getSelection) {
            //Get all client rectangles from body start to selection, count those rectangles that has the max bottom and min top
            var bounding = {};
            var range = window.getSelection().getRangeAt(0);//As this is the demo code, I dont check the range count
            bounding = range.getBoundingClientRect();//!!!GET BOUNDING BEFORE SET START!!!

            //Get bounding and fix it , when the cursor is in the last character of lineCount, it may expand to the next lineCount.
            var boundingTop = bounding.top;
            var boundingBottom = bounding.bottom;
            var node = range.startContainer;
            if (node.nodeType !== 1) {
                node = node.parentNode;
            }
            var style = window.getComputedStyle(node);
            var lineHeight = parseInt(style.lineHeight);
            if (!isNaN(lineHeight)) {
                boundingBottom = boundingTop + lineHeight;
            }
            else {
                var fontSize = parseInt(style.fontSize);
                if (!isNaN(fontSize)) {
                    boundingBottom = boundingTop + fontSize;
                }
            }
            range = range.cloneRange();

            //Now we have enougn datas to compare

            range.setStart(body, 0);
            rects = range.getClientRects();
            lineCount = 0;
            var flags = {};//Mark a flags to avoid of check some repeat lines again
            for (var i = 0; i < rects.length; i++) {
                var rect = rects[i];
                if (rect.width === 0 && rect.height === 0) {//Ignore zero rectangles
                    continue;
                }
                if (rect.bottom > boundingBottom) {//Check if current rectangle out of the real bounding of selection
                    break;
                }
                var top = rect.top;
                var bottom = rect.bottom;
                if (flags[top]) {
                    continue;
                }
                flags[top] = 1;

                //Check if there is no rectangle contains this rectangle in vertical direction.
                var succ = true;
                for (var j = 0; j < rects.length; j++) {
                    var rect2 = rects[j];
                    if (j !== i && rect2.top < top && rect2.bottom > bottom) {
                        succ = false;
                        break;
                    }
                }
                //If succ, add lineCount 1
                if (succ) {
                    lineCount++;
                }
            }
        }
        else if (editor.document.selection) {//IN IE8 getClientRects returns each single lineCount as a rectangle
            var range = body.createTextRange();
            range.setEndPoint("EndToEnd", range);
            rects = range.getClientRects();
            lineCount = rects.length;
        }
        //Now we get lineCount here
0
dexiang