views:

280

answers:

6

EDIT [how can I] change the color of every R and r in my HTML document with javascript?

A: 

You would need to use the DOM (or jQuery) to iterate through every text node in the document. Whenever you find the letter R, apply a transformation that wraps the letter in an appropriate element.

e.g. Transform the text node "art" into "a<span class="colored">r</span>t". This adds two new text nodes, "r" and "t", and the new span element.

Matthew
A: 

Yes this is possible with a little Javascript, a smattering of CSS and some regex.

First, you need to define a style which provides the colour you require (in my example below I refer to a CSS class called "new-colour"), and then run some regex over your HTML content which does a search and replace. You are looking to change all 'r' and 'R' characters into something like this (as an example):

<span class="new-colour">r</span>

If you don't know regex, there are oodles of resources out there to get you started. You will be pleased to know that your requirement is very simple, so no worries there. Here are a couple of links:

regexlib.com

8 regular expressions you should know

Ben Poole
-1 for using regex to complicate the problem
Chris Shouts
Why not use regex? The best answer so far uses it, and it's probably the easiest way to do it that I can see (although I might be missing something).
igul222
-1 for using regex because it's "complicated"? Good grief.
Ben Poole
A: 

Plain JS solution without need of any 20kB JS library:

var body = document.getElementsByTagName("body")[0];
var html = body.innerHTML
               .replace(/(^|>[^<rR]*)([rR])/g, "$1<em>$2</em>");
body.innerHTML = html; // note that you will lose all
                       // event handlers in this step...
Boldewyn
Libraries are a programmers friend. Don't reinvent the wheel, especially when the reinvented solution has caveats.
Sam
-1 for "note that you will loose all event handlers and stuff in this step..." since it makes your solution so destructive.
Justin Johnson
-1 for the above and also for writing "loose" when you meant "lose"
benzado
@Sam: I wouldn't rate jQuery plug-ins as being in the same class of invention as the wheel. With some jQuery plug-ins a reinvention may very well be appropriate. And the jQuery solution here very much has caveats, not least of which is the need to use a 50K library to do something you could do without it in a few lines of code.
Tim Down
So what? You have a single task and you want to solve it. I'm using jQuery mayself, and MooTools, and Prototype. And I learned lot of my JS skills from looking at their source. *But* I use them with care and never without reason. If your only task is to highlight something in a page, 20kB of JS *are overkill*. I still think, that my solution is a good, clean one. *And* if you do the same in jQuery/pürototype/whatever, you *also* loose the event handlers, if you don't know what you do.
Boldewyn
@benzado: If you give your -1s on misspellings: Rest assured, I correct them if one points them out.
Boldewyn
@Justin Johnson: So you give -1s, if someone comments his code?
Boldewyn
+6  A: 

I'd use the highlight plugin for jQuery. Then do something like:

$('*').highlight('r'); // Not sure if it's case-insensitive or not

and in CSS:

.highlight { background-color: yellow; }
Chris Doggett
It **is** case-insensitive.
Jonathan Sampson
Thanks, Jonathan. Not sure if you can chain it, either, so two calls may be necessary.
Chris Doggett
$('*').highlight(['r','R']); works for both cases
Dan
Chris, It's case-**insensitive**, meaning it will already replace both upper-case and lower-case without the need for two calls.
Jonathan Sampson
Ahh, sorry, misread that.
Chris Doggett
Very cool! Didn't know about this plugin.
Ben Poole
+2  A: 

Doable, but not super easy. There's no CSS way to do it.

Basically, you'll need to use Javascript and iterate through the all nodes. If it's a text node, you can search it for "R" and then replace the R with a <span style="color:red">R</span>

I am obviously simplifying this a bit, it's probably better to just dynamically add a "highlight" class, rather than hard code a style, and have that defined in CSS. Similarly, I'm sure you'll wanna parameterize the search string. Also, this doesn't take into account what the text node is, for instance, I have special handling to skip comments, but you'll probably find there's other things (script nodes?) you also need to skip.

 function updateNodes(node) { 
   if (node.nextSibling)
     updateNodes(node.nextSibling);
   if (node.nodeType ==8) return; //Don't update comments
   if (node.firstChild)
      updateNodes(node.firstChild);
   if (node.nodeValue) { // update me    
       if (node.nodeValue.search(/[Rr]/) > -1){ // does the text node have an R
               var span=document.createElement("span");
               var remainingText = node.nodeValue;
               var newValue='';
               while (remainingText.search(/[Rr]/) > -1){ //Crawl through the node finding each R
                 var rPos = remainingText.search(/[Rr]/);
                 var bit = remainingText.substr(0,rPos);
                 var r = remainingText.substr(rPos,1);
                 remainingText=remainingText.substr(rPos+1);
                 newValue+=bit;
                 newValue+='<span style="color:red">';
                 newValue+=r;
                 newValue+='</span>';
               }
               newValue+=remainingText;
               span.innerHTML=newValue;
               node.parentNode.insertBefore(span,node);
               node.parentNode.removeChild(node);   
            } 
        }
     } 

        function replace(){   updateNodes(document.body); 
}
Mark
A: 

The highlight plugin for jQuery is one option. Another option - especially since to-morrow - you might want to extend your highlighting into keywords or other terms is to use Google's Closure goog.dom.annotate Class. The beauty of this Class is that it will actually parse the dom tree properly and ONLY annotate the relevant terms. It will also allow you to EXCLUDE elements or elements with certain classes.

A common problem with annotations is that you can mess your HTML, if you are not careful.

For example the 'simple solution posted above'

var body = document.getElementsByTagName("body")[0];
var html = body.innerHTML
.replace(/(^|>[^<rR]*)([rR])/g, "$1<em>$2</em>");
body.innerHTML = html;

will surely also capture terms in any style attributes. If you had this:

<p class="red">text......</p>

It will become

<p class="<span class="red">r</span>ed .....

that will break your html.

In general DOM parsing is 'slow', so try and avoid annotating the whole body of a webpage, ask yourself why you only need to highlight the R's? Actually I am curious why do you want to annotate the r's?:)

yannis
My "simple solution posted above" will *not* capture attribute values. There is some magic preventing it, which is called in arcane and occult circles as "regular expression look behind".
Boldewyn
And you don't really want to use Google Closure: http://www.sitepoint.com/blogs/2009/11/12/google-closure-how-not-to-write-javascript/
Boldewyn