How to match beginning of two object values in jQuery UI autocomplete?
Asked Answered
A

1

0

I have successfully modified the snippet from this blog post.

In order to only match the beginning of one object value:

$.ui.autocomplete.filter = function(array, term) {
var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(term), "i");
return $.grep(array, function(value) {
return matcher.test(value.value);
});
};

I would like to be able to match on two object values - the object value and description, eg: searching for "01" should return object's 1 and 3:

var array_of_objects = [
{value: "01000", product_desc: "a unique orange from spain"},
{value: "02000", product_desc: "a unique pear from spain 01"},
{value: "02001", product_desc: "01000 desc starts with number"}];

jsFiddle

Edit:

This answer looks up on two values, so I could possibly merge it with my jsFiddle solution, but I just need it to match beginning only.

Edit 2:

I've updated my original fiddle and it seems to be doing what is required, but I'm not sure if it's the most efficient way to do it or if it's cluttering up the dom a lot :/.

Abscess answered 16/6, 2017 at 5:32 Comment(0)
A
0

I think I came up with something cooler and more relevant to what I actually needed to do - it matches at the beginning of the value string or anywhere in the product_desc string.

The forked jsFiddle is highly commented so those wanting to just do a lookup on two values (where the match is at the beginning of either string), can comment out some lines and achieve that effect.

I also implemented a number of other features:

  • Match highlighting
  • Display number of matches
  • Scrollable results area
  • Populate page elements with multiple values from an object
  • Templated results
  • Multiple instances on one page

These are also heavily commented so they can be removed or modified if required.

I would still appreciate confirmation that this is a relatively clean solution that does not clutter up the dom with two many divs (autocomplete seems to add lots of unused divs to the dom?)

JS

// BEGIN show message prompt requiring 2 characters
$(".my_lookup").on("keyup", function() {
  var product_container = $(this).parent();
  var $matches_count = product_container.find(".matches_count");
  var $matches_text = product_container.find(".matches_text");
  var input_length = $(this).val().length;
  if (input_length < 2 && input_length !== 0) {
    $(this).siblings(".matches_prompt").text("search requires min 2 characters").show();
    $matches_count.text("").hide();
    $matches_text.text("").hide();
  } else if (input_length === 0 || input_length >= 2) {
    $(this).siblings(".matches_prompt").hide();
  }
});
// END show message prompt requiring 2 characters

// BEGIN look up on two values solution
// see:  https://stackoverflow.com/a/15849692
function lightwell(request, response) {
  // request.term is what you typed
  // response is the data to suggest to the user
  // added the 'match_type' argument to hasMatch() to:
  // - check for match at beginning of object's 'value' string
  // - check for match anywhere in object's 'product_desc' string
  // BEGIN hasMatch() function
  function hasMatch(s, match_type) {
    // to match beginning of string only,
    // use:  === 0
    // see:  https://stackoverflow.com/a/12482182
    // originally was !==-1 
    // to match anywhere in the string,
    // use !==-1
    // when match_type === "start",
    // below returns true if request.term is at the 0 index
    // of the object.value or object.product_desc string
    // when match_type === "anywhere",
    // returns true if at any index other than no index
    // original usage (anywhere) below
    // (remove 'match_type' argument and references to it):
    // return s.toLowerCase().indexOf(request.term.toLowerCase()) !== -1;
    // added conditional handling
    // BEGIN if match_type === "start"
    if (match_type === "start") {
      // check if typed in term is at the 0 index of object's 'value' string
      var is_at_start_of_string = s.toLowerCase().indexOf(request.term.toLowerCase()) === 0;
      if (is_at_start_of_string === true) {
        console.log("typed term is at start of value string");
      }
      return is_at_start_of_string;
    }
    // END if match_type === "start"

    // BEGIN if match_type === "anywhere"
    else if (match_type === "anywhere") {
      // check if typed in term is at any index of object's 'product_desc' string
      var exists_in_string = s.toLowerCase().indexOf(request.term.toLowerCase()) !== -1;
      if (exists_in_string === true) {
        console.log("typed term exists in product_desc string");
      }
      return exists_in_string;
    }
    // END if match_type === "anywhere"
  }
  // END hasMatch() function

  // declare variables, i and obj are undefined, matches is []
  var i, obj, matches = [];

  // BEGIN get the index of the input being used
  // see: https://mcmap.net/q/86623/-how-to-get-the-focused-element-with-jquery
  var current_input_index = $(document.activeElement).parent().index() - 1;
  // END get the index of the input being used

  // BEGIN get refererences to dproduct relative dom elements
  var $product_container = $(".product_container").eq(current_input_index);

  var $matches_count = $product_container.find(".matches_count");
  var $matches_text = $product_container.find(".matches_text");
  // END get refererences to dproduct relative dom elements

  // BEGIN if the typed in term is nothing
  // pass an empty array to response()
  if (request.term === "") {
    console.log("this thing is happening");
    // hide the matches count display
    $matches_count.text("").hide();
    $matches_text.text("").hide();
    response([]);
    return;
  }
  // END if the typed in term is nothing

  // get length of array_of_objects
  var array_of_objects_length = array_of_objects.length;

  // for each object in the array, call the hasMatch() function
  // and pass it the object's 'value' and 'product_desc' strings
  for (i = 0; i < array_of_objects_length; i++) {
    obj = array_of_objects[i];
    // if either of the below conditions return true,
    // push the object to the matches array
    if (hasMatch(obj.value, "start") || hasMatch(obj.product_desc, "anywhere")) {
      matches.push(obj);
    }
  }
  // pass the matches array to response()
  // get the count of matches for display
  var matches_count = matches.length;
  console.log("matches length is: " + matches_count)
  if (matches_count === 0 || matches_count > 1) {
    var matches_text = " matches"
  } else if (matches_count === 1) {
    var matches_text = " match"
  }
  // display the count of matches
  $matches_count.text(matches_count).show();
  $matches_text.text(matches_text).show();
  response(matches);
}
// END look up on two values solution


