detecting line-breaks with jQuery?
Asked Answered
B

7

25

is it possible to have jQuery/javascript detect where a string is broken (in order to fit into CSS width constraints) so as to insert DOM elements before the beginning of a new line?

Bemba answered 12/1, 2011 at 17:12 Comment(4)
Are you looking for where a word is wrapped, or are you looking for <br>?Andre
what kind of string is the input?Eastbourne
What is the content you're inserting before the beginning of each line?Norenenorfleet
@Andre looking for the wrap. @Eastbourne just a sentence, it's a heading. @Norenenorfleet not sure what you mean? before the dynamic content there is a <h3> and a <span>Bemba
I
42

I came up with an approach, but it might be overkill for your purposes, so take this into account.

You need to create a clone of the element, empty the original, then move each word back into the original element. If the height changes at any point, there's a line-break before that word. This would be fairly simple to do using $(el).text(), but it gets more complicated if there can be other tags inside, not just text. I tried explaining how to break it down by node in this answer box, but found it easier just to create a jQuery plugin in a jsFiddle. Link here: http://jsfiddle.net/nathan/qkmse/ (Gist).

It won't handle floated elements all that well, and there are a few other situations where it'll fall over. Let me know if you'd like more options, or if it doesn't quite work for your purposes, or if you're not sure how to apply it and I'll try to help.

Infrequent answered 15/9, 2011 at 13:40 Comment(2)
Works out of the box - Brute force for the win!Lehr
This comment addresses something similar, it wraps each line of text in a span (with a unique class!). It uses this plugin.Unmentionable
B
6

Here is one approach. Note: I do not see a ideal solution without using monospace fonts. The equal with characters make this task much easier.

  1. Equal width characters
  2. Calculate the size of one character
  3. Calculate the size of the container
  4. Find characters per row
  5. Find where the row will break (ie whitespace, dashes, etc)
  6. Get all breaking indexes.

Have a look at the jsfiddle for associated html. I have not completed this function. More checks need to be put in when calculating the breaking index. Right now it is using lastIndexOf(' '), but this ignores that the next index could be a space, or the current. Also I am not accounting for other line-breaking characters. However this should be a great starting point.

var text = $('#text').text(),                   // "lorem ipsum ... "
    len = text.length,                          // total chars
    width = $('#text').width(),                 // container width
    span = $('<span />').append('a').appendTo('#sandbox'),
    charWidth = span.width(),                  // add single character to span and test width
    charsPerRow = Math.floor(width/charWidth); // total characters that can fit in one row

var breakingIndexes = [], // will contain indexes of all soft-breaks
    gRowStart = 0,        // global row start index
    gRowEnd = charsPerRow;// global row end index

while(gRowEnd < len){
    var rowEnd = text.substring(gRowStart, gRowEnd).lastIndexOf(' '); // add more checks for break conditions here
    breakingIndexes.push(gRowStart + rowEnd); // add breaking index to array
    gRowStart = gRowStart + rowEnd + 1; // next start is the next char
    gRowEnd = gRowStart + charsPerRow;  // global end inxex is start + charsperrow
}

var text2 = $('#text2').text();           // "lorem ipsum ... " now not width bound
var start = 0, newText = '';
for(var i=0; i < breakingIndexes.length; i++){
    newText += text2.substring(start, breakingIndexes[i]) + '<br />'; // add hard breaks
    start = breakingIndexes[i]; // update start
}

$('#text2').html(newText); // output with breaks

http://jsfiddle.net/Y5Ftn/1/

