user2014429
user2014429

Reputation: 2577

highlighting all matching words in div from text input

I have made a function which highlights matching words in a div. but if there are two identical words separated by a different word then only the first word is highlight. so for example if the search criterion was the word "burn", and in the text was the sentence "burn baby burn", I would want it to highlight both "burn"'s. this jsFiddle demonstrates how it only highlights the first "burn". Here is the code below also.

if ($('#search').val().length !== 0) {
  $('.searchable').each(function() {
    $(this).html($(this).html().replace($('#search').val(), "<span class = 'highlight'>" + $('#search').val() + "</span>"));
  });
}
.highlight {
  font-weight: bold;
  color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<input id = "search" type ="text" value = "burn">
<div class="searchable">burn baby burn</div>

Upvotes: 4

Views: 21417

Answers (5)

Slava Knyazev
Slava Knyazev

Reputation: 6073

The existing answers focus on innerHTML manipulation. It's possible to do entirely with DOM APIs:

function highlight(term) {
    // Select all Elements. There is no method to query all Nodes directly.
    for (const element of document.querySelectorAll("*")) {
        
        // Skip element if it doesn't contain target string
        if (!element.textContent.includes(term)) continue;

        // Traverse each node by inspecting the childNodes of every element
        for (const child of element.childNodes) {
            // Skip node if its not a text node
            if (child.nodeType !== Node.TEXT_NODE) continue;
            // Skip node if it doesnt contain our target string
            if (!child.textContent.includes(term)) continue;

            // We want to break up the contents into chunks 
            // of text which don't need to be highlighted
            // You can get creative here if you want 
            // to use more term advanced matching
            const textNodes = child.data.split(term).map(substr => {
                return document.createTextNode(substr);
            });

            // We don't want to modify the overall structure of the DOM
            // a fragment lets us group multiple nodes together without
            // bundling it under another new node
            const fragment = document.createDocumentFragment();

            // Populate the new fragment
            for (const node of textNodes) {
                // Add every text node to the new node
                fragment.appendChild(node);
                // Interweave the term in between them within a span
                const el = document.createElement("span")
                el.textContent = term;
                // Add some styling
                el.style.backgroundColor = 'yellow';
              
                fragment.appendChild(el);
            }

            // Remove extra term that was
            // generated the interweaving
            fragment.removeChild(fragment.lastChild);

            // Swap current child node for new fragment
            element.replaceChild(fragment, child);
        }
    }
}

// highlight a word
highlight("JavaScript")

Upvotes: 0

Rosy Shrestha
Rosy Shrestha

Reputation: 1479

Use regular expression with :

  • global 'g' flag to replace all the matches
  • ignore case 'i' to match all cases

Do not forget to escape special characters

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

const SEARCH_VALUE = $("#search").val().trim();

if (SEARCH_VALUE.length > 0) {
  $('.searchable').each(function() {
    const DIV_TEXT_CONTENT = $('.searchable').text();
    const SPLIT_CONTENT = DIV_TEXT_CONTENT.split(" ");

    SPLIT_CONTENT.forEach((word, index) => {
      const SEARCH_REGEXP = new RegExp(escapeRegExp(SEARCH_VALUE), "gi")
      if (SEARCH_REGEXP.test(word))
        $(this).html($(this).html().replace(SEARCH_REGEXP, "<span class = 'highlight'>" + word + "               </span>"));
    });
  })
}

function escapeRegExp(text) {
  return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}
.highlight {
  font-weight: bold;
  color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="search" type="text" value="BuRn">
<div class="searchable">burn baby burn</div>

Upvotes: 0

user2792959
user2792959

Reputation:

  1. Take care of regex special characters.

  2. Maintain uppercase & lowercase.

So taking care of above things with case insensitive search (in most cases, this is desired; otherwise just remove "i"), here is the final code...

if ($('#search').val().length !== 0) {
    $('.searchable').each(function() {
        //Handle special characters used in regex
        var searchregexp = new RegExp($("#search").val().replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), "gi");

        //$& will maintain uppercase and lowercase characters.
        $(this).html($(this).html().replace(searchregexp, "<span class = 'highlight'>$&</span>"));
    });
}

And a fiddle.

Upvotes: 9

Malevolence
Malevolence

Reputation: 1857

Here's a working jsfiddle for you. Basically, get the text from the div, split it on space, loop through the words and see if one matches.

var term = $('#search').val().trim().toLowerCase();
if (term.length > 0) {
    var source = $('.searchable').text();
    var words = source.split(' ');
    var output = '';
    $.each(words, function(idx, word) {
        if (term === word.toLowerCase()) {
            output += '<span class="highlight">' + word + '</span> ';
        } else {
             output += word + ' ';   
        }
    });

    $('.searchable').html(output);
}

Upvotes: 1

Danny
Danny

Reputation: 3665

You can pass a regular expression into replace() instead of the string, with the g modifier to make replace perform a global match.

if($('#search').val().length !== 0){
   $('.searchable').each(function(){
   var search_value = $("#search").val();
   var search_regexp = new RegExp(search_value, "g");
   $(this).html($(this).html().replace(search_regexp,"<span class = 'highlight'>"+search_value+"</span>"));
 });
}

Upvotes: 12

Related Questions