Can someone help me with a javascript function that can highlight text on a web page. And the requirement is to - highlight only once, not like highlight all occurrences of the text as we do in case of search.
-
6If you post the code of the function we would be able help. If you ask us to create such a function for you... that's less likely. You have to do something on your own. Start doing something and come back when you get stuck. – Felix Kling Dec 27 '11 at 12:08
-
7YEs I have read How to Ask & I have done something on my own but I got stuck and that's why I asked. I work on Android and have little knowledge of javasript that is why I am not able to do it on my own. Earlier I was using a different javascript which did the job but not without certain limitations. I might not have used the right words while asking this question and I am sorry for that but please do not think of otherwise. – Ankit Dec 27 '11 at 12:40
-
1This 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 10:43
-
1Checkout following regex way... https://stackoverflow.com/a/45519242/2792959 – Aug 05 '17 at 07:16
-
I prepared an article on that here, https://exhesham.com/2017/11/20/text-highlight-manipulation-with-angular2/ – Hesham Yassin Dec 04 '17 at 09:06
20 Answers
You can use the jquery highlight effect.
But if you are interested in raw javascript code, take a look at what I got Simply copy paste into an HTML, open the file and click "highlight" - this should highlight the word "fox". Performance wise I think this would do for small text and a single repetition (like you specified)
function highlight(text) {
var inputText = document.getElementById("inputText");
var innerHTML = inputText.innerHTML;
var index = innerHTML.indexOf(text);
if (index >= 0) {
innerHTML = innerHTML.substring(0,index) + "<span class='highlight'>" + innerHTML.substring(index,index+text.length) + "</span>" + innerHTML.substring(index + text.length);
inputText.innerHTML = innerHTML;
}
}
.highlight {
background-color: yellow;
}
<button onclick="highlight('fox')">Highlight</button>
<div id="inputText">
The fox went over the fence
</div>
Edits:
Using replace
I see this answer gained some popularity, I thought I might add on it. You can also easily use replace
"the fox jumped over the fence".replace(/fox/,"<span>fox</span>");
Or for multiple occurrences (not relevant for the question, but was asked in comments) you simply add global
on the replace regular expression.
"the fox jumped over the other fox".replace(/fox/g,"<span>fox</span>");
Replacing the HTML to the entire web-page
to replace the HTML for an entire web-page, you should refer to innerHTML
of the document's body.
document.body.innerHTML

- 20,030
- 7
- 43
- 238

