james_womack
james_womack

Reputation: 10296

Highlight each word on a webpage, one at a time, with JavaScript

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.

Upvotes: 2

Views: 3160

Answers (4)

james_womack
james_womack

Reputation: 10296

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">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <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>

Upvotes: 0

Kyle Jones
Kyle Jones

Reputation: 536

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.

Upvotes: 0

Pyae Phyoe Shein
Pyae Phyoe Shein

Reputation: 13797

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> 

Upvotes: 0

mkoryak
mkoryak

Reputation: 57938

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?

Upvotes: 2

Related Questions