jQuery UI Autocomplete highlight multiple inputs: doesn't work for uppercase?

Viewed 257

I came across an assignment that requires us to highlight multiple matching substrings in jQuery's UI Autocomplete widget.

UPDATE: New problem

Now I have a second problem: when the textbox is empty and I type a letter, the drop-down menu shows all the right highlights and items.

But when I put a comma after the first query, the second search shows the correct menu items, but it does NOT show any highlights:

Input 1: b

Drop-down menu: Bulbasaur

Textbox after selecting menu item: Bulbasaur,

Input 2: c

Drop-down menu: Charmander

Textbox after selecting menu item: Bulbasaur, Charmander,

What I want

Input 2: c

Drop-down menu: Charmander

Any help would be appreciated!

My code

The categories is also part of the requirements.

<script>
    var pokemonList = [ ... ];

    function widgetConstr()
    {
        this._super();
    };

    function renderPokemons(ul, item)
    {
        terms = this.term.split(',');
        term = this.element.val().trim();

        var result = new RegExp(term, "gi");
        var newTerm = item.label
                        .replace(result, "<span class='match-character'>" + term + "</span>");

        return $("<li></li>")
                .data("item.autocomplete", item)
                .append("<a>" + newTerm + "</a>")
                .appendTo(ul);
    };

    function renderPokemonList(ul, items)
    {
        var that = this;
        currentCategory = "";

        $.each(
            items, function(index, item)
            {
                var li;
                if (item.category != currentCategory)
                {
                    ul.append("<li class='ui-autocomplete-category'>" + item.category + "</li>");
                    currentCategory = item.category;
                }

                li = that._renderItemData(ul, item);

                if (item.category)
                    li.attr("aria-label", item.category + " : " + item.label);
            }
        );
    };

    $.widget(
        "custom.PokemonSearch", $.ui.autocomplete,
        {
            _create: widgetConstr,
            _renderItem: renderPokemons,
            _renderMenu: renderPokemonList
        }
    );

    function split(val)
    {
        return val.split(/,\s*/);
    };

    function extractLast(term)
    {
        return split(term).pop();
    };

    $("search").on("keydown", function(event)
        {
            if (event.keyCode == $.ui.keyCode.TAB && $(this).autocomplete("instance").menu.active)
                event.preventDefault();
        }
    );

    function onDocumentReady()
    {
        $("#search").PokemonSearch(
            {
                delay: 0,
                max: 10,
                scroll: true,
                source: function(request, response)
                {
                    response($.ui.autocomplete.filter(
                        pokemonList, extractLast(request.term)
                    ));
                },

                focus: function()
                {
                    return false;
                },

                select: function(event, ui)
                {
                    var terms = split(this.value);
                    terms.pop();
                    terms.push(ui.item.value);
                    terms.push("");
                    this.value = terms.join(", ");

                    return false;
                }
            }
        )
    };

    $(document).ready(onDocumentReady);

</script>

Relevant CSS

.match-character {
    font-weight: bold;
    font-style: italic;
    color: blue;
}

SOLVED: Problem 1

Update: Thanks so much for the help!!

What I want

Whether the input query is upper or lowercase, the drop-down menu displays uppercase and lowercase results as highlighted:

Input: b

Drop-down menu: Bulbasaur

Reality

My drop-down menu displays matching uppercase letters to lowercase, but after selecting a menu item, it displays the uppercase letter correctly:

Input: b

Drop-down menu: bulbasaur

Textbox after selecting menu item: Bulbasaur,

What I've tried

If I remove the i tag in var result = new RegExp(term, "gi");, the menu shows results with capital letters, but they don't show as highlighted.

The lecture notes and labs didn't mention or suggest how to do both upper and lowercase highlighting with a lowercase query.

I tried looking online but either the solutions don't work, or are too complicated for me to understand (I've only been coding in JavaScript for a few weeks). 99% of the code here are copied directly from various sources.

I've only just understood what RegExp() does, but I have no idea where to go to achieve what I want.

Any guidance is appreciated!

1 Answers

You could pass the Regexp variable in replace and access the match in a function callback:

var result = new RegExp(term, "gi");
var newTerm = item.label
    .replace(result, function(match) {
      return "<span class='match-character'>" + match + "</span>"
});

var pokemonList = ['Bulbasaur'];
fetch("https://pokeapi.co/api/v2/pokemon/?limit=50").then(a => a.json().then(b => {
  pokemonList = b.results.map(({
    name,
    ...obj
  }) => name.charAt(0).toUpperCase() + name.slice(1))
}));