Boat answered 12/1, 2011 at 18:28 Comment(4)
Problem is indeed that a lot of characters have a different space. One thing you could do is take 1ex as charWidth. Or maybe (which is harder) create an estimate of the charWidth. But this is language dependent, by means of which character is more likely to be in your text. Or, if word-wrap is not what you are looking for, it could be nice to take the max-char, like W, which is a huge character. Or maybe see it as a Gaussian function with a variance?Andre
@marnix - I cannot see any of these being very accurate calculations, because non-monospace fonts have different character widths. You could of course calculate the width of all common characters and actually calculate each row based on the individual character widths. This however seems horribly inefficient.Boat
I just tested this in IE 7 (Corporate standard here :( ) and it didn't format correctly, but it looks great in Chrome 8! Just thought you might like to know, maybe something can be done...Vela
As I said, it should not be calculated at every line. Although it is doable in polynomial time, it is something you do not want. Creating an estimate for the language you are working in is. Most natural processes are Gaussian, so that would be a good thing to start with, or you could make it a discrete E[X], because the number of characters is discrete. 'W' seems like a good character as max-char, because it is in almost every font the largest character. So yes, creating an estimate is nicer that just taking an 'a' in a SPAN.Andre
N
5

this is my script, that takes text, and then makes each line a span

CSS:

    margin: 0;
        padding: 0;
    }

    .title{
        width: 300px;
        background-color: rgba(233,233,233,0.5);
        line-height: 20px;
    }

    span{
        color: white;
        background-color: red;
        display: inline-block;
        font-size: 30px;
    }

    a{
        text-decoration: none;
        color: black;
    }

html

<div class="title">
        <a href="">SOME TEXT LONG TEXT ANDTHISISLONG AND THIS OTHER TEXT</a>
    </div>

JS

$(function(){

        $(".title").find("a").each(function(){

            var $this = $(this);
            var originalText = $this.text();
            $this.empty();

            var sections = [];

            $.each( originalText.split(" "), function(){
                var $span = $("<span>" + this + "</span>");
                $this.append($span);

                var index = $span.position().top;

                if( sections[index] === undefined ){
                    sections[index] = "";
                }

                sections[index] += $span.text() + " ";
            });

            $this.empty();

            for(var i = 0; i< sections.length; i++){
                if( sections[i] !== undefined ){
                    var spanText = $.trim(sections[i]);                     
                    $this.append("<span>" + spanText + "</span>");
                }
            }

        });

    });

You have to got jQuery included.

Nancienancy answered 2/4, 2013 at 23:35 Comment(1)
This is pretty nice, thanks! Though there are a few edge cases, for example the browser may break at places other than space (tab, newline, U+2028, maybe hyphens).Bent
C
1

Alternatively you could compare width of the text block with its parent's width. If the block is at least 98% of the width of its parent, pretty sure it breaks

Charo answered 23/5, 2013 at 10:5 Comment(0)
R
0

I don't know of any built in jQuery of javascript function to do this. You could, however, do it yourself, but it would be potentially slow if you have a lot of text.

In theory, you could make sure the height is set to auto, remove the text, and then word by word reinsert it. On a change in the height, you remove the last word, and insert your dom element. Again, this will be slow if there is a lot of text, and the better way to do this would be to not change the original element, but do it in another element which could be hidden, and then replace the original element on completion. That way, your user wouldn't see the content disappear and then come back word by word.

Another way would be to use a similar principle, and start with an empty element of the same size with height auto, and insert a new character and a space until you get a new line. From there, you can use this as an approximation with the above techinique, or you can blindly add up the length of each string until you find a new line, taking into account the width of your dom element. This technique works better with monospaced fonts though, which is where using it only as an approximation comes in.

That said, there is a way to measure text using canvas, but that may be extreme. In theory it would be, create a canvas element, get the context, set all the font properties, and then use the context.measureText() method. An example of it use can be found here.

Resonant answered 12/1, 2011 at 17:40 Comment(0)
J
0

Not sure what exactly your use case is but http://code.google.com/p/hyphenator/ may be a solution to your problem or if you dig into the hyphenator.js source code, you may be able to find the code you are looking for.

Joannejoannes answered 13/1, 2011 at 0:26 Comment(0)
A
-1

I think it would be easier to detect using a regex -- much less code while retaining efficiency.

Here's something that worked for me:

if ((/(\r\n|\n|\r)/.test($(this).val()))) {
    alert('there are line breaks here')
}

I'm not sure if this will work with your broken strings, but it works for detecting line-breks with jQuery.

Arbitrary answered 11/2, 2013 at 16:56 Comment(3)
does this code pick breaks caused by normal text wrapping in the DOM?Bemba
I've only done prelim testing on that, but I believe so, yes. I know it definitely works in textareas, which is where I've used it in the past.Arbitrary
it works in textareas because editing the element changes the DOM. this is not the case for other elements.Pompom

© 2022 - 2024 — McMap. All rights reserved.