web-dev-qa-db-fra.com

Trouver des retours à la ligne

Supposons que je dispose d’un bloc de texte aléatoire sur une seule ligne. Ainsi

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Mais pour une raison quelconque (réglages de largeur sur l'élément contenant, utilisation de zoom sur le texte, etc.), l'écran du visualiseur s'affiche sous forme de deux lignes ou plus.

Lorem ipsum dolor sit amet,

consectetur adipiscing elit.

ou 

Lorem ipsum dolor sit

amet, consectetur

adipiscing elit.

Existe-t-il un moyen de savoir via javascript où se trouvent ces retours à la ligne?

$('p').text() et $('p').html() retournent Lorem ipsum dolor sit amet, consectetur adipiscing elit. quelle que soit la manière dont le texte est affiché.

39
Inaimathi

Voici ce que j'ai fini par utiliser (n'hésitez pas à critiquer et à copier à vos propres fins néfastes).

Tout d'abord, lorsque la modification arrive de l'utilisateur, elle est interrompue avec $(editableElement).lineText(userInput).

jQuery.fn.lineText = function (userInput) {
   var a = userInput.replace(/\n/g, " \n<br/> ").split(" ");
   $.each(a, function(i, val) { 
      if(!val.match(/\n/) && val!="") a[i] = '<span class="Word-measure">' + val + '</span>';
   });
   $(this).html(a.join(" "));
};

Le remplacement de nouvelle ligne se produit parce que la zone de texte d'édition est remplie avec $(editableElement).text(), qui ignore les balises <br/>, mais celles-ci modifieront néanmoins la hauteur de la ligne suivante dans l'affichage à des fins de composition. Cela ne faisait pas partie de l'objectif initial, mais des résultats relativement faciles à atteindre.

Quand j’ai besoin d’extraire du texte formaté, j’appelle $(editableElement).getLines(), où

jQuery.fn.getLines = function (){
   var count = $(this).children(".Word-measure").length;
   var lineAcc = [$(this).children(".Word-measure:eq(0)").text()];
   var textAcc = [];
   for(var i=1; i<count; i++){
      var prevY = $(this).children(".Word-measure:eq("+(i-1)+")").offset().top;
      if($(this).children(".Word-measure:eq("+i+")").offset().top==prevY){
         lineAcc.Push($(this).children(".Word-measure:eq("+i+")").text());
   } else {
     textAcc.Push({text: lineAcc.join(" "), top: prevY});
     lineAcc = [$(this).children(".Word-measure:eq("+i+")").text()];
   }
   }
   textAcc.Push({text: lineAcc.join(" "), top: $(this).children(".Word-measure:last").offset().top});
   return textAcc;
};

Le résultat final est une liste de hachages, chacun contenant le contenu et le décalage vertical d'une seule ligne de texte.

[{"text":"Some dummy set to","top":363},
 {"text":"demonstrate...","top":382},
 {"text":"The output of this","top":420},
 {"text":"wrap-detector.","top":439}]

Si je veux juste du texte non formaté, $(editableElement).text() retourne toujours

"Some dummy set to demonstrate... The output of this wrap-detector."
11
Inaimathi

Eh bien, si vous voulez quelque chose qui est ridiculement simple et probablement trop inutile pour vous (cela nécessitera une modification majeure si vous avez du HTML dans le paragraphe), alors regardez ceci: 

var para = $('p');

para.each(function(){
    var current = $(this);
    var text = current.text();
    var words = text.split(' ');

    current.text(words[0]);
    var height = current.height();

    for(var i = 1; i < words.length; i++){
        current.text(current.text() + ' ' + words[i]);

        if(current.height() > height){
            height = current.height();
            // (i-1) is the index of the Word before the text wraps
            console.log(words[i-1]);
        }
    }
});

C'est tellement ridiculement simple que ça pourrait marcher. Cela permet de séparer le texte par des espaces, puis d’ajouter les mots, mot après mot, en observant toute augmentation de la hauteur de l’élément, ce qui indiquerait un retour à la ligne. 

