views:

271

answers:

4

Unusual situation. I have a client, let's call them "BuyNow." They would like for every instance of their name throughout the copy of their site to be stylized like "BuyNow," where the second half of their name is in bold.

I'd really hate to spend a day adding <strong> tags to all the copy. Is there a good way to do this using jQuery?

I've seen the highlight plugin for jQuery and it's very close, but I need to bold just the second half of that word.

+4  A: 

Regular Expressions and replace() spring to mind. Something like

var text = $([selector]).html();
text = text.replace(/Now/g,'<strong>Now<\strong>');
$([selector]).html(text);

A word of caution in using html() to do this. Firstly, there is the potential to replace matched strings in href attributes of <a> elements and other attributes that may cause the page to then incorrectly function. It might be possible to write a better regular expression to overcome some of the potential problems, but performance may suffer (I'm no regular expression guru). Secondly, using html() to replace content will cause non-serializable data such as event handlers bound to elements markup that is replaced, form data, etc. to be lost. Writing a function to target only text nodes may be the better/safer option, it just depends on how complex the pages are.

If you have access to the HMTL files, it would probably be better to do a find and replace on the words they want to change the appearance of in the files if the content is static. NotePad++'s Find in Files option is performant for this job in most cases.

Going with SingleShot's suggestion and using a <span> with a CSS class will afford more flexibility than using a <strong> element.

Russ Cam
Don't parse HTML with regex. `<a href="BuyNow.html">` -> whoops.
bobince
@bobince - is there a better/more correct way to do it?
Russ Cam
Just testing one! Also note setting `html()` will lose all your non-serialisable information, such as event handlers, JavaScript references and form data.
bobince
@bobince - I'll put some caveats in the answer. The safest way would probably be to only operate on text nodes, but I see that you've got that angle covered in your answer :)
Russ Cam
You should better cache the element than the text: `var elem = $([selector]); elem.html(elem.html().replace(/Now/g,'<strong>Now<\strong>'));`
Gumbo
The content will ultimately be in a CMS, so doing a global find/replace (in Coda, in my case :) was not my favored solution, though it crossed my mind. I opted for the other solution simply because the bolded word is also used <strong>frequently</strong> throughout the site, including page names, so that's out too. But thanks. :)
Ben Dyer
+1  A: 

I wrote a little plugin to do just that. Take a look at my answer to a similar question.

Instead of downloading the plugin suggested in the accepted answer, I strongly recommend that you use the plugin I've written--it's a lot faster.

roosteronacid
+5  A: 

To do it reliably you'd have to iterate over each element in the document looking for text nodes, and searching for text in those. (This is what the plugin noted in the question does.)

Here's a plain JavaScript/DOM one that allows a RegExp pattern to match. jQuery doesn't really give you much help here since selectors can only select elements, and the ‘:contains’ selector is recursive so not too useful to us.

// Find text in descendents of an element, in reverse document order
// pattern must be a regexp with global flag
//
function findText(element, pattern, callback) {
    for (var childi= element.childNodes.length; childi-->0;) {
        var child= element.childNodes[childi];
        if (child.nodeType==1) {
            findText(child, pattern, callback);
        } else if (child.nodeType==3) {
            var matches= [];
            var match;
            while (match= pattern.exec(child.data))
                matches.push(match);
            for (var i= matches.length; i-->0;)
                callback.call(window, child, matches[i]);
        }
    }
}

findText(document.body, /\bBuyNow\b/g, function(node, match) {
    var span= document.createElement('span');
    span.className= 'highlight';
    node.splitText(match.index+6);
    span.appendChild(node.splitText(match.index+3));
    node.parentNode.insertBefore(span, node.nextSibling);
});
bobince
A: 
var Run=Run || {};

Run.makestrong= function(hoo, Rx){
 if(hoo.data){
  var X= document.createElement('strong');
  X.style.color= 'red'; // testing only, easier to spot changes
  var pa= hoo.parentNode;
  var res, el, tem;
  var str= hoo.data;
  while(str && (res= Rx.exec(str))!= null){
   var tem= res[1];
   el= X.cloneNode(true);
   el.appendChild(document.createTextNode(tem));
   hoo.replaceData(res.index, tem.length,'');
   hoo= hoo.splitText(res.index);
   str= hoo.data;
   if(str) pa.insertBefore(el, hoo);
   else{
    pa.appendChild(el);
    return;
   }
  }
 }
}

Run.godeep= function(hoo, fun, arg){
 var A= [];
 if(hoo){
  hoo= hoo.firstChild;
  while(hoo!= null){
   if(hoo.nodeType== 3){
    if(hoo.data) A[A.length]= fun(hoo, arg);
   }
   else A= A.concat(arguments.callee(hoo, fun, arg));
   hoo= hoo.nextSibling;
  }
 }
 return A;
}

//test

**Run.godeep(document.body, Run.makestrong,/([Ee]+)/g);**
kennebec