Select2 searches inside the html of the formatted options

Sorin Postelnicu picture Sorin Postelnicu · Sep 13, 2013 · Viewed 7.9k times · Source

I am using the select2 widget, and I need to display the search results formatted as html.

So I am using it like this:

  function formatMyItem(myItem) {
     return defaultEscapeMarkup(myItem.someDescription) + " <strong>(" + myItem.someOtherValue + ")</strong>";
  }

  function defaultEscapeMarkup(markup) {
     var replace_map = {
        '\\': '&#92;',
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#39;',
        "/": '&#47;'
     };

     return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
        return replace_map[match];
     });
  }

  var suggestionValues = [];
  for (var i = 0; i < myData.length; i++) {
     var myItem = myData[i];
     suggestionValues.push({
        id: myItem.someKey,
        text: formatMyItem(myItem)
     });
  }

  $mySelect.select2({
     width: 'resolve',
     multiple: true,
     data: suggestionValues,
     escapeMarkup: function(m) {
        // Do not escape HTML in the select options text
        return m;
     }
  });

But now when the user searches for something, that term is searched inside the HTML of the option.

For example, if the user searches for "strong" (assumming that some descriptions can contain the word "strong"), then select2 will suggest all the values (because all of them contain "strong").

Also, when the user searches for "<" (assuming that some descriptions contain mathematical symbols), then select2 will return all values (because all of them contain html tags), but will not highlight the actual "less than" symbol in the descriptions, because they have been actually converted to "& lt;".

How can I make select2 not search inside the html tags?

Answer

Sorin Postelnicu picture Sorin Postelnicu · Sep 13, 2013

Ok, it seems the solution was actually quite simple :D

I added the following:

  $mySelect.select2({
     width: 'resolve',
     multiple: true,
     data: suggestionValues,
     escapeMarkup: function(m) {
        // Do not escape HTML in the select options text
        return m;
     },
     matcher: function(term, text) {
        // Search the term in the formatted text
        return $("<div/>").html(text).text().toUpperCase().indexOf(term.toUpperCase())>=0;
     }
  });

So now when the user searches for "strong" they get only the relevant results.

But now there is another issue:

Now, if the user searches for "<", then select2 will highlight the "<" inside the strong tag.

It seems that I need to also "patch" somehow the search-results highlighter...

EDIT : Coming back to this, it seems that the solution for the highlighting is not so easy...

The default implementation in select2 is like this:

    formatResult: function(result, container, query, escapeMarkup) {
        var markup=[];
        markMatch(result.text, query.term, markup, escapeMarkup);
        return markup.join("");
    },
    .......

    function markMatch(text, term, markup, escapeMarkup) {
        var match=text.toUpperCase().indexOf(term.toUpperCase()),
            tl=term.length;

        if (match<0) {
            markup.push(escapeMarkup(text));
            return;
        }

        markup.push(escapeMarkup(text.substring(0, match)));
        markup.push("<span class='select2-match'>");
        markup.push(escapeMarkup(text.substring(match, match + tl)));
        markup.push("</span>");
        markup.push(escapeMarkup(text.substring(match + tl, text.length)));
    }

Somehow I need to replace these two functions, but I cannot find an easy solution for mapping from the range of characters in the formatted HTML (the search-term to highlight) back to the source html (so that I can add the < span class='select2-match' > ) ...

If any of you has better solutions, please feel free to share them...