-3

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?

Nope
  • 22,147
  • 7
  • 47
  • 72
Danmoreng
  • 2,367
  • 1
  • 19
  • 32
  • 1
    you said "Those libraries though always want to highlight content which is already present in the DOM" - maybe you could copy them out of the DOM, use the library, then put them back in? – theGleep May 17 '18 at 15:45
  • A current jsfiddle would help and show what is happening and then you can say what you want – Tarun Lalwani May 29 '18 at 11:55
  • can you just add the response inside a div and assign css to it ? – Steve Ruben Jun 04 '18 at 12:32

4 Answers4

1

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>
Shahar
  • 2,101
  • 18
  • 23
  • I do not mind the downvote, but I would really appreciate if you could tell me what did not work as asked or code that does not follow best practices. Thanks in advance. – Shahar Jun 01 '18 at 07:10
  • I do not either, I just think the OP uses DOM7, a snippet, and doesn´t like libraries. (including DOM7) – lucchi Jun 01 '18 at 09:19
  • 1
    I did not downvote any answers and have to check them when I work on Monday. – Danmoreng Jun 02 '18 at 23:09
1

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();
                }
            });
        }
    }
});
Jhon Salazar
  • 161
  • 1
  • 2
0

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.

lucchi
  • 1,006
  • 6
  • 9
  • This looks like the most promising approach. My current mistake was to use the highlight function several times on the string with the different keywords - while applying styling with `span` Elements in between. Instead of checking for keywords and applying the styling at a later stage for all at once. – Danmoreng Jun 04 '18 at 13:41
  • @Danmoreng I think you are on the right track. Take the full advantage of the tools you have. – lucchi Jun 04 '18 at 15:27
0

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;
        }