4
\$\begingroup\$

I have a select box with some options in it and a input box. I have to filter the values of select box on the basis of the keyword entered in the text box.

JavaScript

function filter() {
    var keyword = document.getElementById("search").value;
    var select = document.getElementById("select");
    for (var i = 0; i < select.length; i++) {
        var txt = select.options[i].text;
        if (txt.substring(0, keyword.length).toLowerCase() !== keyword.toLowerCase() && keyword.trim() !== "") {
            select.options[i].style.display = 'none';
        } else {
            select.options[i].style.display = 'list-item';
        }
    }
}

HTML

<div>
    <div>
        <input type="text" id="search" name="search" style="margin: 10px;width: 165px;" onkeyup="filter()">
    </div>
    <div>
        <select id="select" size="10" style="margin: 10px; width: 165px;height: 170px;">
            <option>Cupcake</option>
            <option>Cunut</option>
            <option>Eclair</option>
            <option>Froyo</option>
            <option>Gingerbread</option>
            <option>Honeycomb</option>
            <option>Ice Cream Sandwich</option>
            <option>Jelly Bean</option>
            <option>KitKat</option>
            <option>Lollipop</option>
            <option>Marshmallow</option>
            <option>Nougat</option>
        </select>
    </div>
</div>

Is this the right way for this task?

Here is the working code for the scenario.

\$\endgroup\$
  • \$\begingroup\$ Is it possible that it doesn't work with Internet Explorer? If tried your JSFiddle with IE 11 but it doesn't filter the option-items. \$\endgroup\$ – michael.zech Sep 5 '16 at 6:15
  • \$\begingroup\$ @mizech yes you are right , its not working in IE , any suggestion to make it work in IE too. \$\endgroup\$ – zombie Sep 5 '16 at 15:51
  • \$\begingroup\$ No, sorry. Frankly: I'm not experienced in solving cross-browser issue. Normally a Mac-user I discovered it only because I had to use a windows machine this morning. \$\endgroup\$ – michael.zech Sep 5 '16 at 16:00
  • \$\begingroup\$ OK and thanks for your time to point out that issue. \$\endgroup\$ – zombie Sep 5 '16 at 16:05
  • \$\begingroup\$ Don't mention it. Wish I could help you further. \$\endgroup\$ – michael.zech Sep 5 '16 at 16:14
2
\$\begingroup\$

I would start by moving the search term into the function definition, to save having to look up the DOM element. So I would change in the HTML

 <input type="text" id="search" name="search" style="margin: 10px;width: 165px;" onkeyup="filter(this.value)">

And then have function be defined with keyword as a parameter.

function filter(keyword) {
  ...
}

The other main change I would make is to use string.prototype.startsWith rather than substring checking, this is more explicit about what is trying to be done, and saves having logic to handle the empty string.

Also, rather than an If statement and then setting style.display, the display is set with either 'list-item' or 'none' with the ternary operator.

This leaves the function as

function filter(keyword) {
    var select = document.getElementById("select");
    for (var i = 0; i < select.length; i++) {
        var txt = select.options[i].text;
        var include = txt.toLowerCase().startsWith(keyword.toLowerCase());
        select.options[i].style.display = include ? 'list-item':'none';
    }
}

A further improvement you might want to look at may include moving the select element selection outside of this function to improve re-use.

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks for the review. I first used String.prototype.startsWith() method but in MDN it is mentioned that the method is not supported in all javascript implementation so i used the workaround for that. \$\endgroup\$ – zombie Sep 3 '16 at 19:19
  • \$\begingroup\$ That's a good point about compatibility. Generally I prefer to use the method in the standards and then polyfil but avoiding that is understandable \$\endgroup\$ – Eterm Sep 3 '16 at 19:40
2
\$\begingroup\$

A good attempt I must say, but there are other ways to achieve this.

Just so you know, select.option returns an optionCollection which can be cast to an array using Array.from(select.options).

Based on this, you can utilise the filter pre-defined function for an array object

var optionCollection = Array.from(select.options).filter(x => x.text.toLowerCase().startsWith(keyword.toLowerCase()))