- 27,391
- 16
- 83
- 122
-
Thanks a lot for your reply but can you also tell me how to specify the color in javascript itself – Ankit Dec 27 '11 at 12:31
-
1You can replace the `""` with `""`, color should be something like `var color = "#ff0000";` – Yaniro Dec 27 '11 at 12:35
-
what if i want to highlight all occurrences of a word on the whole page ?@guy mograbi – Baqer Naqvi Feb 07 '14 at 12:58
-
8Using a simple "replace" is a **bad idea**. I've described why here: http://stackoverflow.com/a/32758672/3894981 – dude Sep 24 '15 at 10:43
-
May some JS expert have a look at this [link] (http://stackoverflow.com/questions/40697627/how-to-highlight-a-substring-containing-a-random-character-between-two-known-cha) and suggest how to proceed further? – RanonKahn Nov 22 '16 at 22:07
-
Hey, can anyone please look at question https://stackoverflow.com/questions/49128427/highlight-a-text-from-an-html-file that would be really helpful – ganesh kaspate Mar 07 '18 at 10:34
-
9This isn't a great idea because this will attempt to highlight HTML tags/attributes/etc. For example, what would happen in the case of: `
` You would get invalid HTML that would look like: `
` Not good – dcporter7 Jun 06 '19 at 22:40
The solutions offered here are quite bad.
- You can't use regex, because that way, you search/highlight in the html tags.
- You can't use regex, because it doesn't work properly with UTF* (anything with non-latin/English characters).
- You can't just do an innerHTML.replace, because this doesn't work when the characters have a special HTML notation, e.g.
&
for &,<
for <,>
for >,ä
for ä,ö
for öü
for üß
for ß, etc.
What you need to do:
Loop through the HTML document, find all text nodes, get the textContent
, get the position of the highlight-text with indexOf
(with an optional toLowerCase
if it should be case-insensitive), append everything before indexof
as textNode
, append the matched Text with a highlight span, and repeat for the rest of the textnode (the highlight string might occur multiple times in the textContent
string).
Here is the code for this:
var InstantSearch = {
"highlight": function (container, highlightText)
{
var internalHighlighter = function (options)
{
var id = {
container: "container",
tokens: "tokens",
all: "all",
token: "token",
className: "className",
sensitiveSearch: "sensitiveSearch"
},
tokens = options[id.tokens],
allClassName = options[id.all][id.className],
allSensitiveSearch = options[id.all][id.sensitiveSearch];
function checkAndReplace(node, tokenArr, classNameAll, sensitiveSearchAll)
{
var nodeVal = node.nodeValue, parentNode = node.parentNode,
i, j, curToken, myToken, myClassName, mySensitiveSearch,
finalClassName, finalSensitiveSearch,
foundIndex, begin, matched, end,
textNode, span, isFirst;
for (i = 0, j = tokenArr.length; i < j; i++)
{
curToken = tokenArr[i];
myToken = curToken[id.token];
myClassName = curToken[id.className];
mySensitiveSearch = curToken[id.sensitiveSearch];
finalClassName = (classNameAll ? myClassName + " " + classNameAll : myClassName);
finalSensitiveSearch = (typeof sensitiveSearchAll !== "undefined" ? sensitiveSearchAll : mySensitiveSearch);
isFirst = true;
while (true)
{
if (finalSensitiveSearch)
foundIndex = nodeVal.indexOf(myToken);
else
foundIndex = nodeVal.toLowerCase().indexOf(myToken.toLowerCase());
if (foundIndex < 0)
{
if (isFirst)
break;
if (nodeVal)
{
textNode = document.createTextNode(nodeVal);
parentNode.insertBefore(textNode, node);
} // End if (nodeVal)
parentNode.removeChild(node);
break;
} // End if (foundIndex < 0)
isFirst = false;
begin = nodeVal.substring(0, foundIndex);
matched = nodeVal.substring(foundIndex, foundIndex + myToken.length);
if (begin)
{
textNode = document.createTextNode(begin);
parentNode.insertBefore(textNode, node);
} // End if (begin)
span = document.createElement("span");
span.className += finalClassName;
span.appendChild(document.createTextNode(matched));
parentNode.insertBefore(span, node);
nodeVal = nodeVal.substring(foundIndex + myToken.length);
} // Whend
} // Next i
}; // End Function checkAndReplace
function iterator(p)
{
if (p === null) return;
var children = Array.prototype.slice.call(p.childNodes), i, cur;
if (children.length)
{
for (i = 0; i < children.length; i++)
{
cur = children[i];
if (cur.nodeType === 3)
{
checkAndReplace(cur, tokens, allClassName, allSensitiveSearch);
}
else if (cur.nodeType === 1)
{
iterator(cur);
}
}
}
}; // End Function iterator
iterator(options[id.container]);
} // End Function highlighter
;
internalHighlighter(
{
container: container
, all:
{
className: "highlighter"
}
, tokens: [
{
token: highlightText
, className: "highlight"
, sensitiveSearch: false
}
]
}
); // End Call internalHighlighter
} // End Function highlight
};
Then you can use it like this:
function TestTextHighlighting(highlightText)
{
var container = document.getElementById("testDocument");
InstantSearch.highlight(container, highlightText);
}
Here's an example HTML document
<!DOCTYPE html>
<html>
<head>
<title>Example of Text Highlight</title>
<style type="text/css" media="screen">
.highlight{ background: #D3E18A;}
.light{ background-color: yellow;}
</style>
</head>
<body>
<div id="testDocument">
This is a test
<span> This is another test</span>
äöüÄÖÜäöüÄÖÜ
<span>Test123äöüÄÖÜ</span>
</div>
</body>
</html>
By the way, if you search in a database with LIKE
,
e.g. WHERE textField LIKE CONCAT('%', @query, '%')
[which you shouldn't do, you should use fulltext-search or Lucene], then you can escape every character with \ and add an SQL-escape-statement, that way you'll find special characters that are LIKE-expressions.
e.g.
WHERE textField LIKE CONCAT('%', @query, '%') ESCAPE '\'
and the value of @query is not '%completed%'
but '%\c\o\m\p\l\e\t\e\d%'
(tested, works with SQL-Server and PostgreSQL, and every other RDBMS system that supports ESCAPE)
A revised typescript-version:
namespace SearchTools
{
export interface IToken
{
token: string;
className: string;
sensitiveSearch: boolean;
}
export class InstantSearch
{
protected m_container: Node;
protected m_defaultClassName: string;
protected m_defaultCaseSensitivity: boolean;
protected m_highlightTokens: IToken[];
constructor(container: Node, tokens: IToken[], defaultClassName?: string, defaultCaseSensitivity?: boolean)
{
this.iterator = this.iterator.bind(this);
this.checkAndReplace = this.checkAndReplace.bind(this);
this.highlight = this.highlight.bind(this);
this.highlightNode = this.highlightNode.bind(this);
this.m_container = container;
this.m_defaultClassName = defaultClassName || "highlight";
this.m_defaultCaseSensitivity = defaultCaseSensitivity || false;
this.m_highlightTokens = tokens || [{
token: "test",
className: this.m_defaultClassName,
sensitiveSearch: this.m_defaultCaseSensitivity
}];
}
protected checkAndReplace(node: Node)
{
let nodeVal: string = node.nodeValue;
let parentNode: Node = node.parentNode;
let textNode: Text = null;
for (let i = 0, j = this.m_highlightTokens.length; i < j; i++)
{
let curToken: IToken = this.m_highlightTokens[i];
let textToHighlight: string = curToken.token;
let highlightClassName: string = curToken.className || this.m_defaultClassName;
let caseSensitive: boolean = curToken.sensitiveSearch || this.m_defaultCaseSensitivity;
let isFirst: boolean = true;
while (true)
{
let foundIndex: number = caseSensitive ?
nodeVal.indexOf(textToHighlight)
: nodeVal.toLowerCase().indexOf(textToHighlight.toLowerCase());
if (foundIndex < 0)
{
if (isFirst)
break;
if (nodeVal)
{
textNode = document.createTextNode(nodeVal);
parentNode.insertBefore(textNode, node);
} // End if (nodeVal)
parentNode.removeChild(node);
break;
} // End if (foundIndex < 0)
isFirst = false;
let begin: string = nodeVal.substring(0, foundIndex);
let matched: string = nodeVal.substr(foundIndex, textToHighlight.length);
if (begin)
{
textNode = document.createTextNode(begin);
parentNode.insertBefore(textNode, node);
} // End if (begin)
let span: HTMLSpanElement = document.createElement("span");
if (!span.classList.contains(highlightClassName))
span.classList.add(highlightClassName);
span.appendChild(document.createTextNode(matched));
parentNode.insertBefore(span, node);
nodeVal = nodeVal.substring(foundIndex + textToHighlight.length);
} // Whend
} // Next i
} // End Sub checkAndReplace
protected iterator(p: Node)
{
if (p == null)
return;
let children: Node[] = Array.prototype.slice.call(p.childNodes);
if (children.length)
{
for (let i = 0; i < children.length; i++)
{
let cur: Node = children[i];
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
if (cur.nodeType === Node.TEXT_NODE)
{
this.checkAndReplace(cur);
}
else if (cur.nodeType === Node.ELEMENT_NODE)
{
this.iterator(cur);
}
} // Next i
} // End if (children.length)
} // End Sub iterator
public highlightNode(n:Node)
{
this.iterator(n);
} // End Sub highlight
public highlight()
{
this.iterator(this.m_container);
} // End Sub highlight
} // End Class InstantSearch
} // End Namespace SearchTools
Usage:
let searchText = document.getElementById("txtSearchText");
let searchContainer = document.body; // document.getElementById("someTable");
let highlighter = new SearchTools.InstantSearch(searchContainer, [
{
token: "this is the text to highlight" // searchText.value,
className: "highlight", // this is the individual highlight class
sensitiveSearch: false
}
]);
// highlighter.highlight(); // this would highlight in the entire table
// foreach tr - for each td2
highlighter.highlightNode(td2); // this highlights in the second column of table

- 441
- 6
- 12

- 78,642
- 66
- 377
- 442
-
Great answer.. The method looks like overkill, but concise! Will definitely be interested in doing a speed test with that method as in my case the results are lazy loaded into the DOM (as there **CAN** be thousands of results), curious if this method would add a high latency to the lazy load. – Pogrindis May 21 '15 at 16:52
-
13Sorry, but none of your arguments are true. 1. You absolutely can use a RegExp, you just should not search inside the HTML value but the text value of an element. 2. You can absolutely use RegExp with diacritic characters, as implemented in [mark.js](https://markjs.io/). 3. HTML notations will be converted to the actual characters in the browser DOM, so you also absolutely use them! – dude May 24 '16 at 05:30
-
1@julmot; To 1: Which means you need to iterate through every element, which is precisely what I do. Unless you don't care about loosing formatting, in which case you can search in document.body.innerText, which will be quite slow. 3. Not in the DOM, but in the innerText or the textContent property of a text-element. Which again means you need to iterate through the text elements; can't be done with regEx AFAIK. 2: Don't know mark.js, but I would avoid everything that does a jQuery.each, because that is damn slow. – Stefan Steiger Jun 02 '16 at 16:57
-
1@StefanSteiger 1. Then you should correct your decision relationale, as it says that we can't search with a RegExp at all, which isn't true 2. It doesn't use jQuery.each. What makes you think that? 3. This isn't true, at least in Firefox. `ä` e.g. will be converted to the actual character, even when using `innerHTML`. – dude Jul 12 '16 at 14:10
-
@dude: Hmm, var de = document.createElement("span"); de.innerHTML = "äö"; console.log(de.innerHTML); indeed outputs äö in Chrome. That's funny, but just plain wrong IMHO. Unless the HTML/ECMA specs actually say so, in which case the specs are "funny". Well, JavaScript... whoever expects that a=5 would output 5 for "property a" doesn't know JS, yet ... – Stefan Steiger Jul 14 '16 at 21:13
-
-
@dude: Dude, just realized, that's only true for non-XML escape characters, like ä & '. So if you use <> &, it will fail... Which again means - no regex (unless you want to to replace these characters, and risk it fails on some special character). – Stefan Steiger Dec 11 '17 at 14:15
-
2Hi @StefanSteiger Actually, I am using your solutions. This one is perfect . But there is some problem like,If I I have a P In which there are two spans and one span is has data like Diploma MSBTE and second span has data 2012 . Now If string which I want to highlight is Diploma MSBTE 2012 , this whole string then I checked this does not work, If everything which is going to matched is present in one span then it works, but if the text content is in diff tags then It does not work. Can you please tell something about this ? – ganesh kaspate Mar 07 '18 at 06:54
-
It will be really helpful If you see the question https://stackoverflow.com/questions/49128427/highlight-a-text-from-an-html-file – ganesh kaspate Mar 07 '18 at 06:56
-
@StefanSteiger after applying this function, the surrounding span has TWO classes. Why? Did you test how this code works yourself? Also the class name is hardcoded inside function...shouldn't this be at least an option in the arguments? Thanks – Andrew May 26 '18 at 10:04
-
@Andrew: There are 2 classnames. One for all matches, and a separate one for each highlight-token individually. The one for all matches is hardcoded, yes - but you got the code - you can change that. – Stefan Steiger Oct 03 '18 at 07:40
-
I dont understand what this code is doing, specially myClassName and myClassNameAll. Why is it that complicated? I expect to be able to pass a classname for the highlight in the funciton. But cant see any of that. – Basil Musa Dec 13 '20 at 21:42
-
As of year 2020, I'd always prefer a well maintained open source software from a platform like GitHub instead of taking over this amount of foreign code into my own codebase. I become directly responsible for code that I've copied over, but I'll never know of security patches or compatibility fixes in the case of copy/paste. – observer Dec 20 '20 at 21:50
-
@observer: Yeah - but how many dependencies does it take to add that feature? 100? 300? Fortunately, NPM packages can now be sorted by maliciousness - descending only, from wilfully to ignorantly. – Stefan Steiger Dec 21 '20 at 09:35
-
I've only checked the mark.js. It does not come with any transitive dependencies if you pick the vanilla version. In general, you are right of course: OSS does NOT liberate you from checking the library for its sanity – much to the contrary. – observer Dec 21 '20 at 22:43
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.
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
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).
Disclosure: I am the original author of this library.
-
10Highlighting text alone isn't a good enough reason for me to include jQuery. – Roy Feb 09 '16 at 16:05
-
16@Roy I've taken this to heart. Good news, as of v6.0.0 [mark.js](https://markjs.io/) waived the jQuery dependency and makes it now optionally to use it as jQuery plugin. – dude May 20 '16 at 10:19
-
All true, except: 1st point is not possible, because you cannot get registered event handlers, and even if you could, you couldn't set anonymous functions... 2nd: mark.js does not find text between two tags either, e.g. sed won't find sed... 3rd: whenever a browser (including new version) comes along that you haven't tested it yet, it might break. That is always true, no matter how many tests you write. At 17kb, marks is too big for what it does. – Stefan Steiger Mar 07 '18 at 10:39
-
What points are you referring to @StefanSteiger? Can't say something to the first point without that information. However, the second comment is wrong, mark.js can find matches between tags, using the `acrossElements` option. And to the third comment; mark.js is not big compared to the functionalities it offers. And no, it's unlikely that something breaks in future, since mark.js was tested e.g. starting Chrome 30 and in all newer versions with cross-browser unit tests and there were never any issues with upcoming versions. – dude Mar 07 '18 at 12:33
-
@dude: The three points after the first paragraph. Ah, ok, missing that option in the demo I looked at. In that case, it might make some sense. But still, I find it to be too large. – Stefan Steiger Mar 07 '18 at 13:59
-
Version 8 of `mark.js` is `5.6kB`, although not tree-shakeable, it's still much lower than it used to be. – adi518 Mar 13 '21 at 16:29
-
8
-
3You should disclose in the answer, that you are the author of this library explicitly. – TheMaster Feb 26 '22 at 09:49
function stylizeHighlightedString() {
var text = window.getSelection();
// For diagnostics
var start = text.anchorOffset;
var end = text.focusOffset - text.anchorOffset;
range = window.getSelection().getRangeAt(0);
var selectionContents = range.extractContents();
var span = document.createElement("span");
span.appendChild(selectionContents);
span.style.backgroundColor = "yellow";
span.style.color = "black";
range.insertNode(span);
}

- 3,730
- 1
- 23
- 27

- 248
- 3
- 12
-
3
-
shouldnt' there be a way to select text without creating another node? – Dave Gregory Nov 07 '12 at 00:32
-
@user191433 the question is not just about selecting text, but also applying styles. For that you need a node. – Christophe Feb 01 '13 at 02:31
-
Reminder/tip that the JavaScript `span.style.backgroundColor = "yellow";` translates to CSS `style="background-color: yellow;"` --that subtle difference between the camelCase and dashed-notation tripped me up at first. – MarkHu Sep 18 '13 at 20:17
-
1P.S. Mohit's answer at http://stackoverflow.com/questions/7991474/calculate-position-of-selected-text-javascript-jquery/9988468#9988468 is a more-streamlined variant of this code. (for instance omitting the start and end variables which are solely diagnostic/non-functional here.) – MarkHu Sep 18 '13 at 20:42
None of the other solutions really fit my needs, and although Stefan Steiger's solution worked as I expected I found it a bit too verbose.
Following is my attempt:
/**
* Highlight keywords inside a DOM element
* @param {string} elem Element to search for keywords in
* @param {string[]} keywords Keywords to highlight
* @param {boolean} caseSensitive Differenciate between capital and lowercase letters
* @param {string} cls Class to apply to the highlighted keyword
*/
function highlight(elem, keywords, caseSensitive = false, cls = 'highlight') {
const flags = caseSensitive ? 'gi' : 'g';
// Sort longer matches first to avoid
// highlighting keywords within keywords.
keywords.sort((a, b) => b.length - a.length);
Array.from(elem.childNodes).forEach(child => {
const keywordRegex = RegExp(keywords.join('|'), flags);
if (child.nodeType !== 3) { // not a text node
highlight(child, keywords, caseSensitive, cls);
} else if (keywordRegex.test(child.textContent)) {
const frag = document.createDocumentFragment();
let lastIdx = 0;
child.textContent.replace(keywordRegex, (match, idx) => {
const part = document.createTextNode(child.textContent.slice(lastIdx, idx));
const highlighted = document.createElement('span');
highlighted.textContent = match;
highlighted.classList.add(cls);
frag.appendChild(part);
frag.appendChild(highlighted);
lastIdx = idx + match.length;
});
const end = document.createTextNode(child.textContent.slice(lastIdx));
frag.appendChild(end);
child.parentNode.replaceChild(frag, child);
}
});
}
// Highlight all keywords found in the page
highlight(document.body, ['lorem', 'amet', 'autem']);
.highlight {
background: lightpink;
}
<p>Hello world lorem ipsum dolor sit amet, consectetur adipisicing elit. Est vel accusantium totam, ipsum delectus et dignissimos mollitia!</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Numquam, corporis.
<small>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium autem voluptas perferendis dolores ducimus velit error voluptatem, qui rerum modi?</small>
</p>
I would also recommend using something like escape-string-regexp if your keywords can have special characters that would need to be escaped in regexes:
const keywordRegex = RegExp(keywords.map(escapeRegexp).join('|')), flags);

- 92,861
- 21
- 134
- 171
Here's my regexp pure JavaScript solution:
function highlight(text) {
document.body.innerHTML = document.body.innerHTML.replace(
new RegExp(text + '(?!([^<]+)?<)', 'gi'),
'<b style="background-color:#ff0;font-size:100%">$&</b>'
);
}

- 9,261
- 4
- 31
- 28
-
This works perfectly for me when the block of text I am trying to highlight contains HTML tags. – John Chapman Jan 20 '15 at 18:45
-
You can also tweak the function to accept multiple words via the regexp pipe symbol, e.g. `one|two|three` – Klemen Tusar Jan 22 '15 at 09:06
-
It will not replace the text if the end of the text has a `>` character. Modify the regex using `(?!([^<]+)?<)` for it to work. – Archie Reyes Aug 19 '16 at 05:49
-
-
Since HTML5 you can use the <mark></mark>
tags to highlight text. You can use javascript to wrap some text/keyword between these tags. Here is a little example of how to mark and unmark text.

- 6,950
- 5
- 32
- 51
-
1
-
2This also doesn't work properly because, for example, if you enter into the JSFIDDLE "Lorem", it only marks the first instance of it. – agm1984 Sep 06 '17 at 01:45
-
1Wel you just need to replace all occurrences of the keyword. here is an example with regex globally http://jsfiddle.net/de5q704L/73/ – kasper Taeymans Sep 12 '17 at 16:11
-
This is not a decent solution, mark is basically just a div with a yellow background... – vdegenne Jul 24 '23 at 08:59
If you also want it to be highlighted on page load, there is a new way.
just add #:~:text=Highlight%20These
try accessing this link in a new tab
https://stackoverflow.com/questions/38588721#:~:text=Highlight%20a%20text
which highlights the "Highlight a text" text.
Also, currently only supported on Chrome (Thanks GitGitBoom).
-
2For anyone looking for more information: this is called Scroll to Text Fragment and is currently only supported on Chrome. https://chromestatus.com/feature/4733392803332096 – GitGitBoom Jan 12 '21 at 18:02
-
https://stackoverflow.com/questions/62161819/what-exactly-is-the-text-location-hash-in-an-url – GitGitBoom Jan 12 '21 at 18:05
-
1) doesn't work in all browsers. 2) only works on page's first load. 3) only highlights the first match. 4) no way to edit the css styles. good finding though. – vdegenne Jul 24 '23 at 09:06
I have the same problem, a bunch of text comes in through a xmlhttp request. This text is html formatted. I need to highlight every occurrence.
str='<img src="brown fox.jpg" title="The brown fox" />'
+'<p>some text containing fox.</p>'
The problem is that I don't need to highlight text in tags. For example I need to highlight fox:
Now I can replace it with:
var word="fox";
word="(\\b"+
word.replace(/([{}()[\]\\.?*+^$|=!:~-])/g, "\\$1")
+ "\\b)";
var r = new RegExp(word,"igm");
str.replace(r,"<span class='hl'>$1</span>")
To answer your question: you can leave out the g in regexp options and only first occurrence will be replaced but this is still the one in the img src property and destroys the image tag:
<img src="brown <span class='hl'>fox</span>.jpg" title="The brown <span
class='hl'>fox</span> />
This is the way I solved it but was wondering if there is a better way, something I've missed in regular expressions:
str='<img src="brown fox.jpg" title="The brown fox" />'
+'<p>some text containing fox.</p>'
var word="fox";
word="(\\b"+
word.replace(/([{}()[\]\\.?*+^$|=!:~-])/g, "\\$1")
+ "\\b)";
var r = new RegExp(word,"igm");
str.replace(/(>[^<]+<)/igm,function(a){
return a.replace(r,"<span class='hl'>$1</span>");
});

- 37,593
- 24
- 91
- 160
-
This was the only regex solution that worked for me without messing with `
` or ``. – yvesmancera Oct 22 '15 at 17:50
-
1Golden rule: Never. Use. Regular. Expressions. To. Mess. About. With. XML. – ScottMcGready Jul 27 '17 at 21:47
Fast forward to 2019, Web API now has native support for highlighting texts:
const selection = document.getSelection();
selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
And you are good to go! anchorNode
is the selection starting node, focusNode
is the selection ending node. And, if they are text nodes, offset
is the index of the starting and ending character in the respective nodes. Here is the documentation
They even have a live demo

- 3,959
- 8
- 39
- 60
-
oh this is brilliant. simply use it like this: selection.setBaseAndExtent(desiredNode, 0, desiredNode, 1); to highlight the only node you need. and it works with Gutenberg – tonyAndr Apr 16 '20 at 12:51
-
Simple TypeScript example
NOTE: While I agree with @Stefan in many things, I only needed a simple match highlighting:
module myApp.Search {
'use strict';
export class Utils {
private static regexFlags = 'gi';
private static wrapper = 'mark';
private static wrap(match: string): string {
return '<' + Utils.wrapper + '>' + match + '</' + Utils.wrapper + '>';
}
static highlightSearchTerm(term: string, searchResult: string): string {
let regex = new RegExp(term, Utils.regexFlags);
return searchResult.replace(regex, match => Utils.wrap(match));
}
}
}
And then constructing the actual result:
module myApp.Search {
'use strict';
export class SearchResult {
id: string;
title: string;
constructor(result, term?: string) {
this.id = result.id;
this.title = term ? Utils.highlightSearchTerm(term, result.title) : result.title;
}
}
}

- 1,044
- 8
- 6
I think this code is better because highlight all repeated character
function highlight(text) {
var inputText = document.getElementById("inputText");
var innerHTML = inputText.innerHTML;
var index = innerHTML.indexOf(text);
if (index >= 0) {
inputText.innerHTML=innerHTML.split(text).join('<span class="highlight">'+text+'</span>');
}
}
.highlight {
background-color: yellow;
}
<button onclick="highlight('fox')">Highlight</button>
<div id="inputText">
The fox went over the fence fox fox fox wen fox
</div>

- 211
- 2
- 8
Simply pass your word into the following function:
function highlight_words(word) {
const page = document.body.innerHTML;
document.body.innerHTML = page.replace(new RegExp(word, "gi"), (match) => `<mark>${match}</mark>`);
}
Usage:
highlight_words("hello")
This will highlight all instances of the word on the page.

- 12,628
- 16
- 93
- 132
I was wondering that too, you could try what I learned on this post.
I used:
function highlightSelection() {
var userSelection = window.getSelection();
for(var i = 0; i < userSelection.rangeCount; i++) {
highlightRange(userSelection.getRangeAt(i));
}
}
function highlightRange(range) {
var newNode = document.createElement("span");
newNode.setAttribute(
"style",
"background-color: yellow; display: inline;"
);
range.surroundContents(newNode);
}
<html>
<body contextmenu="mymenu">
<menu type="context" id="mymenu">
<menuitem label="Highlight Yellow" onclick="highlightSelection()" icon="/images/comment_icon.gif"></menuitem>
</menu>
<p>this is text, select and right click to high light me! if you can`t see the option, please use this<button onclick="highlightSelection()">button </button><p>
you could also try it here: http://henriquedonati.com/projects/Extension/extension.html
xc

- 1
- 1

- 317
- 2
- 7
I would like to share more about the usage of the scroll text fragment
syntax: #:~:text=[prefix-,]textStart[,textEnd][,-suffix]
Example | Demo link |
---|---|
#:~:text=to |
How to highlight text using javascript |
#:~:text=to,text |
How to highlight text using javascript |
#:~:text=tex-,t |
How to highlight text using javascript |
#:~:text=text-,using,-javascript |
How to highlight text using javascript |
If you want to highlight multiple text fragments in one URL (&text=
)
Example | Demo link |
---|---|
#:~:text=javascript&text=highlight&text=Ankit |
How to highlight text using javascript |

- 6,105
- 2
- 37
- 45
If you surround any text inside of the mark tag, it will automatically get highlighted by the browser in this striking yellow color. Details are available here: https://dev.to/comscience/highlight-searched-text-on-a-page-with-just-javascript-17b3
<h1>
Searching and Marking
</h1>
<input type="text" id="search"/>
<button onClick="search(id)" id="button">
Highlight
</button>
<p id="text">
What exactly is this Worker thread module, and why do we need it? In this post, we will talk about the historical reasons concurrency is implemented in JavaScript and Node.js, the problems we might find, current solutions, and the future of parallel processing with Worker threads.
Living in a single-threaded world
JavaScript was conceived as a single-threaded programming language that ran in a browser. Being single-threaded means that only one set of instructions is executed at any time in the same process (the browser, in this case, or just the current tab in modern browsers).
This made things easier for implementation and for developers using the language. JavaScript was initially a language only useful for adding some interaction to webpages, form validations, and so on — nothing that required the complexity of multithreading.
</p>
Now JS code will look like this
function search(e) {
let searched = document.getElementById("search").value.trim();
if (searched !== "") {
let text = document.getElementById("text").innerHTML;
let re = new RegExp(searched,"g"); // search for all instances
let newText = text.replace(re, `<mark>${searched}</mark>`);
document.getElementById("text").innerHTML = newText;
}
}

- 1,064
- 16
- 13
On cybernetic: Thanks, the function below works. But there's a problem because it replaces also the words inside tags. Below is an example if the word to highlight is target:
<a <mark>target</mark>="_blank" href="Its not OK to highlight <mark>target</mark> here">Its OK to highlight the words <mark>target</mark>s here</a>
How do we prevent this?
function highlight_words(word) {
const page = document.body.innerHTML;
document.body.innerHTML = page.replace(new RegExp(word, "gi"), (match) => `<mark>${match}</mark>`);
}

- 19
- 2
I implemented something similar recently. My goal was to implement text highlight upon search. Here is how I did it -
Created a regex with the received input.
const regex = new RegExp(`(${value})`, 'gi');
Traversed through the element tree of the concerning element. And then searched for the received input in the text nodes (instead of the innerHTML to avoid matches in the attributes eg. class, href. etc.). When a match was received. It (text node) was swapped with a wrapper span containing the required span. For eg.
text ---> <wrapper-span><span> text </span></wrapper-span>
So, I was able to get my text highlighted.
highlightStr(value, regex, document.getELementById('searchable-div'));
const highlightStr = (value, regex, node) => {
node.childNodes.forEach((childNode) => {
if (childNode.nodeValue && regex.test(childNode.nodeValue)) {
const highLightWrapper = document.createElement('span');
highLightWrapper.classList.add("highlight-wrapper-class");
childNode.replaceWith(highLightedWrapper);
highLightWrapper.innerHTML = childNode.nodeValue.replace(regex, `<span class="highlight">${value}</span>`);
}
this.highlightStr(value, regex, childNode);
});
}
But now the problem is if we do not remove (or check for existing) wrapper-span structure before trying to highlight a text again. We might end with an infinite wrapper span tree or a broken search. So I implemented a reset function for it. You might want to run it every time you would like to highlight the text. It basically just swaps back the text node with its text content. So we get exactly what we had before highlighting the text.
resetHighlight(document.getELementById('searchable-div'));
const resetHighlight = (node) => {
node.childNodes.forEach((childNode) => {
if ((childNode as HTMLElement).classList && (childNode as HTMLElement).classList.contains('highlight-wrapper-class') && childNode.textContent) {
childNode.replaceWith(childNode.textContent);
}
this.resetHighlight(childNode);
});
}

- 6,928
- 2
- 47
- 58

- 1
- 1
- 1
Here's something I got working with React.js
import React from 'react';
interface HighlightProps {
text: string;
searchTerm: string;
highlightStyle?: React.CSSProperties;
}
const defaultHighlightStyle: React.CSSProperties = {
backgroundColor: 'yellow',
};
const Highlight: React.FC<HighlightProps> = ({
text,
searchTerm,
highlightStyle = defaultHighlightStyle,
}) => {
if (!searchTerm) {
return <span>{text}</span>;
}
const regex = new RegExp(`(${searchTerm})`, 'gi');
const parts = text.split(regex);
return (
<span>
{parts.map((part, index) =>
part.toLowerCase() === searchTerm.toLowerCase() ? (
<span key={index} style={highlightStyle}>
{part}
</span>
) : (
<React.Fragment key={index}>{part}</React.Fragment>
),
)}
</span>
);
};
export default Highlight;
Call it like this:
const ExampleComponent = () => {
const someContent = 'the quick brown fox jumped over the lazy dog';
const textToHighlight = 'fox';
return (
<Highlight text={someContent} searchTerm={textToHighlight} />
);
}

- 628
- 7
- 14
Using the surroundContents() method on the Range type. Its only argument is an element which will wrap that Range.
function styleSelected() {
bg = document.createElement("span");
bg.style.backgroundColor = "yellow";
window.getSelection().getRangeAt(0).surroundContents(bg);
}

- 362
- 2
- 14