Jetez un coup d'oeil ici: http://www.jsfiddle.net/xRPYN/2/

26
Yi Jiang

Pour un cas d'utilisation tel que la génération pdf.

Vous pouvez limiter le nombre de caractères par ligne. Si une scission se produit au centre du mot, ajustez-la de manière appropriée.

Pour obtenir des caractères plus précis par ligne, vous pouvez utiliser des polices à espacement fixe, puis déterminer la largeur par caractère de chaque police autorisée. Divisez ensuite la largeur des caractères par la taille de la largeur de ligne de texte autorisée. Vous obtiendrez les caractères autorisés par ligne pour cette police.

Vous pouvez utiliser des polices non monospaces, mais vous devrez ensuite mesurer la largeur de chaque lettre - pouah. Une façon d’automatiser la détermination de la largeur est d’avoir une étendue qui n’a pas de marge ni de remplissage, d’ajouter chaque caractère pour chaque police (et taille), puis de mesurer la largeur de l’étendue et de l’utiliser.

J'ai terminé le code:

/**
 * jQuery getFontSizeCharObject
 * @version 1.0.0
 * @date September 18, 2010
 * @since 1.0.0, September 18, 2010
 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
 * @copyright (c) 2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
 * @license Attribution-ShareAlike 2.5 Generic {@link http://creativecommons.org/licenses/by-sa/2.5/
 */
$.getFontSizeCharObject = function(fonts,sizes,chars){
    var fonts = fonts||['Arial','Times'],
        sizes = sizes||['12px','14px'],
        chars = chars||['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','y','x','z',
                        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','Y','X','Z',
                        '0','1','2','3','4','5','6','7','8','9','-','=',
                        '!','@','#','$','%','^','&','*','(',')','_','+',
                        '[',']','{','}','\\','|',
                        ';',"'",':','"',
                        ',','.','/','<','>','?',' '],
        font_size_char = {},
        $body = $('body'),
        $span = $('<span style="padding:0;margin:0;letter-spacing:0:Word-spacing:0"/>').appendTo($body);

    $.each(fonts, function(i,font){
        $span.css('font-family', font);
        font_size_char[font] = font_size_char[font]||{};
        $.each(sizes, function(i,size){
            $span.css('font-size',size);
            font_size_char[font][size] = font_size_char[font][size]||{};
            $.each(chars,function(i,char){
                if ( char === ' ' ) {
                    $span.html('&nbsp;');
                }
                else {
                    $span.text(char);
                }
                var width = $span.width()||0;
                font_size_char[font][size][char] = width;
            });
        });
    });

    $span.remove();

    return font_size_char;
};

/**
 * jQuery adjustedText Element Function
 * @version 1.0.0
 * @date September 18, 2010
 * @since 1.0.0, September 18, 2010
 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
 * @copyright (c) 2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
 * @license Attribution-ShareAlike 2.5 Generic {@link http://creativecommons.org/licenses/by-sa/2.5/
 */
$.fn.adjustedText = function(text,maxLineWidth){
    var $this = $(this),
        font_size_char = $.getFontSizeCharObject(),
        char_width = font_size_char['Times']['14px'],
        maxLineWidth = parseInt(maxLineWidth,10),
        newlinesAt = [],
        lineWidth = 0,
        lastSpace = null;

    text = text.replace(/\s+/g, ' ');

    $.each(text,function(i,char){
        var width = char_width[char]||0;
        lineWidth += width;
        if ( /^[\-\s]$/.test(char) ) {
            lastSpace = i;
        }
        //console.log(i,char,lineWidth,width);
        if ( lineWidth >= maxLineWidth ) {
            newlinesAt.Push(lastSpace||i);
            lineWidth = width;
            lastSpace = null;
        }
    });

    $.each(newlinesAt,function(i,at){
        text = text.substring(0,at+i)+"\n"+text.substring(at+i);
    });

    text = text.replace(/\ ?\n\ ?/g, "\n");

    console.log(text,newlinesAt);

    $this.text(text);

    return $this;
};