function widgetConstr() {
  this._super();
};

function renderPokemons(ul, item) {
  terms = this.term.split(',');
  term = this.element.val().trim();

  var result = new RegExp(term, "gi");
  var newTerm = item.label
    .replace(result, function(match) {
      return "<span class='match-character'>" + match + "</span>"
    });

  return $("<li></li>")
    .data("item.autocomplete", item)
    .append("<a>" + newTerm + "</a>")
    .appendTo(ul);
};

function renderPokemonList(ul, items) {
  var that = this;
  currentCategory = "";

  $.each(
    items,
    function(index, item) {
      var li;
      if (item.category != currentCategory) {
        ul.append("<li class='ui-autocomplete-category'>" + item.category + "</li>");
        currentCategory = item.category;
      }

      li = that._renderItemData(ul, item);

      if (item.category)
        li.attr("aria-label", item.category + " : " + item.label);
    }
  );
};

$.widget(
  "custom.PokemonSearch", $.ui.autocomplete, {
    _create: widgetConstr,
    _renderItem: renderPokemons,
    _renderMenu: renderPokemonList
  }
);

function split(val) {
  return val.split(/,\s*/);
};

function extractLast(term) {
  return split(term).pop();
};

$("search").on("keydown", function(event) {
  if (event.keyCode == $.ui.keyCode.TAB && $(this).autocomplete("instance").menu.active)
    event.preventDefault();
});

function onDocumentReady() {
  $("#search").PokemonSearch({
    delay: 0,
    max: 10,
    scroll: true,
    source: function(request, response) {
      response($.ui.autocomplete.filter(
        pokemonList, extractLast(request.term)
      ));
    },

    focus: function() {
      return false;
    },

    select: function(event, ui) {
      var terms = split(this.value);
      terms.pop();
      terms.push(ui.item.value);
      terms.push("");
      this.value = terms.join(", ");

      return false;
    }
  })
};

$(document).ready(onDocumentReady);
.match-character {
  font-weight: bold;
  font-style: italic;
  color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<input id="search" />


UPDATE for comma seperated take the latest of the bunch:

term = terms[terms.length -1].trim();

var pokemonList = ['Bulbasaur'];
fetch("https://pokeapi.co/api/v2/pokemon/?limit=50").then(a => a.json().then(b => {
  pokemonList = b.results.map(({
    name,
    ...obj
  }) => name.charAt(0).toUpperCase() + name.slice(1))
}));

function widgetConstr() {
  this._super();
};

function renderPokemons(ul, item) {
  terms = this.term.split(',');
  term = terms[terms.length -1].trim();

  var result = new RegExp(term, "gi");
  var newTerm = item.label
    .replace(result, function(match) {
      return "<span class='match-character'>" + match + "</span>"
    });

  return $("<li></li>")
    .data("item.autocomplete", item)
    .append("<a>" + newTerm + "</a>")
    .appendTo(ul);
};

function renderPokemonList(ul, items) {
  var that = this;
  currentCategory = "";

  $.each(
    items,
    function(index, item) {
      var li;
      if (item.category != currentCategory) {
        ul.append("<li class='ui-autocomplete-category'>" + item.category + "</li>");
        currentCategory = item.category;
      }

      li = that._renderItemData(ul, item);

      if (item.category)
        li.attr("aria-label", item.category + " : " + item.label);
    }
  );
};

$.widget(
  "custom.PokemonSearch", $.ui.autocomplete, {
    _create: widgetConstr,
    _renderItem: renderPokemons,
    _renderMenu: renderPokemonList
  }
);

function split(val) {
  return val.split(/,\s*/);
};

function extractLast(term) {
  return split(term).pop();
};

$("search").on("keydown", function(event) {
  if (event.keyCode == $.ui.keyCode.TAB && $(this).autocomplete("instance").menu.active)
    event.preventDefault();
});

function onDocumentReady() {
  $("#search").PokemonSearch({
    delay: 0,
    max: 10,
    scroll: true,
    source: function(request, response) {
      response($.ui.autocomplete.filter(
        pokemonList, extractLast(request.term)
      ));
    },

    focus: function() {
      return false;
    },

    select: function(event, ui) {
      var terms = split(this.value);
      terms.pop();
      terms.push(ui.item.value);
      terms.push("");
      this.value = terms.join(", ");

      return false;
    }
  })
};

$(document).ready(onDocumentReady);
.match-character {
  font-weight: bold;
  font-style: italic;
  color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<input id="search" />

Related