Reputation: 55
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!
Upvotes: 5
Views: 3792
Reputation: 29092
I've made a few assumptions:
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>
Upvotes: 3
Reputation: 128
This worked for me:
// Get all li's and create an array with each li's data attributes
const lis = document.querySelector("#myUL").children;
const keywordArray = [];
[...lis].forEach((li, i) => {
keywordArray.push(li.children[0].dataset.keywords);
});
function myFunction() {
//Show any previous hidden li's
[...lis].forEach(li => {
li.style.display = 'block';
});
const inputArray = document.querySelector('#myInput').value.split(' ');
const indexes = [];
// For each word in the input field, search through our data attribute array
inputArray.forEach(term => {
keywordArray.forEach((keywords, i) => {
keywords.split(' ').forEach(keyword => {
// If we find a match, add the index of the data attribute array, which
// will be the same as the index of the li element
if (keyword === term) {
indexes.push(i);
}
});
});
});
// If we have a match, hide every non-matching li'
if (indexes.length) {
[...lis].forEach((li, i) => {
if (!indexes.includes(i)) {
li.style.display = 'none';
}
});
}
}
For clarification, do you want additional search terms to make the search more specific or less specific? Because my solution makes the search less specific as it will show any li matching any search term.
Upvotes: 0