JavaScript keyword highlighter

Search term highlighting intended for every use case. Can be used with plain JavaScript or as jQuery plugin.

1. Getting to Know mark.js

mark.js is a text highlighter written in JavaScript. It can be used to dynamically mark search terms or custom regular expressions and offers you built-in options like diacritics support, separate word search, custom synonyms, iframes support, custom filters, accuracy definition, custom element, custom class name and more.

2. Examples

3. Getting Started

3.1 Download

You can either download the package using npm by running:

$ npm install mark.js --save-dev

Or using Bower:

$ bower install mark.js --save-dev

Or download the package manually.

3.2 CDN

If you prefer using a CDN instead of downloading, mark.js is available on:

3.3 Integration

mark.js ships with a few files – when using Bower they're located in ./dist/:

  • Files for usage with pure JavaScript:
    • mark.js - Uncompressed ES5
    • mark.min.js - Compressed ES5 (recommended)
    • mark.es6.js - Uncompressed ES6
    • mark.es6.min.js - Compressed ES6
  • Files for usage as jQuery plugin:
    • jquery.mark.js - Uncompressed ES5
    • jquery.mark.min.js - Compressed ES5 (recommended)
    • jquery.mark.es6.js - Uncompressed ES6
    • jquery.mark.es6.min.js - Compressed ES6

If you don't know what ES5 or ES6 (ECMAScript) is, then simply go ahead with a ES5 file as it might be in the JavaScript syntax you are using in your project.

After you have chosen the appropriate file, you need to embed it, e.g. as follows:

<script src="vendor/mark.js/dist/mark.min.js"></script>

Alternatively you can load it with AMD (RequireJS) or CommonJS.

Note: mark.js requires UTF-8 encoding. If your server or environment serves files with ASCII, you can workaround this behavior by using the charset attribute on above named <script> tag, e.g.:

<script src="vendor/mark.js/dist/mark.min.js" charset="UTF-8"></script>

3.4 Styling

mark.js will wrap matches with a specified element and optionally with an assigned class. When not changing the default element mark, browsers will ensure that it looks highlighted by default. However, you may want to customize the style of it. This can be done using e.g. the following CSS:

mark{
    background: orange;
    color: black;
}

If you've customized the default element or class, make sure to modify the selector.

4. API

4.1 General

JavaScript

Each API method below can be called on an instance object. To initialize a new instance you have to use:

var instance = new Mark(context);

The variable context defines where you want mark.js to search for matches. You can pass a single element (e.g. the return value of document.getElementById(...) or document.querySelector(...)), an array containing multiple single elements, or a NodeList (e.g. document.querySelectorAll(...)). Alternatively you can pass a string selector.

If for example you'd like to highlight matches in a div with a class context then you'd have to use:

var instance = new Mark(document.querySelector("div.context"));
// or
var instance = new Mark("div.context");

jQuery

Each API method below can be called on every jQuery element, e.g. $("div.test").

4.2 mark()

A method to highlight custom search terms.

Syntax

JavaScript:

var context = document.querySelector(".context");
var instance = new Mark(context);
instance.mark(keyword [, options]);

jQuery:

$(".context").mark(keyword [, options]);

Note: Even if this is a chaining method and therefore allows you to call further methods on the returning object, it's recommended to always use the done callback as mark.js works asynchronous.

Parameters

keyword

Type: string or array of string

The keyword to be marked. Can also be an array with multiple keywords. Note that keywords will be escaped. If you need to mark unescaped keywords (e.g. containing a pattern), have a look at the markRegExp() method below.

options

Type: object

Optional options:

OptionTypeDefaultDescription
elementstring"mark"HTML element to wrap matches, e.g. span
classNamestring""A class name that will be appended to element
excludearray[ ]An array with exclusion selectors. Matches inside these elements will be ignored. Example: "filter": ["h1", ".ignore"]
separateWordSearchbooleantrueWhether to search for each word separated by a blank instead of the complete term
accuracystring or object"partially"Either one of the following string values:
  • "partially": When searching for "lor" only "lor" inside "lorem" will be marked
  • "complementary": When searching for "lor" the whole word "lorem" will be marked
  • "exactly": When searching for "lor" only those exact words with a word boundary will be marked. In this example nothing inside "lorem". This value is equivalent to the previous option wordBoundary
