Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery search and filter using keywords

I'm trying to program a search/filter function that searches through unordered list items with data-* attributes based on what the user types in.

<input type="text" placeholder="Search..." id="myInput" onkeyup="myFunction()">        
<ul id="myUL">
    <li><a href="#" data-keywords="photography">Digital Media Design</a></li>
    <li><a href="#" data-keywords="computers">Information Technology</a></li>
    <li><a href="#" data-keywords="coding">Programming</a></li>
</ul>

Here is the code I have so far that works only for one data-keywords item. I need help to get it to bring up search results based on multiple keywords.

<li><a href="#" data-keywords="photography photoshop illustrator premiere">Digital Media Design</a></li>

// Search functionality
function myFunction() {
    // Declare variables
    var input, filter, ul, li, a, i;
    input = document.getElementById('myInput');
    filter = input.value.toUpperCase();
    ul = document.getElementById("myUL");
    li = ul.getElementsByTagName('li');
    // Loop through all list items, and hide those who don't match the search query
    for (i = 0; i < li.length; i++) {
        a = li[i].getElementsByTagName("a")[0];
        if (a.innerHTML.toUpperCase().indexOf(filter) > -1 || $(a).data("keywords") === filter.toLocaleLowerCase()) {
            li[i].style.display = "";
        } else {
            li[i].style.display = "none";
        }
    }
}

If anybody has suggestions on how I can improve the code that would be awesome!

like image 305
Frank Avatar asked Oct 29 '22 02:10

Frank


1 Answers

I've made a few assumptions:

  • If multiple search terms are provided they must all match.
  • Search terms must be letters, a-z. That could easily be changed to include other characters, such as numbers, but ultimately we need some way to decide where one term ends and the next one begins.
  • Matching is based on being a substring.

The bit that needs most explaining is that regex. It's using a standard trick for doing an and match:

Regular Expressions: Is there an AND operator?

So if you search for digital photography the RegExp will be equivalent to:

/(?=.*digital)(?=.*photography)/i

If you wanted searching to be or rather than and you'd just need to tweak the RegExp accordingly. If you wanted to do a starts-with match rather than substring you could throw in \b before each search term (suitably escaped as \\b in the string).

I hope the rest is pretty self-explanatory, I tried to stay close to the code in the question.

// Search functionality
function myFunction() {
    // Declare variables
    var input = document.getElementById('myInput'),
        filter = input.value,
        ul = document.getElementById('myUL'),
        lis = ul.getElementsByTagName('li'),
        searchTerms = filter.match(/[a-z]+/gi),
        re, index, li, a;
        
    if (searchTerms) {
        searchTerms = searchTerms.map(function(term) {
            return '(?=.*' + term + ')';
        });
        
        re = new RegExp(searchTerms.join(''), 'i');
    } else {
        re = /./;
    }

    // Loop through all list items, and hide those who don't match the search query
    for (index = 0; index < lis.length; index++) {
        li = lis[index];
        a = li.firstChild;

        if (re.test(a.innerHTML + ' ' + a.getAttribute('data-keywords'))) {
            li.style.display = '';
        } else {
            li.style.display = 'none';
        }
    }
}
<input type="text" placeholder="Search..." id="myInput" onkeyup="myFunction()">

<ul id="myUL">
  <li><a href="#" data-keywords="photography photoshop illustrator premiere">Digital Media Design</a></li>
  <li><a href="#" data-keywords="computers">Information Technology</a></li>
  <li><a href="#" data-keywords="coding">Programming</a></li>
</ul>
like image 111
skirtle Avatar answered Nov 12 '22 16:11

skirtle