4

I want to change the background color of a single line in a multi-line paragraph when the mouse is over that line (over any word in that line) - can this be achieved using JQuery/JS?

If so, how?

Edit: To clarify, I want any line to be highlighted once the mouse is over it.
The script will have to dynamically isolate the line that the cursor is over and apply a temporary style to it while the mouse is over it.

Edit 2:
A picture for illustration -
enter image description here

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Acidic
  • 6,154
  • 12
  • 46
  • 80

4 Answers4

3

It was a hard fought battle, but I came up with a way to do this without any requirements for styles on the container at all (including its font, alignment, etc.).

This is not a perfect solution, but hopefully it works for your purposes.

var
    //Keeps the content (individual spans) as they are built.
    $keeper = $("<div>"),
    //Used to measure span width for comparison to container.
    $measurer = $("<div>"),
    //The container of the text content
    $p = $("p"),
    //An individual line of the content
    $line = $("<span>").appendTo($measurer),

//make this "invisible," but allow for measurement against container width
//with no restriction on measurer's own width (fixed)
$measurer.css({'position': 'fixed', 'top': '100%'}).appendTo("body");

//Iterate through each "word" derived from splitting on any space.
$p.text().split(/\s/).forEach(function (elem) {
    //Start forming line text.  Don't forget word spacing
    $line.text($line.text() + elem + ' ');

    //Enough words to make the line width longer than the p width.
    //This indicates the end of "line."
    if ($line.width() > $p.width()) {
        //Remove the last word.
        $line.text(function (_, text) {
            return text.slice(0, text.lastIndexOf(elem));
        });

        //Keep the currently formed line to add back later
        $line.appendTo($keeper);

        //Create a new line for measuring
        $line = $("<span>");
        $line.text(' ' + elem).appendTo($measurer);
    }
});
//Append any leftover words not big enough to form a whole line
$keeper.append($measurer.html());
//Add back content
$p.html($keeper.html());
//cleanup
$keeper.remove();
$measurer.remove();

http://jsfiddle.net/6Cx3h/2/

You can also do this on window resize in case the container's width is window-dependent.

(you can see my attempt using height instead of width at http://jsfiddle.net/6Cx3h)

Explosion Pills
  • 188,624
  • 52
  • 326
  • 405
1

Each line shoud be a single tag, you could use <span> if you want, and then with css (better than javascript or jQuery) you could do

span:hover{
  background-color: #ccc;
}
martriay
  • 5,632
  • 3
  • 29
  • 39
  • Unfortunately that's impossible. There are thousands of lines and their width might potentially change in the future. – Acidic Mar 01 '13 at 00:17
1

It all depends on how your text is formatted originally. From the screen shot you show, it looks to me like the text is being wrapped in an element that is enforcing line wraps. In this case there is no real "new line" in the whole text. It's would change if the size of the element changed... As an example, the text you are reading now is being wrapped within the constrains of the site...

But if I were to
insert my own line
breaks, then the
following method
might be of use.

// extract the text
var paragraph = $("#text").text();
// split the text into lines by searching for linebreaks
var lines = paragraph.split(/\r?\n/);
// clear the original text
$("#text").text('');
$.each(lines,function(index,elem){
  if (elem != ''){
    // for each line, wrap it with a span and bring back the linebreak
    $("#text").append('<span>'+elem+'</span><br/>');      
  }
});

Now all you would have to do is make some css rule to highlight the span elements on a hover event -

span:hover { background:#DDD }

A live example

Lix
  • 47,311
  • 12
  • 103
  • 131
  • If you're about to add a `
    ` at each line why not add a ``? I don't think this is what Acidic is looking for.
    – otinanai Mar 01 '13 at 00:36
  • @oti - I don't follow you... I am adding a span. – Lix Mar 01 '13 at 00:39
  • I'm not too sure about performance... you might need to test that out for yourself... – Lix Mar 01 '13 at 00:40
  • Oh, actually, your edit is correct. The line breaks in my example are forced by the browser via the width of the paragraph; there is nothing in the text itself to indicate where the new line begins. – Acidic Mar 01 '13 at 00:43
  • what I mean is that the paragraph is not formatted. If you can format it with breaks, why not format it with ``s? – otinanai Mar 01 '13 at 00:43
0

You can simulate highlighting of a single line in a non-formatted paragraph by creating a block that will be placed behind each line of text. This block will have the same size as your p line-height and according to the mouse y-position the top position can change. This approach can not be consider as a native highlight but it has some advantages over the other suggestions. First, it can be accommodated to any text. Second, it can calculate the number of lines even after resizing a fluid text block. Third, you keep your text clean from any extra markup (<br/>) or other breaks.

The HTML:

<p>Some long non-formatted - fluid text</p>

The CSS:

p {
    position:relative;
    text-align:justify;
    font: 14px/18px Arial; /*Line-height is essential to be defined because not all browsers translate the default value in px - e.g. Chrome returns "normal" and FF returns pixels*/
}
p span {  /*The highlighter*/
    background:yellow; /*Highlight color*/
    display:none; /*Hide it until we have a hover*/
    position:absolute;
    left:0;
    z-index:-1; /*Place it behind the text*/
}

The jQuery:

//get the line-height
var theLineheight = $('p').css('line-height'); 
//strip it from the 'px'
var Lineheight = parseInt(theLineheight); 
//get the text height
var thePheight = $('p').height(); 
//update the height after resize
window.onresize = function () {
    return thePheight = $('p').height(); 
}
//create the highlight box
$('p').append('<span style="height:' + theLineheight + ';width:100%"></span>'); 
//detect mouse movement in the text container
$('p').mousemove(function (e) {
    //show the highlighter
    $('p span').show();
    //get the mouse position
    mouseTop = e.pageY - this.offsetTop;
    //round it to a line-height scale
    topPos = Math.ceil(mouseTop / Lineheight) * Lineheight - Lineheight;
    //position the highlighter vertical
    $('p span').css('top', topPos + 'px');
    //hide the highlighter when mouse is at the end of the text - including the space that highlighter takes
    if (topPos > (thePheight - Lineheight)) {
        $('p span').hide();
    }
//hide the highlighter when mouse is not over the text
}).mouseout(function (e) {
    $('p span').hide();
});

Here's the demo: http://jsfiddle.net/gh8gZ/1/

The only disadvantages I can see to my suggestion is that when you have a text block with empty lines they will be highlighted as well and also, the highlighted area takes the whole width of the line - although this doesn't bother me at all but I have to indicate that this is not a perfect solution.

otinanai
  • 3,987
  • 3
  • 25
  • 43