// BEGIN autocomplete
$(".my_lookup").autocomplete({
  // only show 5 results with scrollbar
  // from:  http://anseki.github.io/jquery-ui-autocomplete-scroll
  maxShowItems: 5,
  source: lightwell,
  minLength: 2,
  // called on input blur if value has changed
  change: function(event, ui) {
    var product_container = $(this).closest(".product_container");
    var $matches_count = product_container.find(".matches_count");
    var $matches_text = product_container.find(".matches_text");
    $matches_count.text("").hide();
    $matches_text.text("").hide();
  },
  // called when selecting an option
  select: function(event, ui) {

    // get references to product relative selectors
    var product_container = $(this).closest(".product_container");
    var product_desc = product_container.find(".product_desc");
    var product_type = product_container.find(".product_type");
    var product_attr_01 = product_container.find(".product_attr_01");
    var product_attr_02 = product_container.find(".product_attr_02");

    var $matches_count = product_container.find(".matches_count");
    var $matches_text = product_container.find(".matches_text");
    $matches_count.text("").hide();
    $matches_text.text("").hide();

    // add object's values to relative product container inputs
    product_desc.val(ui.item.product_desc);
    product_type.val(ui.item.product_type);
    product_attr_01.val(ui.item.product_attr_01);
    product_attr_02.val(ui.item.product_attr_02);

    // BEGIN animate realtive inputs when selecting an option
    product_desc.animate({
      "backgroundColor": "#d0e4ff"
    }, {
      "queue": false,
      "duration": 800
    });
    product_desc.animate({
      "borderColor": "#7190dd"
    }, {
      "queue": false,
      "duration": 800
    });
    product_type.animate({
      "backgroundColor": "#d0e4ff"
    }, {
      "queue": false,
      "duration": 800
    });
    product_type.animate({
      "borderColor": "#7190dd"
    }, {
      "queue": false,
      "duration": 800
    });
    product_attr_01.animate({
      "backgroundColor": "#d0e4ff"
    }, {
      "queue": false,
      "duration": 800
    });
    product_attr_01.animate({
      "borderColor": "#7190dd"
    }, {
      "queue": false,
      "duration": 800
    });
    product_attr_02.animate({
      "backgroundColor": "#d0e4ff"
    }, {
      "queue": false,
      "duration": 800
    });
    product_attr_02.animate({
      "borderColor": "#7190dd"
    }, {
      "queue": false,
      "duration": 800
    });
    setTimeout(function() {
      product_desc.animate({
        "backgroundColor": "#ffff"
      }, {
        "queue": false,
        "duration": 800
      });
      product_desc.animate({
        "borderColor": "#cacaca"
      }, {
        "queue": false,
        "duration": 800
      });
      product_type.animate({
        "backgroundColor": "#ffff"
      }, {
        "queue": false,
        "duration": 800
      });
      product_type.animate({
        "borderColor": "#cacaca"
      }, {
        "queue": false,
        "duration": 800
      });
      product_attr_01.animate({
        "backgroundColor": "#ffff"
      }, {
        "queue": false,
        "duration": 800
      });
      product_attr_01.animate({
        "borderColor": "#cacaca"
      }, {
        "queue": false,
        "duration": 800
      });
      product_attr_02.animate({
        "backgroundColor": "#ffff"
      }, {
        "queue": false,
        "duration": 800
      });
      product_attr_02.animate({
        "borderColor": "#cacaca"
      }, {
        "queue": false,
        "duration": 800
      });
    }, 2000);
    // END animate realtive inputs when selecting an option
  },
  // show fontawesome loading animation when search starts
  search: function(event, ui) {
    console.log("search started");
    $(this).closest(".product_container").find(".fa-circle-o-notch").css("visibility", "visible");
  },
  // hide fontawesome loading animation when search finishes
  response: function(event, ui) {
    console.log("search finished");
    $(this).closest(".product_container").find(".fa-circle-o-notch").css("visibility", "hidden");
  },

  // BEGIN add styles to results
  // from:  https://gist.github.com/DBasic/9545690
  create: function() {
      $(this).data('ui-autocomplete')._renderItem = function(ul, item) {

        // create a new 'value' string for match highlighting
        // see: https://mcmap.net/q/515384/-how-to-highlight-input-words-in-autocomplete-jquery-ui
        var newValueText = String(item.value).replace(
          // changed "gi" to i so that the match
          // on instance of search term is limited
          // to the first match, see:  
          // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
          new RegExp(this.term, "i"),
          "<span class='ui-state-highlight'>$&</span>");

        // create a new 'description' string for match highlighting
        var newDescText = String(item.product_desc.toUpperCase()).replace(
          new RegExp(this.term, "gi"),
          "<span class='ui-state-highlight'>$&</span>");

        return $("<li></li>")
          .data("ui-autocomplete-item", item)
          // newValueText below used to be: item.value.toUpperCase()
          // newDescText below used to be: item.product_desc.toUpperCase()
          .append("<div class=\"my_result\"><span class=\"helper\"></span><img class=\"result_img\" src=\"https://placeimg.com/30/30/nature\">" + newValueText + " - " + newDescText + "</div>")
          .appendTo(ul);
      }
    }
    // END add styles to results

});
// END autocomplete