this will get rid of the for..loopyou had. In addition, using filter function doesn't change the array but returns a new array with the filtered content.

The result from this can be used to replace the html of the select element i.e

$("#select").html(optionCollection)

Note: the above is JQuery

To effectively use the JQuery, you can embed this in a ternary operator

optionCollection.length > 0 ? $("#select").html(b) : $("#select").html(realCollection);

Note: the realCollection will be the initial select options collection.

I hope this helps

\$\endgroup\$
  • \$\begingroup\$ the for loop is way better than the mapping and filter functions \$\endgroup\$ – PirateApp Jan 10 '18 at 5:53
2
\$\begingroup\$

My code is based on the question-code of mizech in this StackOverflow question, I add focus() and setInterval() to achieve real time search and show all items when the input box is empty.

The view in IE:

enter image description here

The adjusted code with jQuery:

$("#search-case-name1").focus(
  function() {
    setInterval(
      function() {
        var keyword = document
          .getElementById("search-case-name1").value;
        var select = document.getElementById("case-name1");
        if (keyword.length > 0) {
          for (var i = 0; i < select.length; i++) {
            var txt = select.options[i].text;
            if (txt.substring(0, keyword.length)
              .toLowerCase() !== keyword
              .toLowerCase() &&
              keyword.trim() !== "") {
              $(select.options[i]).attr('disabled',
                'disabled').hide();
            } else {
              $(select.options[i]).removeAttr(
                'disabled').show();
            }
          }
        } else {
          for (var i = 0; i < select.length; i++) {
            $(select.options[i]).removeAttr('disabled')
              .show();
          }
        }
      }, 300);
  });

\$\endgroup\$
  • 1
    \$\begingroup\$ Could you correct the extreme indentations? \$\endgroup\$ – t3chb0t Aug 1 '17 at 6:27
1
\$\begingroup\$

I have tried to fix the Internet Explorer problem.

My code is a mixture of the question-code combined with the answer from "Gev" in this StackOverflow question: https://stackoverflow.com/questions/20373558/options-with-displaynone-not-hidden-in-ie

It still doesn't remove the options but they become disabled after leaving the textbox via tabulator key.

The view in Internet Explorer 11:

In Internet Explorer:

The adjusted code with jQuery:

function filter() {
    var keyword = document.getElementById("search").value;
    var select = document.getElementById("select");
    for (var i = 0; i < select.length; i++) {
        var txt = select.options[i].text;
        if (txt.substring(0, keyword.length).toLowerCase() !== keyword.toLowerCase() && keyword.trim() !== "") {
          $(select.options[i]).attr('disabled', 'disabled').hide();
        } else {
          $(select.options[i]).removeAttr('disabled').show();
        }
    }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
    <div>
        <input type="text" id="search" name="search" style="margin: 10px;width: 165px;" onkeyup="filter()">
    </div>
    <div>
        <select id="select" size="10" style="margin: 10px; width: 165px;height: 170px;">
            <option>Cupcake</option>
            <option>Cunut</option>
            <option>Eclair</option>
            <option>Froyo</option>
            <option>Gingerbread</option>
            <option>Honeycomb</option>
            <option>Ice Cream Sandwich</option>
            <option>Jelly Bean</option>
            <option>KitKat</option>
            <option>Lollipop</option>
            <option>Marshmallow</option>
            <option>Nougat</option>
        </select>
    </div>
</div>

\$\endgroup\$
  • \$\begingroup\$ Thanks mizech , but my requirement is to hide the options. \$\endgroup\$ – zombie Sep 6 '16 at 13:44
  • \$\begingroup\$ You're welcome. Sadly the nearest solution approach I could find. :( \$\endgroup\$ – michael.zech Sep 6 '16 at 15:19
  • \$\begingroup\$ Found a workaround that also works in IE. Here is the link. It works fine in IE edge. \$\endgroup\$ – zombie Sep 7 '16 at 11:25
  • \$\begingroup\$ Indeed. Tried it out and it works like charm on the IE 11 I got here. Heartly congrats ! And thanks for the update. :) \$\endgroup\$ – michael.zech Sep 7 '16 at 11:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.