104

I basically need to highlight a particular word in a block of text. For example, pretend I wanted to highlight the word "dolor" in this text:

<p>
    Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
</p>
<p>
    Quisque bibendum sem ut lacus. Integer dolor ullamcorper libero.
    Aliquam rhoncus eros at augue. Suspendisse vitae mauris.
</p>

How do I convert the above to something like this:

<p>
    Lorem ipsum <span class="myClass">dolor</span> sit amet, consectetuer adipiscing elit.
</p>
<p>
    Quisque bibendum sem ut lacus. Integer <span class="myClass">dolor</span> ullamcorper
    libero. Aliquam rhoncus eros at augue. Suspendisse vitae mauris.
</p>

Is this possible with jQuery?

Edit: As Sebastian pointed out, this is quite possible without jQuery - but I was hoping there might be a special method of jQuery which would let you do selectors on the text itself. I'm already using jQuery heavily on this site, so keeping everything wrapped up in jQuery would make things perhaps a bit more tidy.

arhak
  • 2,488
  • 1
  • 24
  • 38
nickf
  • 537,072
  • 198
  • 649
  • 721
  • 1
    Instead of highlighting words with a ``, it is more correct to use ``, semantically speaking. – Jose Rui Santos Mar 06 '12 at 06:30
  • Hi I am on board late but here is yet another code snippet that helps highlight and filter text based on tags. Hopefully that will help someone [jQuery Plugin for text highlighting and Filtering](http://jaspreetchahal.org/jquery-text-highlighter-and-filter-plugin/) – Jaspreet Chahal Jun 27 '12 at 11:28
  • 3
    In case you need a lenient version of the jQuery highlight plugin: [http://www.frightanic.com/2011/02/27/lenient-jquery-highlight-plugin-javascript/](http://www.frightanic.com/2011/02/27/lenient-jquery-highlight-plugin-javascript/) – Marcel Stör Mar 07 '11 at 20:57
  • Hey, I wrote a plugin that does exactly this -- it's like the Johann Burkard plugin mlarsen posted, but works with regular expressions instead of strings. [Check it out on github](http://github.com/jbr/jQuery.highlightRegex) and please let me know if there are additional features you need. –  Jul 01 '09 at 08:39
  • This plugin may be of interest for you: https://github.com/julmot/jmHighlight . It can **highlight keywords separately or as a term**, can highlight the match **with your custom element and classname** and can also **search for diacritics**. On top it allows you to **filter the context** in which to search for matches. – dude Sep 24 '15 at 09:44

13 Answers13

87

Try highlight: JavaScript text highlighting jQuery plugin. Warning: The source code available on this page contains a cryptocurrency mining script, either use the code below or remove the mining script from the script downloaded from the website.

/*

highlight v4

Highlights arbitrary terms.

<http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html>

MIT license.

Johann Burkard
<http://johannburkard.de>
<mailto:jb@eaio.com>

*/

jQuery.fn.highlight = function(pat) {
 function innerHighlight(node, pat) {
  var skip = 0;
  if (node.nodeType == 3) {
   var pos = node.data.toUpperCase().indexOf(pat);
   if (pos >= 0) {
    var spannode = document.createElement('span');
    spannode.className = 'highlight';
    var middlebit = node.splitText(pos);
    var endbit = middlebit.splitText(pat.length);
    var middleclone = middlebit.cloneNode(true);
    spannode.appendChild(middleclone);
    middlebit.parentNode.replaceChild(spannode, middlebit);
    skip = 1;
   }
  }
  else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
   for (var i = 0; i < node.childNodes.length; ++i) {
    i += innerHighlight(node.childNodes[i], pat);
   }
  }
  return skip;
 }
 return this.length && pat && pat.length ? this.each(function() {
  innerHighlight(this, pat.toUpperCase());
 }) : this;
};

jQuery.fn.removeHighlight = function() {
 return this.find("span.highlight").each(function() {
  this.parentNode.firstChild.nodeName;
  with (this.parentNode) {
   replaceChild(this.firstChild, this);
   normalize();
  }
 }).end();
};

Also try the "updated" version of the original script.

/*
 * jQuery Highlight plugin
 *
 * Based on highlight v3 by Johann Burkard
 * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html
 *
 * Code a little bit refactored and cleaned (in my humble opinion).
 * Most important changes:
 *  - has an option to highlight only entire words (wordsOnly - false by default),
 *  - has an option to be case sensitive (caseSensitive - false by default)
 *  - highlight element tag and class names can be specified in options
 *
 * Usage:
 *   // wrap every occurrance of text 'lorem' in content
 *   // with <span class='highlight'> (default options)
 *   $('#content').highlight('lorem');
 *
 *   // search for and highlight more terms at once
 *   // so you can save some time on traversing DOM
 *   $('#content').highlight(['lorem', 'ipsum']);
 *   $('#content').highlight('lorem ipsum');
 *
 *   // search only for entire word 'lorem'
 *   $('#content').highlight('lorem', { wordsOnly: true });
 *
 *   // don't ignore case during search of term 'lorem'
 *   $('#content').highlight('lorem', { caseSensitive: true });
 *
 *   // wrap every occurrance of term 'ipsum' in content
 *   // with <em class='important'>
 *   $('#content').highlight('ipsum', { element: 'em', className: 'important' });
 *
 *   // remove default highlight
 *   $('#content').unhighlight();
 *
 *   // remove custom highlight
 *   $('#content').unhighlight({ element: 'em', className: 'important' });
 *
 *
 * Copyright (c) 2009 Bartek Szopka
 *
 * Licensed under MIT license.
 *
 */

jQuery.extend({
    highlight: function (node, re, nodeName, className) {
        if (node.nodeType === 3) {
            var match = node.data.match(re);
            if (match) {
                var highlight = document.createElement(nodeName || 'span');
                highlight.className = className || 'highlight';
                var wordNode = node.splitText(match.index);
                wordNode.splitText(match[0].length);
                var wordClone = wordNode.cloneNode(true);
                highlight.appendChild(wordClone);
                wordNode.parentNode.replaceChild(highlight, wordNode);
                return 1; //skip added node in parent
            }
        } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
                !/(script|style)/i.test(node.tagName) && // ignore script and style nodes
                !(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
            for (var i = 0; i < node.childNodes.length; i++) {
                i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
            }
        }
        return 0;
    }
});

jQuery.fn.unhighlight = function (options) {
    var settings = { className: 'highlight', element: 'span' };
    jQuery.extend(settings, options);

    return this.find(settings.element + "." + settings.className).each(function () {
        var parent = this.parentNode;
        parent.replaceChild(this.firstChild, this);
        parent.normalize();
    }).end();
};

jQuery.fn.highlight = function (words, options) {
    var settings = { className: 'highlight', element: 'span', caseSensitive: false, wordsOnly: false };
    jQuery.extend(settings, options);
    
    if (words.constructor === String) {
        words = [words];
    }
    words = jQuery.grep(words, function(word, i){
      return word != '';
    });
    words = jQuery.map(words, function(word, i) {
      return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
    });
    if (words.length == 0) { return this; };

    var flag = settings.caseSensitive ? "" : "i";
    var pattern = "(" + words.join("|") + ")";
    if (settings.wordsOnly) {
        pattern = "\\b" + pattern + "\\b";
    }
    var re = new RegExp(pattern, flag);
    
    return this.each(function () {
        jQuery.highlight(this, re, settings.element, settings.className);
    });
};
SuperStormer
  • 4,997
  • 5
  • 25
  • 35
mlarsen
  • 4,617
  • 1
  • 20
  • 17
  • There are two solutions, and they are contained to one file each. I added them above. At least, as a worst-case scenario, they will always be available here in the edit history. – Erick Robertson Apr 21 '14 at 23:13
  • highlight v4 is buggy a bit. There is a fix on Burkard's home page: http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html In this case it was not a good idea to copy the code here; the link points to the latest version (now :)). – Lerin Sonberg Jan 24 '15 at 09:56
  • By the way the -tag is probably better than the tag here. – unitario May 20 '15 at 05:06
  • This plugin: https://github.com/julmot/jmHighlight was build on the idea of the posted plugins above. But it has many improvements that may be helpful for others. It can **highlight keywords separately or as a term**, can highlight the match **with your custom element and classname** and can also **search for diacritics**. On top it allows you to **filter the context** in which to search for matches. – dude Sep 24 '15 at 09:48
  • 1
    If you're looking for small and lightweight, the highlight jquery plugin is indeed your best choice. It's great at highlight-ing and removing highlights matching given text. If you need regular expression or other support; however, check out mark.js or any of the extensions and forks for highlight linked to from the highlight page. I use highlight myself over others because lightweight is highly appreciated. – Greg Jul 08 '16 at 13:42
  • 5
    IMPORTANT: Johann Burkard included a mining script into the source provided on his website!!!!!! – Lukars Mar 12 '18 at 15:03
  • Thanks for the code, just works as required. Just one thing, when the word get highlighted and the selection is inside the word to highlight, the selection get changed to the beginning of the word to highlight. Is it possible to keep the selection at the same position in the highlighted word also after the word get highlighted? – Paolo Mosna Nov 25 '20 at 08:54
42
function hiliter(word, element) {
    var rgxp = new RegExp(word, 'g');
    var repl = '<span class="myClass">' + word + '</span>';
    element.innerHTML = element.innerHTML.replace(rgxp, repl);
}
hiliter('dolor');
Andrew Hedges
  • 21,688
  • 16
  • 67
  • 79
  • 2
    You don't want to use innerHTML as it was introduced by Microsoft in the 80ies, and later dropped by Microsoft again, as usual. Even though most browser support it, it is everything but W3C standart. – Stephan Kristyn Aug 22 '12 at 16:36
  • 22
    What should you use instead of innerHTML? – Kebman May 03 '13 at 10:34
  • 15
    @Sir Ben Benji: I think you're confusing innerHTML with innerText (the Microsoft-developed alternative to textContent, which is indeed anathema to the spec). innerHTML may have started as a Microsoft extension but in no way has been "dropped"; it's been supported by every major browser since the very early 2000's, and is part of HTML5 (as early as 2008): http://www.w3.org/TR/2008/WD-html5-20080610/dom.html#innerhtml It's still present in the latest revision at http://www.w3.org/TR/DOM-Parsing/. See also http://www.w3.org/TR/html5/references.html#refsDOMPARSING – Jay Dansand Jul 23 '14 at 13:31
  • 1
    Not a really good solution. I just used this but if i search for example 'person' it also replaces all classes and html elements with 'person' in it. And lowercase and uppercase is also not integrated. var rgxp = new RegExp("(\\b" + word + "\\b)", "gim"); fixed that but still, I think the code should not replace html elements. – Richard Lindhout Jun 01 '16 at 13:33
42

Why using a selfmade highlighting function is a bad idea

The reason why it's probably a bad idea to start building your own highlighting function from scratch is because you will certainly run into issues that others have already solved. Challenges:

  • You would need to remove text nodes with HTML elements to highlight your matches without destroying DOM events and triggering DOM regeneration over and over again (which would be the case with e.g. innerHTML)
  • If you want to remove highlighted elements you would have to remove HTML elements with their content and also have to combine the splitted text-nodes for further searches. This is necessary because every highlighter plugin searches inside text nodes for matches and if your keywords will be splitted into several text nodes they will not being found.
  • You would also need to build tests to make sure your plugin works in situations which you have not thought about. And I'm talking about cross-browser tests!

Sounds complicated? If you want some features like ignoring some elements from highlighting, diacritics mapping, synonyms mapping, search inside iframes, separated word search, etc. this becomes more and more complicated.

Use an existing plugin

When using an existing, well implemented plugin, you don't have to worry about above named things. The article 10 jQuery text highlighter plugins on Sitepoint compares popular highlighter plugins. This includes plugins of answers from this question.

Have a look at mark.js

mark.js is such a plugin that is written in pure JavaScript, but is also available as jQuery plugin. It was developed to offer more opportunities than the other plugins with options to:

  • search for keywords separately instead of the complete term
  • map diacritics (For example if "justo" should also match "justò")
  • ignore matches inside custom elements
  • use custom highlighting element
  • use custom highlighting class
  • map custom synonyms
  • search also inside iframes
  • receive not found terms

DEMO

Alternatively you can see this fiddle.

Usage example:

// Highlight "keyword" in the specified context
$(".context").mark("keyword");

// Highlight the custom regular expression in the specified context
$(".context").markRegExp(/Lorem/gmi);

It's free and developed open-source on GitHub (project reference).

dude
  • 5,678
  • 11
  • 54
  • 81
  • but in my case, I found this plugin `mark.js` is somehow slow, sometimes it take a 300ms delay and I dunno why. – Mizok.H Jul 18 '22 at 13:44
13

Here's a variation that ignores and preserves case:

jQuery.fn.highlight = function (str, className) {
    var regex = new RegExp("\\b"+str+"\\b", "gi");

    return this.each(function () {
        this.innerHTML = this.innerHTML.replace(regex, function(matched) {return "<span class=\"" + className + "\">" + matched + "</span>";});
    });
};
Community
  • 1
  • 1
bjarlestam
  • 147
  • 1
  • 2
  • 6
    This works for plain text, but it doesn't seem to exclude tags and attributes. i.e. Search for "lass" when you have a class attribute on a div in your innerHTML. – Jonathan Mar 02 '12 at 11:26
  • How is this function invoked? – jiy Feb 27 '15 at 17:24
  • `innerHTML` is evil, see my answer here. Also, `\\b` does not work for unicode characters. Furthermore this function misses almost anything, e.g. searching inside nested children. – dude May 20 '16 at 10:08
3

JSFiddle

Uses .each(), .replace(), .html(). Tested with jQuery 1.11 and 3.2.

In the above example, reads the 'keyword' to be highlighted and appends span tag with the 'highlight' class. The text 'keyword' is highlighted for all selected classes in the .each().

HTML

<body>
   <label name="lblKeyword" id="lblKeyword" class="highlight">keyword</label>
   <p class="filename">keyword</p>
   <p class="content">keyword</p>
   <p class="system"><i>keyword</i></p>
</body>

JS

$(document).ready(function() {
   var keyWord = $("#lblKeyword").text(); 
   var replaceD = "<span class='highlight'>" + keyWord + "</span>";
   $(".system, .filename, .content").each(function() {
      var text = $(this).text();
      text = text.replace(keyWord, replaceD);
      $(this).html(text);
   });
});

CSS

.highlight {
    background-color: yellow;
}
arhak
  • 2,488
  • 1
  • 24
  • 38
Van Peer
  • 2,127
  • 2
  • 25
  • 35
2

You can use my highlight plugin jQuiteLight, that can also work with regular expressions.

To install using npm type:

npm install jquitelight --save

To install using bower type:

bower install jquitelight 

Usage:

// for strings
$(".element").mark("query here");
// for RegExp
$(".element").mark(new RegExp(/query h[a-z]+/));

More advanced usage here

iamawebgeek
  • 2,713
  • 1
  • 18
  • 34
  • @user3631654 no that is a different plugin. My plugin can work with RegExp and has a feature of smart highlight. If you have included plugin you have mentioned before this plugin, you can get it using `var oldMark = $.fn.mark.noConflict()` – iamawebgeek Mar 27 '16 at 02:52
  • Seems like [jquery.mark](https://github.com/julmot/jquery.mark) has a method `markRegExp()` to also highlight custom regular expressions. So this shouldn't be an argument. – user3631654 Mar 31 '16 at 13:52
  • And @zazu, what do you mean with "smart highlight"? – user3631654 Mar 31 '16 at 13:53
  • @user3631654 if you turn on smart highlight and pass a word "consequnce" it would also highlight word "consequences" and its other forms, but if you pass "the" or "bla" it would not take "theme" or "black" – iamawebgeek Mar 31 '16 at 14:00
2

You can use the following function to highlight any word in your text.

function color_word(text_id, word, color) {
    words = $('#' + text_id).text().split(' ');
    words = words.map(function(item) { return item == word ? "<span style='color: " + color + "'>" + word + '</span>' : item });
    new_words = words.join(' ');
    $('#' + text_id).html(new_words);
    }

Simply target the element that contains the text, choosing the word to colorize and the color of choice.

Here is an example:

<div id='my_words'>
This is some text to show that it is possible to color a specific word inside a body of text. The idea is to convert the text into an array using the split function, then iterate over each word until the word of interest is identified. Once found, the word of interest can be colored by replacing that element with a span around the word. Finally, replacing the text with jQuery's html() function will produce the desired result.
</div>

Usage,

color_word('my_words', 'possible', 'hotpink')

enter image description here

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Cybernetic
  • 12,628
  • 16
  • 93
  • 132
1

You need to get the content of the p tag and replace all the dolors in it with the highlighted version.

You don't even need to have jQuery for this. :-)

Sebastian Hoitz
  • 9,343
  • 13
  • 61
  • 77
1

I wrote a very simple function that uses jQuery to iterate the elements wrapping each keyword with a .highlight class.

function highlight_words(word, element) {
    if(word) {
        var textNodes;
        word = word.replace(/\W/g, '');
        var str = word.split(" ");
        $(str).each(function() {
            var term = this;
            var textNodes = $(element).contents().filter(function() { return this.nodeType === 3 });
            textNodes.each(function() {
              var content = $(this).text();
              var regex = new RegExp(term, "gi");
              content = content.replace(regex, '<span class="highlight">' + term + '</span>');
              $(this).replaceWith(content);
            });
        });
    }
}

More info:

http://www.hawkee.com/snippet/9854/

Hawkee
  • 2,027
  • 23
  • 20
0

This is a modified version from @bjarlestam.

This will only search text.

jQuery.fn.highlight = function(str) {
  var regex = new RegExp(str, "gi");
  return this.each(function() {
    this.innerHTML = this.innerText.replace(regex, function(matched) {
      return "<span class='mark'>" + matched + "</span>";
    });
  });
};

// Mark
jQuery('table tr td').highlight('desh')
.mark {
  background: #fde293;
  color: #222;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<h2>HTML Table</h2>

<table>
  <tr>
    <th>Company</th>
    <th>Contact</th>
    <th>Country</th>
  </tr>
  <tr>
    <td>Sodeshi</td>
    <td>Francisco Chang</td>
    <td>Mexico</td>
  </tr>
  <tr>
    <td>Ernst Handel</td>
    <td>Roland Mendel</td>
    <td>Austria</td>
  </tr>
  <tr>
    <td>Island Trading</td>
    <td>Helen Bennett</td>
    <td>Bangladesh</td>
  </tr>

</table>

Usages: jQuery('.selector').highlight('sample text')

-1

I have created a repository on similar concept that changes the colors of the texts whose colors are recognised by html5 (we don't have to use actual #rrggbb values and could just use the names as html5 standardised about 140 of them)

colors.js colors.js

$( document ).ready(function() {
 
 function hiliter(word, element) {
  var rgxp = new RegExp("\\b" + word + "\\b" , 'gi'); // g modifier for global and i for case insensitive 
  var repl = '<span class="myClass">' + word + '</span>';
  element.innerHTML = element.innerHTML.replace(rgxp, repl);
   
   };

 hiliter('dolor', document.getElementById('dolor'));
});
.myClass{

background-color:red;
}
<!DOCTYPE html>
<html>
 <head>
  <title>highlight</title>
  
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
 
   <link href="main.css" type="text/css"  rel="stylesheet"/>
   
 </head>
 <body id='dolor'>
<p >
    Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
</p>
<p>
    Quisque bibendum sem ut lacus. Integer dolor ullamcorper libero.
    Aliquam rhoncus eros at augue. Suspendisse vitae mauris.
</p>
 <script type="text/javascript" src="main.js" charset="utf-8"></script>
 </body>
</html>
abe312
  • 2,547
  • 25
  • 16
-2
$(function () {
    $("#txtSearch").keyup(function (event) {
        var txt = $("#txtSearch").val()
        if (txt.length > 3) {
            $("span.hilightable").each(function (i, v) {
                v.innerHTML = v.innerText.replace(txt, "<hilight>" + txt + "</hilight>");
            });

        }
    });
});

Jfiddle here

L.Grillo
  • 960
  • 3
  • 12
  • 26
-2

Is it possible to get this above example:

jQuery.fn.highlight = function (str, className)
{
    var regex = new RegExp(str, "g");

    return this.each(function ()
    {
        this.innerHTML = this.innerHTML.replace(
            regex,
            "<span class=\"" + className + "\">" + str + "</span>"
        );
    });
};

not to replace text inside html-tags like , this otherwise breakes the page.

nickf
  • 537,072
  • 198
  • 649
  • 721