Highlight when search term is tokenized into individual words #821

Open
BrentWheeldon opened this Issue Apr 9, 2014 · 6 comments

4 participants

@BrentWheeldon

Hi there,

We parse our search query into individual terms, which means the builtin highlighting functionality doesn't highlight some results. For example, if we search for "John Boston", and a result of "John Smith Boston" does have any highlighting applied. It seems that the highlight function supports o.pattern being an array, but I can't work out how to split the query on space. Is there a way of customizing the pattern which is passed into the highlight function?

Thanks!

@BrentWheeldon

PS I'm happy to send a PR for this if I can get some guidance on where you'd like this customization to live.

@jharding

When I get a chance I'll take a look to see what would need to be done in order to support this. In the meantime, feel free to dive in on your own. This is definitely something I'd like to support.

@BrentWheeldon

Thanks, @jharding. Do you think this should be a default behavior, or something that one could configure?

@jharding

I'd lean towards default behavior.

@jharding jharding added this to the v1.0.0 milestone Aug 20, 2014
@devourment77

This would be a great feature. Example: "hotels with spa", when typing "hotels" highlighting works, but when we type "hotels spa", neither hotel or spa are highlighted (but the correct suggestion is shown).

Thanks,
Brandon

@jagltoroTech

I have modified the library and added this code for the highlight, so far it is working

var highlight = function(doc) {
        "use strict";
        var defaults = {
            node: null,
            pattern: null,
            tagName: "strong",
            className: null,
            wordsOnly: true,
            caseSensitive: false
        };
        return function hightlight(o) {
            var regex;
            o = _.mixin({}, defaults, o);
            if (!o.node || !o.pattern) {
                return;
            }
            var array = o.pattern;
            var array = array.split(' ');
            var array = array.filter(Boolean);
            var index, len;
            for (index = 0, len = array.length; index < len; ++index) {
                if(typeof(array[index]) !== 'undefined' || array[index] !== ''){
                    regex = getRegex(array[index], o.caseSensitive, o.wordsOnly);
                    traverse(o.node, hightlightTextNode);
                }
            }
            function hightlightTextNode(textNode) {
                var match, patternNode, wrapperNode;
                if (match = regex.exec(textNode.data)) {
                    wrapperNode = doc.createElement(o.tagName);
                    o.className && (wrapperNode.className = o.className);
                    patternNode = textNode.splitText(match.index);
                    patternNode.splitText(match[0].length);
                    wrapperNode.appendChild(patternNode.cloneNode(true));
                    textNode.parentNode.replaceChild(wrapperNode, patternNode);
                }
                return !!match;
            }
            function traverse(el, hightlightTextNode) {
                var childNode, TEXT_NODE_TYPE = 3;
                for (var i = 0; i < el.childNodes.length; i++) {
                    childNode = el.childNodes[i];
                    if (childNode.nodeType === TEXT_NODE_TYPE) {
                        i += hightlightTextNode(childNode) ? 1 : 0;
                    } else {
                        traverse(childNode, hightlightTextNode);
                    }
                }
            }
        };
        function getRegex(patterns, caseSensitive, wordsOnly) {
            var escapedPatterns = [], regexStr;
            for (var i = 0, len = patterns.length; i < len; i++) {
                escapedPatterns.push(_.escapeRegExChars(patterns[i]));
            }
            regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("") + ")\\b" : "(" + escapedPatterns.join("|") + ")";
            return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
        }
    }(window.document);

This part wich is the one that i added:

var array = o.pattern; 
var array = array.split(' '); //Split the coming words separated by space
var array = array.filter(Boolean); //filter so no blank space / undefined is stored
var index, len;
for (index = 0, len = array.length; index < len; ++index) {  
      if(typeof(array[index]) !== 'undefined' || array[index] !== ''){ //another filter for blank spaces
         regex = getRegex(array[index], o.caseSensitive, o.wordsOnly);  //normal functions of typeahead
         traverse(o.node, hightlightTextNode);
     }
}
@cyberj0g cyberj0g added a commit to cyberj0g/typeahead.js that referenced this issue Feb 17, 2016
@cyberj0g cyberj0g Highlighting of individual words in suggestions
Resolves issue #821, based on jagltoroTech comment.
9a33561
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment