9

Is it possible to have jquery/javascript insert sequential line number at the start of all lines in a paragraph and, better still, to follow the sequence through to subsequent paragraphs?

I want to be able to refer students quickly to particular lines of an article (in a classroom setting). I have lots of articles to which I would like to apply this functionality, each of which has varying numbers of paragraphs.

I was hoping this might be possible, even in a responsive page, where the width of the paragraphs changes, depending on the display device, and the consequent number of lines in each paragraph becomes greater or fewer.

Thanks in advance to anyone who can help.

user2195707
  • 111
  • 3
  • 2
    For reference purposes, wouldn't you want the line numbers not to respond to width changes of the paragraph? – FishBasketGordo Oct 15 '13 at 21:13
  • Yes, it is possible - do you have anything that you've tried yet? – Codeman Oct 15 '13 at 21:15
  • Yes, it is certainly possible. However I don't see how line numbers make sense on a responsive page, where the line numbers differ from device to device so that you cannot refer to them. – Bergi Oct 15 '13 at 21:16
  • 4
    Obviously the changing width of the content is going to be a huge problem, making referencing this content by line number pretty useless because the line numbers are going to be different for each different environment. It would be better in my opinion to reference them by paragraph, or to possibly have specific parts that you would make targetable by wrapping in `sometext` so that you can say, "Go to reference 1" and all they'd have to do is click the reference one link targeting `#ref1` – Kevin B Oct 15 '13 at 21:16
  • You've got a point, FishBasketGordo. Maybe I'll dispense with the responsive page in that case and keep the width fixed. That would suffice if I could get the numbering working. – user2195707 Oct 15 '13 at 21:16
  • It might be easier (and enough for your students) to number only the paragraphs. They even would be constant regardless of the display shown on. – Bergi Oct 15 '13 at 21:18
  • Pheonixblade9, no... but I'm trying to figure it out! – user2195707 Oct 15 '13 at 21:18
  • 1
    Bergi, numbering the paragraphs won't suffice. It must be the lines within the paragraphs. Many of the articles I'm working on consist of 4 or 5 long paragraphs in a foreign language... So referring to the lines individually is important. – user2195707 Oct 15 '13 at 21:20
  • @user2195707 I posted an answer below. I didn't test it, but it should give you an idea of how to do this (assuming you aren't using enormous text files) – Codeman Oct 15 '13 at 21:26
  • @user2195707: but referring to specific lines doesn't make any sense, if the contents of each line vary according to the width of the display. – David Thomas Oct 16 '13 at 14:11

3 Answers3

2

Here is one approach that may suit your purposes.

  1. Get the height of a one-line paragraph, for reference.
  2. For each paragraph, get the actual height, and infer the number of lines.
  3. Loop through the lines and add the numbering at absolute positions.

var refHeight = $("p").eq(0).height();
$("p").eq(0).remove();
var cnt = 1;
$("p").each(function(index) {
    var pos = $(this).position();
    var h = $(this).height();
    var lines = h / refHeight;
    var lineHeight = h / lines;
    for (var i=pos.top ; i<pos.top+h ; i += lineHeight) {
        var num = $("<p>", { class: "number" });
        num.text(cnt++);
        num.css("top", i);
        $(this).before(num);
        console.log(i);
    }
});

(Fiddle)


Edit

If you wanted to use a fixed line length (so that everyone is seeing the same numbers), you could combine the above with the following:

  1. Break the paragraphs into lines.
  2. Wrap each line in a span/div, and re-append.
  3. Block the browser from text wrapping.

$("p").each(function() {
    var text = $(this).text();
    $(this).html("");
    var i=0;
    while (i<text.length) {
        lineCharWidth = charWidth;
        while (i+lineCharWidth < text.length && text[i+lineCharWidth] != ' ') {
            lineCharWidth++;
        }
        var line = $("<span>", { class: "line" }).text(text.substr(i, lineCharWidth));
        $(this).append(line).append("<br/>");
        i += lineCharWidth;
    }
});

(Fiddle)

Community
  • 1
  • 1
McGarnagle
  • 101,349
  • 31
  • 229
  • 260
1

Here's a solution that uses a function to split the paragraph text on space characters based on a pre-determined line length and then replaces the text with an <ol> comprised of <li> elements each containing one line of text:

var lineNum = 1;

function splitLines(text, lineLen) {
    var words = text.split(/\s/g), line = '', lines = [];
    $.each(words, function(idx) {
        line += this + ' ';
        if (line.length > lineLen || idx == words.length - 1) {
            lines.push(line);
            line = '';
            lineNum += 1;
        }
    });
    return lines;
}

$('p').each(function() {
    var $p = $(this), $ol = $('<ol start="' + lineNum + '">'), lineLen = 50;

    $.each(splitLines($p.text(), lineLen), function(idx) {
        $ol.append('<li>' + this + '</li>');
    });

    $p.text('').append($ol);
});

I'm not sure about the support for the start attribute of the <ol>. It does work in Chrome. Even still, I like using the list element because it's a little more semantically meaningful, in my opinion.

FishBasketGordo
  • 22,904
  • 4
  • 58
  • 91
  • Very nice. Now must figure out how to set the colour of the number in the list item to a colour different from the text.. – user2195707 Oct 15 '13 at 22:08
  • I'll leave that as an exercise for the reader. Suffice it to say, googling "css for ordered lists" brings up several leads. :) – FishBasketGordo Oct 16 '13 at 13:35
  • Ah yes, I always previously used a span around a counter (generated in php) for differently-coloured numbering. But the css :before pseudo class will be great for this. Thanks for the help, FishBasketGordo. – user2195707 Oct 16 '13 at 15:04
0

Sure. Just make sure you're encoding your line returns and use it to split up the text with a simple replace.

Sample text:

The quick
brown fox
jumped over
the lazy dog

for this, the actual string would be the following:

The quick\r\nbrown fox\r\njumped over\r\nthe lazy dog

I think something like this would work (without the document.write, and there could be performance improvements):

var input = '\r\nThe quick\r\nbrown fox\r\njumped over\r\nthe lazy dog';
input = input.replace(/\r\n/g, '<div class=\'index\'></div>');
document.write(input);
var idx = 0;
$('.index').each(function(){
    $(this).text(idx++);
});

If I'm not mistaken, this should write out an index number on each line. Could use some testing/debugging, though :)

For an example of how this is done, check out Github's diff pages.

Maciej Łebkowski
  • 3,837
  • 24
  • 32
Codeman
  • 12,157
  • 10
  • 53
  • 91
  • Thanks... but... where you say "make sure you're encoding your line returns", that's where I'm not sure how it would work. I'm using php/mysql to put the content into a db. When I pull the content back out, how can I have those line returns, short of manually inserting the returns at some point in the workflow? – user2195707 Oct 15 '13 at 21:31
  • The line returns would have to be there as part of the data. You could do this earlier by determining how long each line would be. This may not be a perfect solution, it's just the first thing that came to mind. – Codeman Oct 15 '13 at 21:33