Or an object containing two properties:
  • "value": One of the above named string values
  • "limiters": A custom array of string limiters for accuracy "exactly" or "complementary". Read more about this in the tutorials section
diacriticsbooleantrueIf diacritic characters should be matched. For example "piękny" would also match "piekny" and "doner" would also match "döner"
synonymsobject{ }An object with synonyms. The key will be a synonym for the value and the value for the key. Example: "synonyms": {"one": "1"} will add the synonym "1" for "one" and vice versa
iframesbooleanfalseWhether to search also inside iframes. If you don't have permissions to some iframes (e.g. because they have a different origin) they will be silently skipped. If you don't want to search inside specific iframes (e.g. facebook share), you can pass an exclude selector that matches these iframes
iframesTimeoutnumber5000The maximum ms to wait for a load event before skipping an iframe. Especially important when there's no internet connection or a browser "offline" mode is enabled and an iframe has an online src – then the load event is never fired
acrossElementsbooleanfalseWhether to search for matches across elements
caseSensitivebooleanfalseWhether to search case sensitive
ignoreJoinersbooleanfalseWhether to also find matches that contain soft hyphen, zero width space, zero width non-joiner and zero width joiner. They're used to indicate a point for a line break where there isn't enough space to show the full word
ignorePunctuationarray[ ]An array of punctuation mark strings. These punctuation marks can be between any characters, e.g. setting this option to ["'"] would match "Worlds", "World's" and "Wo'rlds". One or more apostrophes between the letters would still produce a match (e.g. "W'o''r'l'd's"). A typical setting for this option could be as follows: ":;.,-–—‒_(){}[]!'\"+=".split("")
wildcardsstring"disabled"Set to any of the following string values:
  • "disabled": Disable wildcard usage
  • "enabled": When searching for "lor?m", the "?" will match zero or one non-space character (e.g. "lorm", "loram", "lor3m", etc). When searching for "lor*m", the "*" will match zero or more non-space characters (e.g. "lorm", "loram", "lor123m", etc).
  • "withSpaces": When searching for "lor?m", the "?" will match zero or one space or non-space character (e.g. "lor m", "loram", etc). When searching for "lor*m", the "*" will match zero or more space or non-space characters (e.g. "lorm", "lore et dolor ipsum", "lor: m", etc).
eachfunctionA callback for each marked element. Receives the marked DOM element as a parameter
filterfunctionA callback to filter or limit matches. It will be called for each match and receives the following parameters:
  1. The text node which includes the match
  2. The term that has been found
  3. A counter indicating the total number of all marks at the time of the function call
  4. A counter indicating the number of marks for the term
The function must return false if the mark should be stopped, otherwise true
noMatchfunctionA callback function that will be called when there are no matches. Receives the not found term as a parameter
donefunctionA callback function after all marks are done. Receives the total number of marks as a parameter
debugbooleanfalseSet this option to true if you want to log messages
logobjectconsoleLog messages to a specific object (only if debug is true)

Examples

JavaScript:
var context = document.querySelector(".context"); // requires an element with class "context" to exist
var instance = new Mark(context);
instance.mark("test"); // will mark the keyword "test"
jQuery:
$(".context").mark("test"); // will mark the keyword "test", requires an element with class "context" to exist