// new data set generated from https://github.com/Marak/faker.js

var array_of_objects = [{
  "value": "020101",
  "product_desc": "Handmade Frozen Pants",
  "product_type": "Automotive",
  "product_attr_01": "106.00",
  "product_attr_02": "171.00"
}, {
  "value": "010101",
  "product_desc": "Practical Fresh Hat",
  "product_type": "Movies",
  "product_attr_01": "589.00",
  "product_attr_02": "777.00"
}, {
  "value": "7099",
  "product_desc": "Awesome Concrete Sausages",
  "product_type": "Grocery",
  "product_attr_01": "249.00",
  "product_attr_02": "167.00"
}, {
  "value": "5740",
  "product_desc": "Sleek Plastic Pizza",
  "product_type": "Home",
  "product_attr_01": "924.00",
  "product_attr_02": "379.00"
}, {
  "value": "7627",
  "product_desc": "Intelligent Plastic Mouse",
  "product_type": "Games",
  "product_attr_01": "71.00",
  "product_attr_02": "412.00"
}, {
  "value": "6606",
  "product_desc": "Handmade Steel Pizza",
  "product_type": "Music",
  "product_attr_01": "71.00",
  "product_attr_02": "791.00"
}, {
  "value": "0973",
  "product_desc": "Intelligent Granite Tuna",
  "product_type": "Industrial",
  "product_attr_01": "25.00",
  "product_attr_02": "441.00"
}, {
  "value": "5453",
  "product_desc": "Generic Steel Sausages",
  "product_type": "Sports",
  "product_attr_01": "887.00",
  "product_attr_02": "786.00"
}, {
  "value": "3871",
  "product_desc": "Refined Wooden Keyboard",
  "product_type": "Sports",
  "product_attr_01": "897.00",
  "product_attr_02": "402.00"
}, {
  "value": "9646",
  "product_desc": "Incredible Soft Chicken",
  "product_type": "Kids",
  "product_attr_01": "849.00",
  "product_attr_02": "438.00"
}, {
  "value": "2948",
  "product_desc": "Licensed Plastic Gloves",
  "product_type": "Baby",
  "product_attr_01": "608.00",
  "product_attr_02": "748.00"
}, {
  "value": "1561",
  "product_desc": "Sleek Steel Towels",
  "product_type": "Music",
  "product_attr_01": "315.00",
  "product_attr_02": "332.00"
}, {
  "value": "5012",
  "product_desc": "Licensed Rubber Computer",
  "product_type": "Electronics",
  "product_attr_01": "886.00",
  "product_attr_02": "738.00"
}, {
  "value": "4827",
  "product_desc": "Unbranded Wooden Shoes",
  "product_type": "Shoes",
  "product_attr_01": "390.00",
  "product_attr_02": "753.00"
}, {
  "value": "0056",
  "product_desc": "Handcrafted Fresh Sausages",
  "product_type": "Shoes",
  "product_attr_01": "26.00",
  "product_attr_02": "257.00"
}, {
  "value": "0628",
  "product_desc": "Fantastic Steel Tuna",
  "product_type": "Tools",
  "product_attr_01": "881.00",
  "product_attr_02": "127.00"
}, {
  "value": "8498",
  "product_desc": "Gorgeous Soft Fish",
  "product_type": "Toys",
  "product_attr_01": "105.00",
  "product_attr_02": "604.00"
}, {
  "value": "9265",
  "product_desc": "Gorgeous Wooden Cheese",
  "product_type": "Clothing",
  "product_attr_01": "257.00",
  "product_attr_02": "438.00"
}, {
  "value": "0666",
  "product_desc": "Small Soft Keyboard",
  "product_type": "Baby",
  "product_attr_01": "960.00",
  "product_attr_02": "852.00"
}, {
  "value": "4628",
  "product_desc": "Intelligent Plastic Car",
  "product_type": "Music",
  "product_attr_01": "598.00",
  "product_attr_02": "339.00"
}, {
  "value": "3341",
  "product_desc": "Intelligent Metal Mouse",
  "product_type": "Garden",
  "product_attr_01": "92.00",
  "product_attr_02": "371.00"
}, {
  "value": "6547",
  "product_desc": "Awesome Concrete Shirt",
  "product_type": "Health",
  "product_attr_01": "344.00",
  "product_attr_02": "145.00"
}, {
  "value": "0803",
  "product_desc": "Unbranded Metal Chair",
  "product_type": "Kids",
  "product_attr_01": "343.00",
  "product_attr_02": "700.00"
}, {
  "value": "9769",
  "product_desc": "Awesome Granite Bike",
  "product_type": "Home",
  "product_attr_01": "545.00",
  "product_attr_02": "391.00"
}, {
  "value": "3087",
  "product_desc": "Refined Wooden Tuna",
  "product_type": "Industrial",
  "product_attr_01": "58.00",
  "product_attr_02": "68.00"
}, {
  "value": "3202",
  "product_desc": "Small Concrete Gloves",
  "product_type": "Kids",
  "product_attr_01": "846.00",
  "product_attr_02": "60.00"
}, {
  "value": "9638",
  "product_desc": "Generic Rubber Ball",
  "product_type": "Garden",
  "product_attr_01": "160.00",
  "product_attr_02": "123.00"
}, {
  "value": "4762",
  "product_desc": "Tasty Frozen Computer",
  "product_type": "Health",
  "product_attr_01": "698.00",
  "product_attr_02": "832.00"
}, {
  "value": "6606",
  "product_desc": "Rustic Frozen Shirt",
  "product_type": "Automotive",
  "product_attr_01": "867.00",
  "product_attr_02": "92.00"
}, {
  "value": "6853",
  "product_desc": "Ergonomic Steel Pants",
  "product_type": "Sports",
  "product_attr_01": "712.00",
  "product_attr_02": "378.00"
}, {
  "value": "5418",
  "product_desc": "Awesome Cotton Cheese",
  "product_type": "Toys",
  "product_attr_01": "483.00",
  "product_attr_02": "918.00"
}, {
  "value": "0230",
  "product_desc": "Licensed Cotton Towels",
  "product_type": "Clothing",
  "product_attr_01": "540.00",
  "product_attr_02": "415.00"
}, {
  "value": "3975",
  "product_desc": "Sleek Granite Pants",
  "product_type": "Outdoors",
  "product_attr_01": "823.00",
  "product_attr_02": "331.00"
}, {
  "value": "7581",
  "product_desc": "Ergonomic Concrete Bacon",
  "product_type": "Automotive",
  "product_attr_01": "640.00",
  "product_attr_02": "718.00"
}, {
  "value": "8550",
  "product_desc": "Practical Granite Table",
  "product_type": "Shoes",
  "product_attr_01": "94.00",
  "product_attr_02": "487.00"
}, {
  "value": "3358",
  "product_desc": "Fantastic Plastic Computer",
  "product_type": "Clothing",
  "product_attr_01": "448.00",
  "product_attr_02": "440.00"
}, {
  "value": "4586",
  "product_desc": "Ergonomic Steel Table",
  "product_type": "Games",
  "product_attr_01": "218.00",
  "product_attr_02": "806.00"
}, {
  "value": "6331",
  "product_desc": "Intelligent Wooden Gloves",
  "product_type": "Shoes",
  "product_attr_01": "236.00",
  "product_attr_02": "546.00"
}, {
  "value": "2871",
  "product_desc": "Handcrafted Wooden Salad",
  "product_type": "Beauty",
  "product_attr_01": "546.00",
  "product_attr_02": "259.00"
}, {
  "value": "1648",
  "product_desc": "Tasty Soft Pants",
  "product_type": "Kids",
  "product_attr_01": "641.00",
  "product_attr_02": "251.00"
}, {
  "value": "8096",
  "product_desc": "Practical Steel Chair",
  "product_type": "Toys",
  "product_attr_01": "609.00",
  "product_attr_02": "374.00"
}, {
  "value": "5810",
  "product_desc": "Refined Steel Chicken",
  "product_type": "Kids",
  "product_attr_01": "529.00",
  "product_attr_02": "705.00"
}, {
  "value": "7057",
  "product_desc": "Tasty Metal Mouse",
  "product_type": "Garden",
  "product_attr_01": "911.00",
  "product_attr_02": "935.00"
}, {
  "value": "2344",
  "product_desc": "Intelligent Cotton Pizza",
  "product_type": "Sports",
  "product_attr_01": "705.00",
  "product_attr_02": "220.00"
}, {
  "value": "9188",
  "product_desc": "Awesome Wooden Ball",
  "product_type": "Movies",
  "product_attr_01": "896.00",
  "product_attr_02": "850.00"
}, {
  "value": "1474",
  "product_desc": "Sleek Plastic Salad",
  "product_type": "Tools",
  "product_attr_01": "15.00",
  "product_attr_02": "668.00"
}, {
  "value": "6513",
  "product_desc": "Small Soft Chips",
  "product_type": "Health",
  "product_attr_01": "433.00",
  "product_attr_02": "74.00"
}, {
  "value": "4036",
  "product_desc": "Unbranded Wooden Soap",
  "product_type": "Grocery",
  "product_attr_01": "826.00",
  "product_attr_02": "920.00"
}, {
  "value": "9226",
  "product_desc": "Licensed Fresh Cheese",
  "product_type": "Industrial",
  "product_attr_01": "144.00",
  "product_attr_02": "102.00"
}, {
  "value": "1944",
  "product_desc": "Fantastic Rubber Shoes",
  "product_type": "Electronics",
  "product_attr_01": "820.00",
  "product_attr_02": "808.00"
}, {
  "value": "9379",
  "product_desc": "Small Wooden Tuna",
  "product_type": "Baby",
  "product_attr_01": "199.00",
  "product_attr_02": "160.00"
}, {
  "value": "7888",
  "product_desc": "Handcrafted Frozen Sausages",
  "product_type": "Electronics",
  "product_attr_01": "70.00",
  "product_attr_02": "419.00"
}, {
  "value": "1941",
  "product_desc": "Fantastic Granite Car",
  "product_type": "Grocery",
  "product_attr_01": "821.00",
  "product_attr_02": "853.00"
}, {
  "value": "8322",
  "product_desc": "Licensed Wooden Fish",
  "product_type": "Games",
  "product_attr_01": "998.00",
  "product_attr_02": "703.00"
}, {
  "value": "5586",
  "product_desc": "Fantastic Cotton Salad",
  "product_type": "Games",
  "product_attr_01": "887.00",
  "product_attr_02": "841.00"
}];

/* code to generate the above data set,
from: https://github.com/Marak/faker.js

var array_of_objects = [];

for (i = 0; i < 5; i++) {
    var obj = {}; 
    obj['value'] = faker.fake("{{finance.mask}}");
    obj['product_desc'] = faker.fake("{{commerce.productName}}");
    obj['product_type'] = faker.fake("{{commerce.department}}");
    obj['product_attr_01'] = faker.fake("{{commerce.price}}");
    obj['product_attr_02'] = faker.fake("{{commerce.price}}");
    array_of_objects.push(obj);
}

*/
Abscess answered 16/6, 2017 at 10:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.