27

I've been trying to make a simple search inside a static HTML page using JQuery. I have to mention that this is just my first time working with JQuery.

I'm trying to change the background of the found word in the page and this is what I've tried so far:

myJavascript.js:

$(document).ready(function(){

     $('#searchfor').keyup(function(){
         page = $('#all_text').text();
         searchedText = $('#searchfor').val();
         $("p:contains('"+searchedText+"')").css("color", "white");
    });
});

Here's the HTML code as well:

page.html:

<html>
<head>
    <title>Test page</title>
</head>
<body bgcolor="#55c066">
<input type="text" id="searchfor"></input>
    <p id="all_text">Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euism modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.
    <font color="red">Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tinci futurum.</font>
    </p>
</body>
    <script src="jquery-1.7.2.min.js"></script>
    <script src="myJavascript.js"></script>
</html>

After inspecting the page with Firebug I can see that the variables in JQuery do get the value from the input field but I guess I'm messing up the highlighting part.

Thanks in advance for your help!

Radu Gheorghiu
  • 20,049
  • 16
  • 72
  • 107
  • Do you need to underline _only_ the word or the whole paragraph(`p`)? – mshsayem Apr 04 '12 at 12:52
  • Only the word, and I changed the title to what I really wanted, highlighting of the found word. – Radu Gheorghiu Apr 04 '12 at 12:52
  • 1
    If you want to underline only the searched word, you'll need to wrap the word in an html tag such as `span` (set the style of the span to underline) and the replace the content of the `p` element – scibuff Apr 04 '12 at 12:53
  • Sorry for the later edits, I want to highlight the found word. That is what my code is trying to do. Underlining was the first thing I tried, then I changed my mind and tried to highlight the word but forgot to change the title. – Radu Gheorghiu Apr 04 '12 at 12:55
  • an idea: get the text of the paragraph (`$("p:contains('"+searchedText+"')").html());` now use regex and replace(wrap) those words with ``, and set them as `.html()` of the paragraph – mshsayem Apr 04 '12 at 12:57
  • Is there a reason you are duplicating what is basic browser functionality? (ie why would the user not just hit Ctrl-F and use the browser's text search box?) – Spacedman Apr 04 '12 at 12:58
  • Because it's a requirement in a project. – Radu Gheorghiu Apr 04 '12 at 12:59
  • Using `innerHTML` (VanillaJS) or `.html` (jQuery) is evil!!! It destroys all events inside the element and trigger generation of the DOM again and again. Have a look at my answer! – dude May 23 '16 at 19:59

7 Answers7

27

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).

Example of mark.js keyword highlighting with your code

$(function() {
  $("input").on("input.highlight", function() {
    // Determine specified search term
    var searchTerm = $(this).val();
    // Highlight search term inside a specific context
    $("#context").unmark().mark(searchTerm);
  }).trigger("input.highlight").focus();
});
mark {
  background: orange;
  color: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/mark.js/7.0.0/jquery.mark.min.js"></script>
<input type="text" value="test">
<div id="context">
  Lorem ipsum dolor test sit amet
</div>
dude
  • 5,678
  • 11
  • 54
  • 81
  • 2
    I agree with what you said, but since this question dated back to waay waay 2012, when not many JS libraries were available, this was the only way I could get things done. So, moving forward, you should definitely look for existing plugins. Otherwise, start making your own and share it! – Radu Gheorghiu Jan 06 '17 at 11:43
14

Do something like this

 $("p:contains('"+searchedText+"')").each( function( i, element ) {
      var content = $(element).text();
      content = content.replace( searchedText, '<span class="search-found">' + searchedText + '</span>' );
      element.html( content );
 });

 .search-found {
     text-decoration: underline;
 }

p.s. this will work only if each of the "elements" has plain text only content otherwise it would remove children nodes

EDIT: removed the extra ')' in the each callback

scibuff
  • 13,377
  • 2
  • 27
  • 30
  • I've tried your code but Firebug gives me an error: at line 6, column 66 but there's nothing there. I see there should be another parameter in `each()` function, but I can't seem to understand what it is. – Radu Gheorghiu Apr 04 '12 at 13:25
  • there's an extra `)` after `element` remove one of those, i.e .`function( i, element ){` but honestly you should be able to do this kinda minor debugging yourself - I've updated the code above – scibuff Apr 04 '12 at 13:28
  • Umh, `element.text()` is not a function. I can see that element is a `[object HTMLParagraphElement]` – Radu Gheorghiu Apr 04 '12 at 13:32
  • Ok, I got this error and another one after it, and learned something more in the process. Thanks! Like I said, this was my first time using JQuery, so I'm just taking my first steps. – Radu Gheorghiu Apr 04 '12 at 13:41
  • 1
    Using `innerHTML` (VanillaJS) or `.html` (jQuery) is evil!!! It destroys all events inside the element and trigger generation of the DOM again and again. Have a look at my answer! – dude May 23 '16 at 19:59
11

Here is mine: http://jsfiddle.net/x8rpY/1/

JS:

$('#searchfor').keyup(function(){
         var page = $('#all_text');
         var pageText = page.text().replace("<span>","").replace("</span>");
         var searchedText = $('#searchfor').val();
         var theRegEx = new RegExp("("+searchedText+")", "igm");    
         var newHtml = pageText.replace(theRegEx ,"<span>$1</span>");
         page.html(newHtml);
    });

CSS:

#all_text span
{
    text-decoration:underline;
    background-color:yellow;    
}

Works for repeated search also.

mshsayem
  • 17,557
  • 11
  • 61
  • 69
  • Thanks, but if I search for uppercase letters and finds those exact same letters as lowercase then it converts the lowercase ones to uppercase. – Radu Gheorghiu Apr 04 '12 at 14:40
  • @mshsayem **Awesome code**, thanks. Is it possible to move the document to where the word is present in Jquery?. Because I have a large webpage and I need to move the pointer to where the actual word is present like Google chrome (ctrl+f5) feature. – webblover Nov 06 '14 at 17:40
  • 1
    it is possible; just get the `span` element and call `.scrollIntoView()` – mshsayem Nov 09 '14 at 02:15
  • Using `innerHTML` (VanillaJS) or `.html` (jQuery) is evil!!! It destroys all events inside the element and trigger generation of the DOM again and again. Have a look at my answer! – dude May 23 '16 at 19:59
5

$(function() {
  $("input").on("input.highlight", function() {
    // Determine specified search term
    var searchTerm = $(this).val();
    // Highlight search term inside a specific context
    $("#context").unmark().mark(searchTerm);
  }).trigger("input.highlight").focus();
});
mark {
  background: orange;
  color: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/mark.js/7.0.0/jquery.mark.min.js"></script>
<input type="text" value="test">
<div id="context">
  Lorem ipsum dolor test sit amet
</div>
Rajan
  • 51
  • 1
  • 2
0

Pure JS as it might help others too who come in search.
Using a regex which matches the input text (case-insensitive - i , all occurences - g )

var searchQuery = document.querySelector("#inputSearch");
var context = document.querySelector("#context");

searchQuery.addEventListener('input', searchQueryResults)

function searchQueryResults() {
  context.innerHTML = context.textContent.replace(new RegExp(searchQuery.value, "gi"), (match) => `<u>${match}</u>`);
}
<input type="text" value="" id="inputSearch">
<div id="context">
  Lorem ipsum dolor test sit amet
</div>
Rana
  • 2,500
  • 2
  • 7
  • 28
-1

(for one thing you want to use Background-Color, not Color, for background)

I would create a css class for normal and a seperate (inherited) css class for highlighted text, and then use the JQuery to change the css class when you find what you are looking for.

Just my initial thoughts though, not sure if there is a better way of doing it.

EDIT: if you want to change only a specific word, you'll have to modify innerHTML to put it in a seperate tag at that point.

Oofpez
  • 514
  • 1
  • 7
  • 18
-1

Here is another example that I quickly hacked: http://jsfiddle.net/VCJUX/

alxbrd
  • 1,675
  • 1
  • 15
  • 16
  • This is actually pretty cool, the problem is; if you're searching a page with any styling at all, it breaks the entire page by removing all p tag, spans, links, ect... – Denny Smith Oct 30 '15 at 23:02