Filtering a list as you type with jQuery
I was planning to create an unordered list of users of my web application using a simple database query, but then was planning to let people filter this list down by typing the person's name they're looking for into a text input.
I was hoping to use jQuery to match the string in the input box to any inside one of the list items, then hide the other ones开发者_StackOverflow社区, maybe by applying a new class dynamically to the ones that contain the matching string, and hiding all others that don't contain that class.
Does anyone know of a good way to do this?
Assuming that your ul
has an id
of theList
, the following would be one way of doing it.
<input type="text" onkeyup="filter(this)" />
<script language="javascript" type="text/javascript">
function filter(element) {
var value = $(element).val();
$("#theList > li").each(function() {
if ($(this).text().search(value) > -1) {
$(this).show();
}
else {
$(this).hide();
}
});
}
</script>
If you don't wish to have case-sensitive filter then add .toLowerCase()
to these lines like so:
var value = $(element).val().toLowerCase();
if ($(this).text().toLowerCase().search(value) > -1)
Alternatively for a more concise version based on what Marek Tihkan posted you could replace the each() loop with the following. Not sure whether this would perform better or worse.
$('#theList > li:not(:contains(' + value + '))').hide();
$('#theList > li:contains(' + value + ')').show();
The solution given by Nikolas combined with the one of Marek rise an error if the input text is empty.
The solution below corrects that and works for lists surrounded by an 'a' tag.
The function is also designed to filter elements with words having their first letter uppercased (as for example with names). The filtering is thus ordered. If you type 'An' or 'an' then you will get all the elements in the list starting by those letters (e.g. Anthony will match but Fanny no).
function filter (element,what) {
var value = $(element).val();
value = value.toLowerCase().replace(/\b[a-z]/g, function(letter) {
return letter.toUpperCase();
});
if (value == '') {
$(what+' > a > li').show();
}
else {
$(what + ' > a > li:not(:contains(' + value + '))').hide();
$(what + ' > a > li:contains(' + value + ')').show();
}
};
Below is a sample HTML code that works with the script:
<input type="text" onkeyup="filter(this,'theList')" />
<ul id="theList">
<li><a href="">Tyrone Schlecht</a></li>
<li><a href="">Javier Ress</a></li>
<li><a href="">Carlene Tomes</a></li>
<li><a href="">Neil Aigner</a></li>
<li><a href="">Nita Schreffler</a></li>
<li><a href="">Clinton Knuckles</a></li>
<li><a href="">Eve Kellett</a></li>
<li><a href="">Jamie Kaspar</a></li>
<li><a href="">Emilia Hooton</a></li>
<li><a href="">Kenya Sidney</a></li>
</ul>
I did it by iterating all of them and hide those witch didn't match and showed those which matched.
$('li').hide();
$('li:contains(' + needle + ')').show();
You can use LiveQuery which was ported from php to jQuery by John Resig.
Note: It has a dependency on Quicksilver's score
method in PHP method, which has been ported to JavaScript by LiquidMetal.score
and joshaven's string.score
Usage Example:
$("#text_box_selector").liveUpdate("#list_selector");
Note: #list_selector
must find an element that contains li
elements
Plugin + Sort + Live Demo
// https://github.com/joshaven/string_score
String.prototype.score = function (word, fuzziness) {
'use strict';
// If the string is equal to the word, perfect match.
if (this === word) { return 1; }
//if it's not a perfect match and is empty return 0
if (word === "") { return 0; }
var runningScore = 0,
charScore,
finalScore,
string = this,
lString = string.toLowerCase(),
strLength = string.length,
lWord = word.toLowerCase(),
wordLength = word.length,
idxOf,
startAt = 0,
fuzzies = 1,
fuzzyFactor,
i;
// Cache fuzzyFactor for speed increase
if (fuzziness) { fuzzyFactor = 1 - fuzziness; }
// Walk through word and add up scores.
// Code duplication occurs to prevent checking fuzziness inside for loop
if (fuzziness) {
for (i = 0; i < wordLength; i+=1) {
// Find next first case-insensitive match of a character.
idxOf = lString.indexOf(lWord[i], startAt);
if (idxOf === -1) {
fuzzies += fuzzyFactor;
} else {
if (startAt === idxOf) {
// Consecutive letter & start-of-string Bonus
charScore = 0.7;
} else {
charScore = 0.1;
// Acronym Bonus
// Weighing Logic: Typing the first character of an acronym is as if you
// preceded it with two perfect character matches.
if (string[idxOf - 1] === ' ') { charScore += 0.8; }
}
// Same case bonus.
if (string[idxOf] === word[i]) { charScore += 0.1; }
// Update scores and startAt position for next round of indexOf
runningScore += charScore;
startAt = idxOf + 1;
}
}
} else {
for (i = 0; i < wordLength; i+=1) {
idxOf = lString.indexOf(lWord[i], startAt);
if (-1 === idxOf) { return 0; }
if (startAt === idxOf) {
charScore = 0.7;
} else {
charScore = 0.1;
if (string[idxOf - 1] === ' ') { charScore += 0.8; }
}
if (string[idxOf] === word[i]) { charScore += 0.1; }
runningScore += charScore;
startAt = idxOf + 1;
}
}
// Reduce penalty for longer strings.
finalScore = 0.5 * (runningScore / strLength + runningScore / wordLength) / fuzzies;
if ((lWord[0] === lString[0]) && (finalScore < 0.85)) {
finalScore += 0.15;
}
return finalScore;
};
// http://ejohn.org/apps/livesearch/jquery.livesearch.js
jQuery.fn.liveUpdate = function(list) {
list = jQuery(list);
if (list.length) {
var rows = list.children('li'),
cache = rows.map(function() {
return this.innerHTML.toLowerCase();
});
this
.keyup(filter).keyup()
.parents('form').submit(function() {
return false;
});
}
return this;
function filter() {
var term = jQuery.trim(jQuery(this).val().toLowerCase()),
scores = [];
if (!term) {
rows.show();
} else {
rows.hide();
cache.each(function(i) {
var score = this.score(term);
if (score > 0) {
scores.push([score, i]);
}
});
jQuery.each(scores.sort(function(a, b) {
return b[0] - a[0];
}), function() {
jQuery(rows[this[1]]).show();
});
}
}
};
$("#search").liveUpdate("#colors");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input type="text" id="search"><br/>
<ul id="colors">
<li>Cat Book</li>
<li>Dog Basket</li>
<li>Bear Cub</li>
<li>Car Door</li>
<li>Another option</li>
<li>Another Animal</li>
</ul>
// Just a shorter version
$('ul>li').hide().has(':contains(' + needle + ')').show();
// case insensitive searching with animation
$("ul>li").slideUp().filter( function() {
return $(this).text().toLowerCase().indexOf(needle) > -1
}).stop(true).fadeIn();
Taking main inspiration from this W3 example, I developed a possible alternative and compact solution.
In the example linked is possible to see 3 different options:
- Filter only UL items
- Filter any item
- Filter any item with css animated effect
The simplest JS code is the following, that simply filter any kind of element thanks to $("#anyItemList *")
selector:
$("#anyItemListInputFilter").on("keyup", function() {
var value = $(this).val().toLowerCase();
$("#anyItemList *").filter(function() {
let item = $(this).text().toLowerCase().indexOf(value) > -1;
$(this).toggle(item);
});
});
In case that desired filtering would be only for UL
list, that selector could changed with $("#ulList li")
(as in example)
If you want also to add css animation effect, there are some constraints:
- Needed a pre-defined
max-height
inpx
(maybe not so much impacting if you set an enough taller one) overflow-y:hidden;
So I was forced to declare:
#anyItemAnimatedList * {
transition:all 0.5s ease;
opacity:1;
max-height:500px;
overflow-y:hidden;
}
#anyItemAnimatedList ul {
list-style-position:inside;
}
#anyItemAnimatedList .hidden {
max-height:0;
opacity:0;
border:0;
}
Hiding effect is made with a combination of opacity
and max-height
css transitions, and border:0
is necessary to override button
tag default browser style.
In addition, in case of OL
and UL
lists is necessary to set list-style-position:inside;
due to a strange default behaviour shared by Firefox and Chrome that hide bullets in case of default list-style-position:outside
.
For Firefox this is a known bug since 2003!!! but also Chrome has the same behaviour.
精彩评论