$(function(){
    var $body = $('body'),
        $textarea = $('#mytext'),
        $btn = $('#mybtn'),
        $div = $('#mydiv');

    if ( $textarea.length === 0 && $div.length === 0 ) {
        $body.empty();

        $textarea = $('<textarea id="mytext"/>').val('(When spoken repeatedly, often three times in succession: blah blah blah!) Imitative of idle, meaningless talk; used sometimes in a slightly derogatory manner to mock or downplay another\'s words, or to show disinterest in a diatribe, rant, instructions, unsolicited advice, parenting, etc. Also used when recalling and retelling another\'s words, as a substitute for the portions of the speech deemed irrelevant.').appendTo($body);
        $div = $('<div id="mydiv"/>').appendTo($body);
        $btn = $('<button id="mybtn">Update Div</button>').click(function(){
            $div.adjustedText($textarea.val(),'300px');
        }).appendTo($body);

        $div.add($textarea).css({
            'width':'300px',
            'font-family': 'Times',
            'font-size': '14px'
        });
        $div.css({
            'width':'auto',
            'white-space':'pre',
            'text-align':'left'
        });
    }

});
11
balupton

Les solutions ci-dessus ne fonctionnent pas une fois que vous avez une structure plus complexe, telle qu'un lien dans un paragraphe (vous pouvez par exemple avoir <b><i><a href></a> dans un <p>).

J'ai donc créé une bibliothèque javascript pour détecter où les lignes s’allient et qui fonctionnent dans ces cas: http://github.com/xdamman/js-line-wrap-detector

J'espère que ça aide.

7
xdamman

J'ai une situation où j'ai besoin d'envelopper chaque ligne dans un intervalle. Je le fais pour pouvoir ajouter un effet de surbrillance matelassé à un bloc de texte. L'ajout de l'arrière-plan à une balise span englobant le texte ne remplit que le début et la fin du bloc de texte; chaque ligne doit être emballée individuellement.

C’est ce que j’ai trouvé à partir des suggestions ci-dessus:

$.fn.highlghtWrap = function () {
    this.each( function () {
      var current = $( this );
      var text = current.text();
      var words = text.split( ' ' );
      var line = '';
      var lines = [];

      current.text( words[ 0 ] );
      var height = current.height();
      line = words[ 0 ];
      for ( var i = 1; i < words.length; i++ ) {
        current.text( current.text() + ' ' + words[ i ] );

        if ( current.height() > height ) {
          lines.Push( line );
          line = words[ i ];
          height = current.height();
        } else {
          line = line + ' ' + words[ i ];
        }
      }
      lines.Push( line );
      current.html( '' );
      $.each( lines, function ( v, a ) {
        current.html( current.html() + '<span>' + a +
          ' </span>' );
      } );
    } );
  }

  $( '.home-top_wrapper h2' ).highlghtWrap();
  $( '.home-top_wrapper p' ).highlghtWrap();
0
user3761817

Une méthode conceptuellement simple, qui fonctionne également lorsqu'il existe un balisage interne et des polices et styles arbitraires, consiste à effectuer une première passe qui place simplement chaque mot dans son propre élément (peut-être 'SPAN' ou un nom personnalisé comme 'w').

Ensuite, vous pouvez effectuer une itération à l'aide de getBoundingClientRect () pour rechercher où la propriété 'top' change:

function findBreaks() {
    var words = document.getElementsByTagName('w');
    var lastTop = 0;
    for (var i=0; i<words.length; i++) {
        var newTop = words[i].getBoundingClientRect().top;
        if (newTop == lastTop) continue;
        console.log("new line " + words[i].textContent + " at: " + newTop);
        lastTop = newTop;
    }
}

Cela semble lent, mais à moins que les documents ne soient vraiment volumineux, vous ne le remarquerez pas.

0
TextGeek