For both, JavaScript and jQuery:
var options = {
    "element": "mark",
    "className": "",
    "exclude": [],
    "separateWordSearch": true,
    "accuracy": "partially",
    "diacritics": true,
    "synonyms": {},
    "iframes": false,
    "iframesTimeout": 5000,
    "acrossElements": false,
    "caseSensitive": false,
    "ignoreJoiners": false,
    "ignorePunctuation": [],
    "wildcards": "disabled",
    "each": function(node){
        // node is the marked DOM element
    },
    "filter": function(textNode, foundTerm, totalCounter, counter){
        // textNode is the text node which contains the found term
        // foundTerm is the found search term
        // totalCounter is a counter indicating the total number of all marks
        //              at the time of the function call
        // counter is a counter indicating the number of marks for the found term
        return true; // must return either true or false
    },
    "noMatch": function(term){
        // term is the not found term
    },
    "done": function(counter){
        // counter is a counter indicating the total number of all marks
    },
    "debug": false,
    "log": window.console
};
JavaScript:
var context = document.querySelector(".context"); // requires an element with class "context" to exist
var instance = new Mark(context);
instance.mark("test", options); // will mark the keyword "test"
jQuery:
$(".context").mark("test", options); // will mark the keyword "test", requires an element with class "context" to exist

4.3 markRegExp()

A method to highlight custom regular expressions.

Syntax

JavaScript:

var context = document.querySelector(".context");
var instance = new Mark(context);
instance.markRegExp(regexp [, options]);

jQuery:

$(".context").markRegExp(regexp [, options]);

Note: Even if this is a chaining method and therefore allows you to call further methods on the returning object, it's recommended to always use the done callback as mark.js works asynchronous.

Parameters

regexp

Type: RegExp

The regular expression to be marked. Example: /Lor[^]?m/gmi. Note that groups will be ignored and mark.js will always find all matches, regardless of the g flag.

options

Type: object

Optional options:

OptionTypeDefaultDescription
elementstring"mark"HTML element to wrap matches, e.g. span
classNamestring""A class name that will be appended to element
excludearray[ ]An array with exclusion selectors. Matches inside these elements will be ignored. Example: "filter": ["h1", ".ignore"]
iframesbooleanfalseWhether to search also inside iframes. If you don't have permissions to some iframes (e.g. because they have a different origin) they will be silently skipped. If you don't want to search inside specific iframes (e.g. facebook share), you can pass an exclude selector that matches these iframes
iframesTimeoutnumber5000The maximum ms to wait for a load event before skipping an iframe. Especially important when there's no internet connection or a browser "offline" mode is enabled and an iframe has an online src – then the load event is never fired
acrossElementsbooleanfalseWhether to search for matches across elements
ignoreGroupsnumber0Indicates the number of matching groups to ignore in the replacement. Can be used e.g. to implement non-capturing lookbehind groups. Note that when the value is > 0 (when groups should be ignored), it expects a total amount of groups in the RegExp of ignoreGroups + 1
eachfunctionA callback for each marked element. Receives the marked DOM element as a parameter
filterfunctionA callback to filter or limit matches. It will be called for each match and receives the following parameters:
  1. The text node which includes the match
  2. The matching string that has been found
  3. A counter indicating the number of all marks
The function must return false if the mark should be stopped, otherwise true
noMatchfunctionA callback function that will be called when there are no matches. Receives the not found term as a parameter
donefunctionA callback function after all marks are done. Receives the total number of marks as a parameter
debugbooleanfalseSet this option to true if you want to log messages
logobjectconsoleLog messages to a specific object (only if debug is true)

Examples

JavaScript:
var context = document.querySelector(".context"); // requires an element with class "context" to exist
var instance = new Mark(context);
instance.markRegExp(/lorem/gmi);
jQuery:
$(".context").markRegExp(/lorem/gmi); // requires an element with class "context" to exist

For both, JavaScript and jQuery:
var options = {
    "element": "mark",
    "className": "",
    "exclude": [],
    "iframes": false,
    "iframesTimeout": 5000,
    "acrossElements": false,
    "ignoreGroups": 0,
    "each": function(node){
        // node is the marked DOM element
    },
    "filter": function(textNode, foundTerm, totalCounter){
        // textNode is the text node which contains the found term
        // foundTerm is the found search term
        // totalCounter is a counter indicating the total number of all marks
        //              at the time of the function call
        return true; // must return either true or false
    },
    "noMatch": function(term){
        // term is the not found term
    },
    "done": function(counter){
        // counter is a counter indicating the total number of all marks
    },
    "debug": false,
    "log": window.console
};
JavaScript:
var context = document.querySelector(".context"); // requires an element with class "context" to exist
var instance = new Mark(context);
instance.markRegExp(/test/gmi, options);
jQuery:
$(".context").markRegExp(/test/gmi, options); // requires an element with class "context" to exist

