8

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.

Cody Guldner
  • 2,888
  • 1
  • 25
  • 36
Ben Dyer
  • 205
  • 1
  • 4
  • 7
  • 6
    Rather than marking it strong, I would think you'd want to it so you can apply CSS to mark it strong, blue, or whatever... not that that answers your jquery question ;-) – SingleShot Sep 30 '09 at 22:40
  • @SingleShot - agreed, a `` with a class will afford you more flexibility – Russ Cam Sep 30 '09 at 22:45
  • 1
    Yes, semantically **Now** is not strongly emphasised, unless you *really* shout “NOW!” when you say the company name! It's a typographical quirk, for which `` would be much more appropriate. – bobince Sep 30 '09 at 22:54
  • 1
    Have a look at [mark.js](https://markjs.io/) which will do it for you. – dude May 24 '16 at 06:43

5 Answers5

12

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
  • 528,062
  • 107
  • 651
  • 834
4

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
  • 124,184
  • 33
  • 204
  • 266
  • Don't parse HTML with regex. `` -> whoops. – bobince Sep 30 '09 at 22:55
  • 1
    Just testing one! Also note setting `html()` will lose all your non-serialisable information, such as event handlers, JavaScript references and form data. – bobince Sep 30 '09 at 23:26
  • @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 Oct 01 '09 at 11:23
  • You should better cache the element than the text: `var elem = $([selector]); elem.html(elem.html().replace(/Now/g,'Now<\strong>'));` – Gumbo Oct 01 '09 at 11:32
  • 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 frequently throughout the site, including page names, so that's out too. But thanks. :) – Ben Dyer Oct 01 '09 at 14:39
1

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.

Community
  • 1
  • 1
cllpse
  • 21,396
  • 37
  • 131
  • 170
0
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);**
cllpse
  • 21,396
  • 37
  • 131
  • 170
kennebec
  • 102,654
  • 32
  • 106
  • 127
0

This is not a jQuery script but pure javaScript, i believe it can be altered a little. Link.

Samuel
  • 9,883
  • 5
  • 45
  • 57