Highlight Ajax Response with Javascript

1.5k Views Asked by At

I'm trying to highlight a query inside a text coming from an ajax response, before constructing HTML with it and pasting that into the DOM. Right now I'm using this code snippet:

function highlightWords(line, word, htmltag) {
    var tag = htmltag || ["<b>", "</b>"];
    var regex = new RegExp('(' + preg_quote(word) + ')', 'gi');
    return line.replace(regex, tag[0] + "$1" + tag[1]);
}

function preg_quote(str) {
    return (str + '').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
}

However, this is not capeable of highlighting different words if the query is something like sit behind. It will only highlight the complete phrase and not the single words. It also doesn't care about HTML tags and that produces unpretty results if the query is span for example...

I've found various libraries which handle highlighting way better, like https://markjs.io/ or https://www.the-art-of-web.com/javascript/search-highlight/

Those libraries though always want to highlight content which is already present in the DOM.

My search gets an ajax response, which I then turn into HTML with JS and paste the complete HTMLString into a parent container using DOM7 (which is similar to jQuery). Therfor I would prefer to highlight the text before creating the HTMLString and pasting it in the DOM.

Any ideas?

4

There are 4 best solutions below

2
lucchi On BEST ANSWER

Snippet style: Warning: this uses DOM7 as per Question

Overview: Instead of appending the whole text as HTML string to your #container, Append the portions of normal text, as text, and the highlighted elements as elements, so you can style them at will.

var text // your ajax text response
var strQuery = 'sit behind' // your query string 

var queryWords = strQuery.split(' ')
var textWords = text.split(' ')
var bufferNormalWords  = []

textWords.forEach(function (word) {
    if (queryWords.indexOf(word) != -1) { // found
      var normalWords = bufferNormalWords.splice(0, buffer.length) // empty buffer
      // Your DOM7 commands
      $$('#container').add('span').text(normalWords.join(' ')) // normal text
      $$('#container').add('span').css('color', 'red').text(word + ' ') // why not red          
    }
    else bufferNormalWords.push(word)
})

Do not mess up with text becoming HTMLStrings, just set text, and create the necesary elements to style them as you want with your DOM7.

0
WolfgangDeveloper On

If your ajax response contains html, I don't think there's an easy way to get around creating DOM elements first. Below gets the job done, even in the case where span is in the query and the ajax results contain <span>

        function highlightWords(line, word, htmltag) {
            var words = word.split(/\s+/);
            var tag = htmltag || ["<b>", "</b>"];

            var root = document.createElement("div");
            root.innerHTML = line;
            root = _highlightWords(words, tag, root);
            return root.innerHTML;
        }

        // Recursively search the created DOM element
        function _highlightWords(words, htmlTag, el) {
            var children = [];

            el.childNodes.forEach(function(el) {
                if (el.nodeType != 3) { // anything other than Text Type
                    var highlighted = _highlightWords(words, htmlTag, el);
                    children.push(highlighted);
                } else {
                    var line = _highlight(el.textContent, words, htmlTag);
                    var span = document.createElement("span");
                    span.innerHTML = line;
                    children.push(span);
                }
            });

            // Clear the html of the element, so the new children can be added
            el.innerHTML = "";
            children.forEach(function (c) { el.appendChild(c)});
            return el;
        }

        // Find and highlight any of the words
        function _highlight(line, words, htmlTag) {
            words.forEach(function(singleWord) {
                if (!!singleWord) {
                    singleWord = htmlEscape(singleWord);
                    line = line.replace(singleWord, htmlTag[0] + singleWord + htmlTag[1]);
                }
            });
            return line;
        }
3
Shahar On

I think you were on the right track using a library for that. I have been using for that a great library named mark.js.

It works without dependencies or with jQuery. The way that you can make it work.

  1. Make the AJAX call.
  2. Load the string to the DOM.
  3. Call the Mark.js API on the content you have loaded.

Here's a code snippet:

document.addEventListener('DOMContentLoaded', getText);

function getText() {
  const headline = document.getElementsByTagName("h1")[0];
  const p = document.getElementsByTagName("p")[0];

  fetch('https://jsonplaceholder.typicode.com/posts/1').
  then(response => response.json()).
  then(json => {
    console.log(json);
    headline.innerHTML = json.title;
    p.innerHTML = json.body;
    addMark('aut facere');
  });
}

function addMark(keyword) {
  var markInstance = new Mark(document.querySelector('.context'));
  var options = {
    separateWordSearch: true
  };
  markInstance.unmark({
    done: function() {
      markInstance.mark(keyword, options);
    },
  });
}
<script src="https://cdn.jsdelivr.net/mark.js/8.6.0/mark.min.js"></script>
<div class="context">
  <h1></h1>
  <p></p>
</div>

0
Jhon Salazar On

I just make the highlight in the response of ajax request. It's works for me:

$.ajax({
    url : url,
    type : 'POST',
    success: function(response) {
        // Highlight
        let term = 'word';
        $context = $("#selector");
        $context.show().unmark();
        if (term){
            $context.mark(term, {
                done: function() {
                    $context.not(":has(mark)").hide();
                }
            });
        }
    }
});