4.4 markRanges()

A method to mark ranges with a start position and length. They will be applied to text nodes in the specified context.

Syntax

JavaScript:

var context = document.querySelector(".context");
var instance = new Mark(context);
instance.markRanges(ranges [, options]);

jQuery:

$(".context").markRanges(ranges [, options]);

Note: Even if this is a chaining method and therefore allows you to call further methods on the returning object, it's recommended to always use the done callback as mark.js works asynchronous.

Parameters

ranges

Type: array

An array of objects with a start and length property. Note that the start positions must be specified including whitespace characters.

options

Type: object

Optional options:

OptionTypeDefaultDescription
elementstring"mark"HTML element to wrap matches, e.g. span
classNamestring""A class name that will be appended to element
excludearray[ ]An array with exclusion selectors. Matches inside these elements will be ignored. Example: "filter": ["h1", ".ignore"]
iframesbooleanfalseWhether to search also inside iframes. If you don't have permissions to some iframes (e.g. because they have a [different origin][SOP]) they will be silently skipped. If you don't want to search inside specific iframes (e.g. facebook share), you can pass an exclude selector that matches these iframes
iframesTimeoutnumber5000The maximum ms to wait for a load event before skipping an iframe. Especially important when there's no internet connection or a browser "offline" mode is enabled and an iframe has an online src – then the load event is never fired
eachfunctionA callback for each marked element. Receives the marked DOM element and the corresponding range as a parameter
filterfunctionA callback to filter or limit matches. It will be called for each match and receives the following parameters:
  1. The text node which includes the range
  2. The current range
  3. The extracted term from the matching range
  4. A counter indicating the total number of all marks at the time of the function call
The function must return false if the mark should be stopped, otherwise true
noMatchfunctionA callback function that will be called when there are no matches. Receives the not found range as a parameter
donefunctionA callback function after all marks are done. Receives the total number of marks as a parameter
debugbooleanfalseSet this option to true if you want to log messages
logobjectconsoleLog messages to a specific object (only if debug is true)

Examples

JavaScript:
var context = document.querySelector(".context"); // requires an element with class "context" to exist
var instance = new Mark(context);
instance.markRanges([{
    start: 15,
    length: 5
}, {
    start: 25:
    length: 8
}]); // marks matches with ranges 15-20 and 25-33
jQuery:
$(".context").markRanges([{
    start: 15,
    length: 5
}, {
    start: 25:
    length: 8
}]); // marks matches with ranges 15-20 and 25-33

For both, JavaScript and jQuery:
var options = {
    "element": "mark",
    "className": "",
    "exclude": [],
    "iframes": false,
    "iframesTimeout": 5000,
    "each": function(node, range){
        // node is the marked DOM element
        // range is the corresponding range
    },
    "filter": function(textNode, range, term, counter){
        // textNode is the text node which contains the found term
        // range is the found range
        // term is the extracted term from the matching range
        // counter is a counter indicating the number of marks for the found term
        return true; // must return either true or false
    },
    "noMatch": function(range){
        // the not found range
    },
    "done": function(counter){
        // counter is a counter indicating the total number of all marks
    },
    "debug": false,
    "log": window.console
};
JavaScript:
var context = document.querySelector(".context"); // requires an element with class "context" to exist
var instance = new Mark(context);
instance.markRanges([{
    start: 15,
    length: 5
}, {
    start: 25:
    length: 8
}], options);
jQuery:
$(".context").markRanges([{
    start: 15,
    length: 5
}, {
    start: 25:
    length: 8
}], options);

4.5 unmark()

A method to remove highlights created by mark.js.

Syntax

JavaScript:

var context = document.querySelector(".context");
var instance = new Mark(context);
instance.unmark(options);

jQuery:

$(".context").unmark(options);

Note: Even if this is a chaining method and therefore allows you to call further methods on the returning object, it's recommended to always use the done callback as mark.js works asynchronous.

