views:

75

answers:

4

What's the best way to visually highlight each word on the page, one at a time? I figure that the words should be broken up into an array and iterated over that way, but what's the best way to do so?

I already know how to perform string replacements and style individual elements, the trick is to do this on each word on the page, one at a time, in the most efficient manner.

+2  A: 

you will need to grab the innerHTML of something, then split it over space, then wrap a span around each word with different classes, and set it back in place of text.

use css to color these differently or something.

i am not sure how you plan to highlight them "one at a time". does that involve an animation or something?

mkoryak
Related example - http://stackoverflow.com/questions/3404850/applying-div-span-tag-to-a-word-by-specific-co-ordinates/3405247#3405247
Andy E
@Andy E, your "related example" may get me there, I'm trying it out. @mkoryak, yes is animation involved. Your answer is the sort of thing I had in my head, but I was looking for the best way to do it.
Cirrostratus
@Cirro: hope you find it useful, it was only intended as an example. Leave a comment if you get stuck with anything I might be able to help with.
Andy E
It turns out that example doesn't work because it doesn't account for HTML tags inside the text
Cirrostratus
@Andy E, are you aware of a method for selecting one line of text in the DOM?
Cirrostratus
@Cirrostratus: yes, it's a big disadvantage but working around it would be very complicated, taking more time than I have spare for answering a question on SO :-) Re: selecting a whole line, it's difficult to know exactly where in the text a line ends and a new one begins with wrapping. You should check out another of my answers - [here](http://stackoverflow.com/questions/2456442/how-can-i-highlight-the-line-of-text-that-is-closest-to-the-mouse/2456631#2456631) - that shows a *sneaky* method of "highlighting" a single line of text.
Andy E
A: 

It's simple.

<script type="text/javascript">
var str="ppshein is coldfusion developer.!";
document.write(str.replace("coldfusion", "<span style='background:orange;'>coldfusion8</span>"));
</script> 
ppshein
That answer only addresses a small part of the question. I already know how to perform string replacements on the DOM.
Cirrostratus
A: 

It actually kind of depends on the text. If you've got tags within the area you're wanting to replace, the regex replace on the innerHTML won't work that well I don't suspect.

Something like this:

<p><strong>F</strong>irst letter of paragraph</p>

The alternative is to iterate over text nodes within the DOM. If you're not targeting IE, you can use the TreeWalker to easily get a collection of text nodes in document order. If you need to target IE, this seems to be a good summary of other ways to iterate text nodes

Then the idea would be to iterate over the text nodes, do the regex split on the node data and wrap each child node in a span with background to highlight.

Here's a fairly hastily written example, but it should give you the idea.

var iterator = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false),
 template = document.createElement('span'),
 span,
 node,
 text,
 newNode;

template.style.backgroundColor = 'yellow';

while ((node = iterator.nextNode())) {
 text = node.data.split(/\b/g);

 for (var i = 0, len = text.length; i < len; i++) {
  newNode = document.createTextNode(text[i]);
  if (text[i].search(/\w+/) >= 0) {
   span = template.cloneNode(false);
   span.appendChild(newNode);
   node.parentNode.insertBefore(span, node);
  } else {
   node.parentNode.insertBefore(newNode, node);
  }
  iterator.currentNode = newNode;
 }
 node.parentNode.removeChild(node);
}

It won't be the fastest method available, but it should be more accurate than a regex replace on the innerHTML. It's also not that difficult to modify the loop to use window.setTimeout to animate the highlighting.

Kyle Jones
A: 

Here is my working but unrefined code that achieves highlighting each word one at a time as well as scrolling when the last word in each line is reached:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
    <html xmlns="http://www.w3.org/1999/xhtml"&gt;
    <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <title></title>
    <style type="text/css">
    span.highlight {background-color:red;}
    </style>
    <script>
    var height;
    var width;
    var spans = document.getElementsByTagName("span");
    var i = 0;
    var timePerChar = 100;

    function getPositionOfElement(el) {
        var elw =  el.offsetWidth;
        var elh =  el.offsetHeight;
        // yay readability
        for (var lx=0, ly=0;
             el != null;
             lx += el.offsetLeft, ly += el.offsetTop, el = el.offsetParent);
        return {x: lx,y: ly,w: elw, h: elh};
    }

    function highlightElements() {
        //alert(spans[i].innerHTML.length);
        var highlightSpeed = timePerChar * spans[i].innerHTML.length;
        spans[i].setAttribute("class", "highlight");
        var objInfo = new Object();
        var nxtObjInfo = new Object();
        objInfo = getPositionOfElement(spans[i]);
        nxtObjInfo = getPositionOfElement(spans[i+1]);
        var amt = (objInfo.x + objInfo.w + 50);
        console.log(amt);
        console.log(width);
        if(amt >= width && objInfo.x > nxtObjInfo.x){
            console.log('SCROLL ' +objInfo.h+ ' ');
            window.scrollBy(0,objInfo.h);
        }
        setTimeout('unHighlight()', highlightSpeed);
    }

    function unHighlight (){
        spans[i].removeAttribute("class");
        i++;
        if(i < spans.length) {
            highlightElements();
        }
    }

    // This is just a namespace 
    var CIRRO = function(){ 
     return { 
      /** 
       * Initialize the page behaviors 
       */ 
      init : function() { 
       CIRRO.wrapWordsWithSpan(); 
      },   
      /** 
       * Replace the contents of each paragraph with span wrapped words 
       */ 
      wrapWordsWithSpan : function() { 
       var paragraphs = document.getElementsByTagName("p");
       //alert(paragraphs.length);
       if(!paragraphs ) return; 
       var j = 0;
       while(j < paragraphs.length) {
            // Parse the text into an array 
           var arr = paragraphs[j].innerHTML.split(" "); 
           // Generate span's for each item/word in the array 
           for( var i = 0; i < arr.length; i++ ) { 
            arr[i] = "<span>" + arr[i] + "</span>"; 
           }    
           paragraphs[j].innerHTML = arr.join(" "); 
           //alert(paragraphs[j].innerHTML); 
           j++
       }
      } 
     }; 
    }(); 
    window.onload = CIRRO.init; 
    </script>
    </head>
    <body id="body">
    <input type="button" onclick="highlightElements();" value="Get selected elements" />
    <p>Test test test test test Test test test test test Test test test test test Test 
test test test test Test test test test test Test test test test test Test test test test 
test Test test test test test Test test test test test Test test test test test Test test 
test test test Test test test test test Test test test test test Test test test test test 
Test test test test test Test test test test test Test test test test test Test test test 
test test</p>
    <script>
    var test = document.getElementById("body");
    height = (test.clientHeight + 1);
    width = (test.clientWidth + 1);
    //alert(width +' '+ height);
    </script>
    </body>

</html>
Cirrostratus
Thanks for every one's help.
Cirrostratus