Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Obtain a line of a paragraph in HTML

In JavaScript, is it possible to obtain a specific line from a paragraph that is displayed on a page?

For example, here I'm trying to obtain the 3rd line of a paragraph as a string:

JavaScript:

//this function should return the specified line from the specified paragraph.
function getLine(paragraphId, lineNum){
    //for example, getLine("sampleParagraph", 1); should return
    // tore but year. An from mean on with when sing pain. Oh to as principles devonshire
}

HTML:

<p id = "sampleParagraph">
Instrument cultivated alteration any favourable expression law far nor. Both new like tore but year. An from mean on with when sing pain. Oh to as principles devonshire companions unsatiable an delightful. The ourselves suffering the sincerity. Inhabit her manners adapted age certain. Debating offended at branched striking be subjects.
</p>

Here it is on jsfiddle (showing how the paragraph is displayed): http://jsfiddle.net/RrXWW/

like image 445
Anderson Green Avatar asked Dec 20 '22 07:12

Anderson Green


2 Answers

Demo 1: http://jsfiddle.net/LeTW6/2/
Demo 2: http://jsfiddle.net/LeTW6/3/

I'm using jQuery here for simplicity, but this would work with pure JavaScript. In fact, I'm using direct DOM access for performance in some parts.

  1. Break all words into separate elements.
  2. Use position to calculate the line number.
  3. At the selected line, start building a buffer of all word elements.
  4. Greater than the selected line, quit.
  5. On resize, recalculate (this may not be needed, or may be invoked from a different event).

Code

(function () {

    // wrap all words
    $(".count").each(function () {
        var obj = $(this);
        var html = obj.html().replace(/(\S+\s*)/g, "<span>$1</span>");
        obj.html(html);
    });

    var offset = 0; // keeps track of distance from top
    var spans = $(".count span"); // collection of elements

    function getLine(index) {
        var top = 0,
            buffer = [];

        for (var i = 0; i < spans.length; i++) {

            if (top > index) {
                break; // quit once the line is done to improve performance
            }

            // position calculation
            var newOffset = spans[i].offsetTop;
            if (newOffset !== offset) {
                offset = newOffset;
                top++;
            }

            // store the elements in the line we want
            if (top === index) {
                buffer.push(spans[i]);
            }
        }

        // buffer now contains all spans in the X line position

        // this block is just for output purposes
        var text = "";
        for (var i = 0; i < buffer.length; i++) {
            text += buffer[i].innerHTML;
        }

        $("#output").html(text);
    }

    var line = 3; // the line to select/highlight
    getLine(line); // initial highlighting

    // other recalculation triggers can be added here, such as a button click

    // throttling to handle recalculation upon resize
    var timeout;
    function throttle() {
        window.clearTimeout(timeout);
        timeout = window.setTimeout(function () {
            getLine(line);
        }, 100);
    }

    $(window).on("resize", throttle);
})();

See also my answer for highlighting alternate lines in a variable width container.

like image 55
Tim M. Avatar answered Dec 29 '22 00:12

Tim M.


If you want to use pure JavaScript to improve performance and so that you don't have to include jQuery, you can use this.

DEMO: http://jsfiddle.net/PX7cj/2/

function getLine(paragraphId, lineNum) {
    lineNum--;
    var elem = document.getElementById(paragraphId);
    var spanChildren = elem.getElementsByTagName("span");
    var paragraphText = elem.innerHTML.replace(/(\r\n|\n|\r)/gm, "");
    var newParagraphText = "";
    var words = [];
    if (spanChildren.length === 0) {
        words = paragraphText.split(" ");
        for (var i = 0; max = words.length, i < max; i++)
        newParagraphText += '<span>' + words[i] + "</span> ";
        elem.innerHTML = newParagraphText;
    }else{
        for(var i=0; max = spanChildren.length, i<max; i++){
            words[words.length] = spanChildren[i].innerHTML;
        }
    }
    var lineCounter = 0;
    var previousY = spanChildren[0].offsetTop;
    var returnText = "";
    var startReturning = false;
    for (var i = 0; max = words.length, i < max; i++) {
        if (spanChildren[i].offsetTop != previousY) lineCounter++;
        if (lineCounter === lineNum) startReturning = true;
        if (lineCounter !== lineNum && startReturning) return returnText.substring(0, returnText.length - 1);
        if (startReturning) {
            returnText += words[i] + " ";
            if (i + 1 === words.length) return returnText.substring(0, returnText.length - 1);
        }
        previousY = spanChildren[i].offsetTop;
    }
}

alert(getLine("sampleParagraph", 5));
alert(getLine("sampleParagraph", 4));
like image 29
Tom Avatar answered Dec 28 '22 22:12

Tom