Parameters

options

Type: object

Optional options:

OptionTypeDefaultDescription
elementstring""Will remove only marked elements with this specific element
classNamestring""Will remove only marked elements with this specific class name
excludearray[ ]An array with exclusion selectors. These elements will be ignored. Example: "filter": ["h1", ".ignore"]
iframesbooleanfalseWhether to search also inside iframes. If you don't have permissions to some iframes (e.g. because they have a different origin) they will be silently skipped. If you don't want to search inside specific iframes (e.g. facebook share), you can pass an exclude selector that matches these iframes
iframesTimeoutnumber5000The maximum ms to wait for a load event before skipping an iframe. Especially important when there's no internet connection or a browser "offline" mode is enabled and an iframe has an online src – then the load event is never fired
donefunctionA callback function after all marked elements were removed
debugbooleanfalseSet this option to true if you want to log messages
logobjectconsoleLog messages to a specific object (only if debug is true)

JavaScript:
var context = document.querySelector(".context"); // requires an element with class "context" to exist
var instance = new Mark(context);
instance.unmark();
jQuery:
$(".context").unmark(); // requires an element with class "context" to exist

For both, JavaScript and jQuery:
var options = {
    "element": "",
    "className": "",
    "exclude": [],
    "iframes": false,
    "iframesTimeout": 5000,
    "done": function(){},
    "debug": false,
    "log": window.console
};
JavaScript:
var context = document.querySelector(".context"); // requires an element with class "context" to exist
var instance = new Mark(context);
instance.unmark(options);
jQuery:
$(".context").unmark(options); // requires an element with class "context" to exist

5. Compatibility

mark.js works in all modern browsers.
It is intensively tested in:

  • Firefox (30+)
  • Chrome (30+)
  • Safari (7+)
  • Edge (13+)
  • IE (9+)

Firefox Chrome Safari Edge IE9-11

6. Tutorials

Below is an explanation of options that might not be self explanatory.

6.1 Element and Class Name

You may ask yourself how mark.js works generally or how to wrap matches with a custom element and class name. Take this simple DOM fragment as an example:

<div class="context">
    <p>My text content</p>
</div>

When you're calling mark.js on div.context – whether by mark() or by markRegExp() – it will find matches and wraps them with a defined element (tag name) and optionally with a class assigned to that element. So assuming we'd like to highlight "text" with a span tag and a class highlight assigned, then this could be done e.g. as follows:

With JavaScript:

var instance = new Mark(document.querySelector("div.context"));
instance.mark("text", {
    "element": "span",
    "className": "highlight"
});

With jQuery:

$("div.context").mark("text", {
    "element": "span",
    "className": "highlight"
});

The generated DOM fragment would then look like:

<div class="context">
    <p>My <span class="highlight">text</span> content</p>
</div>

6.2 Exclude

When having a context that contains multiple elements and you need to ignore matches in some of them, the option exclude may be worth gold. You can pass an array of exclusion selectors and matches within these selectors will be ignored from highlighting.

Assuming you have the following DOM fragment:

<div class="context">
    <article>
        <header>
            <h1>Article Text Headline</h1>
            <div class="byline">
                <address class="author">
                    By <a rel="author" href="demo/link">John Doe</a>
                </address>
                on
                <time pubdate datetime="2016-30-05" title="May 30th, 2016">5/30/16</time>
            </div>
        </header>
        <div class="article-content">
            Article text content
        </div>
    </article>
</div>

And you want to highlight all "text" matches, but only in the actual article content. Then you can either initialize mark.js directly on the div containing the article's content (in this case div.article-content), or specify a exclude array matching the elements that should be ignored, e.g. as follows:

With JavaScript:

var instance = new Mark(document.querySelector("div.context"));
instance.mark("text", {
    "exclude": [
        "h1",
        ".byline *"
    ]
});

With jQuery:

$("div.context").mark("text", {
    "exclude": [
        "h1",
        ".byline *"
    ]
});

This would ignore matches inside <h1> elements and those having a class byline assigned.

6.3 Separate Word Search

