7

Say I've a p element or div element having a text say about 10-15 lines, now my client has a weird call on this, he needs odd/even lines having different text color. Say Line 1 - Black, so Line 2 should be Grey, Line 3 should be again black and so on...

So I decided using span's and changed the color but variable resolution is killing things here, Am aware of the :first-line selector (Which won't be useful in this case), also selectors like :odd & :even will be ruled out here as am not using tables, so is there any way I can achieve this using CSS or do I need to use jQuery?

TL; DR : I want to target odd/even lines in a paragraph or a div

I need a CSS solution, if not, jQuery and JavaScript are welcome

VisioN
  • 143,310
  • 32
  • 282
  • 281
Mr. Alien
  • 153,751
  • 34
  • 298
  • 278
  • 1
    It's "TL;DR" for "too long; didn't read", not "TLTR"/"too long to read" :) – BoltClock Apr 05 '13 at 09:34
  • Anyway, you can't style the nth line with CSS. You'll need JavaScript. – BoltClock Apr 05 '13 at 09:35
  • @BoltClock Just a typo :p – Mr. Alien Apr 05 '13 at 09:37
  • There is no way to do this is css; you will have to use jQuery. I think the answer you're looking for is [here](http://stackoverflow.com/questions/4147080/can-i-wrap-each-line-of-multi-line-text-in-a-span/4148445#4148445) Another useful answer [here](http://stackoverflow.com/questions/2456442/how-can-i-highlight-the-line-of-text-that-is-closest-to-the-mouse/2456582#2456582) – Bill Apr 05 '13 at 09:57

3 Answers3

12

Demo 1

http://jsfiddle.net/Fptq2/2/
Should work in all modern browsers.

Essentially it:

  1. Splits the source into individual words once
  2. Wraps each word in a span (ugly but effective-any style can now be applied to the span)
  3. Uses a simple position calculation to determine if the element is lower than the previous
  4. Changes colors based on index change
  5. Performs #3-5 on resize (this should definitely be throttled!)
$(".stripe").each(function(){
  var obj = $(this);
  var html = obj.html().replace(/(\S+\s*)/g, "<span>$1</span>");
  obj.html(html);
});

function highlight(){
    var offset = 0;
    var colorIndex = 0;
    var colors = ["#eee","#000"];
    var spans = $(".stripe span");

    // note the direct DOM usage here (no jQuery) for performance
    for(var i = 0; i < spans.length; i++){
        var newOffset = spans[i].offsetTop;  

        if(newOffset !== offset){
            colorIndex = colorIndex === 0 ? 1 : 0;
            offset = newOffset;
       }

       spans[i].style.color = colors[colorIndex];
    }
}

highlight();
$(window).on("resize", highlight);

Demo 2

Fiddle: http://jsfiddle.net/Fptq2/4/

  • Uses a larger block of text
  • Shows effect applied to multiple elements
  • Caches the "all spans" selector
  • Adds resize throttling
(function () {
    $(".stripe").each(function () {
        var obj = $(this);
        var html = obj.html().replace(/(\S+\s*)/g, "<span>$1</span>");
        obj.html(html);
    });

    var offset = 0;
    var colorIndex = 0;
    var colors = ["#ccc", "#000"];
    var spans = $(".stripe span");

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

            var newOffset = spans[i].offsetTop;
            if (newOffset !== offset) {
                colorIndex = colorIndex === 0 ? 1 : 0;
                offset = newOffset;
            }

            spans[i].style.color = colors[colorIndex];
        }
    }

    highlight(); // initial highlighting

    var timeout;
    function throttle(){
        window.clearTimeout(timeout);
        timeout = window.setTimeout(highlight, 100);
    }

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

Output

enter image description here

Tim M.
  • 53,671
  • 14
  • 120
  • 163
4

Here is one possible solution. It generates a number of <div> elements that are placed behind the text. <div> elements inherit font size from the parent container, so the markup shouldn't be damaged.

HTML:

<div id="test">Lorem ipsum ...</div>

JavaScript:

var div = document.getElementById("test"),
    layer = document.createElement("div"),
    text = div.innerHTML,
    lineHeight;

layer.appendChild(document.createTextNode("\u00A0"));
div.insertBefore(layer, div.firstChild);

lineHeight = layer.offsetHeight;
div.style.position = "relative";
div.style.overflow = "hidden";
div.style.color = "transparent";
layer.style.position = "absolute";
layer.style.zIndex = "-1";

window.addEventListener("resize", (function highlight() {
    while (layer.firstChild)
        layer.removeChild(layer.firstChild);

    for (var i = 0, n = Math.ceil(div.offsetHeight / lineHeight); i < n; i++) {
        var line = document.createElement("div"),
            block = document.createElement("div");

        line.style.height = lineHeight + "px";
        line.style.color = i % 2 ? "#ccc" : "#aaa";
        line.style.overflow = "hidden";

        block.innerHTML = text;
        block.style.marginTop = -i * lineHeight + "px";

        line.appendChild(block);
        layer.appendChild(line);
    }
    return highlight;
})(), false);

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

VisioN
  • 143,310
  • 32
  • 282
  • 281
  • I had this same idea, but OP wants text-color, not background :/ – Bill Apr 05 '13 at 09:56
  • Brilliant, this fails to detect line breaks, but nice try +1, any idea to change color of the text or target an element instead of spoofing up a background? – Mr. Alien Apr 05 '13 at 09:59
  • @VisioN If you do, than you are about to be a star :p – Mr. Alien Apr 05 '13 at 10:02
  • 1
    @Mr.Alien Yeeeeah! Check the updated solution. Now it does exactly what was expected. I've tried to optimize the code to be fast and short. As previously it doesn't require any third party libraries. – VisioN Apr 05 '13 at 12:31
  • @VisioN Cheers :) and btw if you want I'll find you more bugs, text cannot be selected now hehe but really a nice attempt – Mr. Alien Apr 05 '13 at 12:39
  • 1
    @Mr.Alien Which browser do you use? I was able to select it perfectly in Chrome and FF. I have specified `z-index` for the inner absolute block, you could try now. – VisioN Apr 05 '13 at 13:57
1

It is too late to answer this question.. But if this answer help to others who wants to separate a wrapped text paragraph into separate lines then I am very happy
Converting wrapped text to lines (either for line numbering or to break each line into individual elements) is a question that comes up on the boards quite a bit, and I finally had a need to do so, so here it is (for both MooTools and jQuery – the jQuery version is not tested, so if there’s any issues please leave a comment). This particular incarnation breaks each wrapped line into a distinct new element, but could be modified to just count lines pretty easily.
using the Code you can achieve this $("#someElement").linify()
Here the following URL is the Proof Of Concept for this
jquery-mootools

Here is the Quick fiddle which demonstrates the wrapped text lines to individual div items.
http://jsfiddle.net/UANeP/

Mohasin Ali
  • 3,955
  • 1
  • 20
  • 24