Constraint: Only available in the mark() method

Regardless of whether you are highlighting an array of search terms or just a single one, it makes sense to search for each word separately. Assuming you have a search term "lorem ipsum dolor", then this option will make mark.js search for "lorem", "ipsum" and "dolor" separately.

6.4 Accuracy

Constraint: Only available in the mark() method

As the name suggests, the accuracy option allows you to specify the accuracy of mark.js.

To demonstrate this, we're taking the following DOM fragment as a basis for all examples below:

<div class="context">
    <p>highlight high</p>
</div>

By default, the provided search term will be highlighted, regardless if it's a substring of a word or a own standing one. This option value is called partially. When searching for "high" inside above named DOM fragment, the following will be generated:

<div class="context">
    <p><mark>high</mark>light <mark>high</mark></p>
</div>

However, there might be cases where you want to ignore partial matches and only highlight matches for entire words with a word boundary (see what is a word boundary). Then the option value exactly is what you're looking for. When searching for "high", only whole words "high" will be highlighted:

<div class="context">
    <p>highlight <mark>high</mark></p>
</div>

Last but not least, the option value complementary will complement matches. When searching for "high" inside above named DOM fragment, the following will be generated:

<div class="context">
    <p><mark>highlight</mark> <mark>high</mark></p>
</div>

The option value complementary highlights the search term and all surrounding characters until a blank or the start/end of the search context occurs. The option value exactly highlights only whole words that match the specified search term separated by a blank or the start/end of the search context.

But there might be cases where you might want to use accuracy exactly, but highlight also words that are followed by punctuation marks e.g. a , or a .. Or when using accuracy complementary you might not want to highlight a , or a . following a word. In these cases you can pass an array of custom limiters, e.g. as follows:

var options = {
    "accuracy": {
        "value": "exactly",
        "limiters": [",", "."]
    }
};

This will highlight words that are preceded and followed by a blank, the end/start of the search context (default), . or , (custom).

6.5 Diacritics

The option diacritics will map normal and diacritic characters. Assuming you would like to highlight either "piekny" or "piękny" in the following DOM fragment:

<div class="context">
    <p>My piekny or piękny text</p>
</div>

Then both "piękny" and "piekny" will be highlighted.

6.6 Synonyms

There might be cases where highlighting synonyms for words is helpful for users. Take "one" and "1" as an example. When a user searches for "one last todo", he might expect to highlight also "1 last todo". In German languages, you could also map umlauts, e.g. "ü" with "ue" or "ö" with "oe".

The following example will add the synonym "1" for "one", "2" for "two" and vice versa:

var options = {
    "synonyms": {
        "one": "1",
        "two": "2"
    }
};

6.7 Filter

The callback option filter can be used to filter highlights yourself. For example you could use it to limit highlights for specific words or generally to a specific amount. Or you could check if the match is inside a complex HTML construct which can not be expressed in a exclusion selector. Just be creative!

Here is an example to limit matches for a word "the" to a maximum of 10:

var options = {
    "filter": function(node, term, totalCounter, counter){
        if(term === "the" && counter >= 10){
            return false;
        } else {
            return true;
        }
    }
};

6.8 Across Elements

In some cases you need to highlight matches even if they occur in multiple elements. Let's say the following is the context where you'd like to highlight the search term "Highlight Text":

<div class="context">
    <b>Highlight</b>
    Text
</div>

Then "Highlight" and "Text" are only highlighted when you haven't disabled separateWordSearch, as this will cause a separated search for words.
If you have disabled separateWordSearch nothing will be highlighted. This appears due to the fact that the two words included in the search term aren't located within the same text node. As you can see above "Highlight" is wrapped in a separate <b> element.
To make sure matches will be found across multiple elements – in this example across <b> – you'll have to enable the acrossElements option with the value true.

This option acrossElements will find matches even if the matching search term is located across multiple HTML elements or even iframes (if enabled).

7. Contribute

If you are having any questions, would like to submit a feature request or found a bug, please don't hesitate to open an issue on GitHub!

Before submitting pull requests or creating issues, please read the guidelines for contributing.