I’m using the jQuery UI Autocomplete plug-in. Is there a way to highlight search character sequence in drop-down results?
For example, if I have “foo bar” as data and I type "foo" I’ll get “foo bar” in the drop-down, like this:
I’m using the jQuery UI Autocomplete plug-in. Is there a way to highlight search character sequence in drop-down results?
For example, if I have “foo bar” as data and I type "foo" I’ll get “foo bar” in the drop-down, like this:
Yes, you can if you monkey-patch autocomplete.
In the autocomplete widget included in v1.8rc3 of jQuery UI, the popup of suggestions is created in the _renderMenu function of the autocomplete widget. This function is defined like this:
_renderMenu: function( ul, items ) {
var self = this;
$.each( items, function( index, item ) {
self._renderItem( ul, item );
});
},
The _renderItem function is defined like this:
_renderItem: function( ul, item) {
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( "<a>" + item.label + "</a>" )
.appendTo( ul );
},
So what you need to do is replace that _renderItem fn with your own creation that produces the desired effect. This technique, redefining an internal function in a library, I have come to learn is called monkey-patching. Here's how I did it:
function monkeyPatchAutocomplete() {
// don't really need this, but in case I did, I could store it and chain
var oldFn = $.ui.autocomplete.prototype._renderItem;
$.ui.autocomplete.prototype._renderItem = function( ul, item) {
var re = new RegExp("^" + this.term) ;
var t = item.label.replace(re,"<span style='font-weight:bold;color:Blue;'>" +
this.term +
"</span>");
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( "<a>" + t + "</a>" )
.appendTo( ul );
};
}
Call that function once in $(document).ready(...)
.
Now, this is a hack, because:
there's a regexp obj created for every item rendered in the list. That regexp obj ought to be re-used for all items.
there's no css class used for the formatting of the completed part. It's an inline style.
This means if you had multiple autocompletes on the same page, they'd all get the same treatment. A css style would solve that.
...but it illustrates the main technique, and it works for your basic requirements.
updated working example: http://output.jsbin.com/qixaxinuhe
To preserve the case of the match strings, as opposed to using the case of the typed characters, use this line:
var t = item.label.replace(re,"<span style='font-weight:bold;color:Blue;'>" +
"$&" +
"</span>");
In other words, starting from the original code above, you just need to replace this.term
with "$&"
.
EDIT
The above changes every autocomplete widget on the page. If you want to change only one, see this question:
How to patch *just one* instance of Autocomplete on a page?
item.label
and call the built in function passing the modified item object? –
Robtrobust var re = new RegExp(this.term, "i");
Great post! –
Vilhelmina padding:0;
in my CSS setup. I also removed the "^"
in the regex to allow searching inside the labels. –
Cowherd this also works:
$.ui.autocomplete.prototype._renderItem = function (ul, item) {
item.label = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(this.term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
return $("<li></li>")
.data("item.autocomplete", item)
.append("<a>" + item.label + "</a>")
.appendTo(ul);
};
a combination of @Jörn Zaefferer and @Cheeso's responses.
$().autocomplete()
–
Ba Super helpful. Thank you. +1.
Here is a light version that sorts on "String must begin with the term":
function hackAutocomplete(){
$.extend($.ui.autocomplete, {
filter: function(array, term){
var matcher = new RegExp("^" + term, "i");
return $.grep(array, function(value){
return matcher.test(value.label || value.value || value);
});
}
});
}
hackAutocomplete();
Here it goes, a functional full example:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Autocomplete - jQuery</title>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css">
</head>
<body>
<form id="form1" name="form1" method="post" action="">
<label for="search"></label>
<input type="text" name="search" id="search" />
</form>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.2/jquery-ui.js"></script>
<script>
$(function(){
$.ui.autocomplete.prototype._renderItem = function (ul, item) {
item.label = item.label.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(this.term) + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
return $("<li></li>")
.data("item.autocomplete", item)
.append("<a>" + item.label + "</a>")
.appendTo(ul);
};
var availableTags = [
"JavaScript",
"ActionScript",
"C++",
"Delphi",
"Cobol",
"Java",
"Ruby",
"Python",
"Perl",
"Groove",
"Lisp",
"Pascal",
"Assembly",
"Cliper",
];
$('#search').autocomplete({
source: availableTags,
minLength: 3
});
});
</script>
</body>
</html>
Hope this helps
jQueryUI 1.9.0 changes how _renderItem works.
The code below takes this change into consideration and also shows how I was doing highlight matching using Jörn Zaefferer's jQuery Autocomplete plugin. It will highlight all individual terms in the overall search term.
Since moving to using Knockout and jqAuto I found this a much easier way of styling the results.
function monkeyPatchAutocomplete() {
$.ui.autocomplete.prototype._renderItem = function (ul, item) {
// Escape any regex syntax inside this.term
var cleanTerm = this.term.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
// Build pipe separated string of terms to highlight
var keywords = $.trim(cleanTerm).replace(' ', ' ').split(' ').join('|');
// Get the new label text to use with matched terms wrapped
// in a span tag with a class to do the highlighting
var re = new RegExp("(" + keywords + ")", "gi");
var output = item.label.replace(re,
'<span class="ui-menu-item-highlight">$1</span>');
return $("<li>")
.append($("<a>").html(output))
.appendTo(ul);
};
};
$(function () {
monkeyPatchAutocomplete();
});
.jqAutocompleteMatch { font-weight: bold; }
–
Vilhelmina this.term
for regex should be used before doing any processing. See Escape string for use in Javascript regex as one of many answers to how to do this. –
Vilhelmina for an even easier way, try this:
$('ul: li: a[class=ui-corner-all]').each (function (){
//grab each text value
var text1 = $(this).text();
//grab user input from the search box
var val = $('#s').val()
//convert
re = new RegExp(val, "ig")
//match with the converted value
matchNew = text1.match(re);
//Find the reg expression, replace it with blue coloring/
text = text1.replace(matchNew, ("<span style='font-weight:bold;color:green;'>") + matchNew + ("</span>"));
$(this).html(text)
});
}
Here's a rehash of Ted de Koning's solution. It includes :
$.ui.autocomplete.prototype._renderItem = function (ul, item) {
var sNeedle = item.label;
var iTermLength = this.term.length;
var tStrPos = new Array(); //Positions of this.term in string
var iPointer = 0;
var sOutput = '';
//Change style here
var sPrefix = '<strong style="color:#3399FF">';
var sSuffix = '</strong>';
//Find all occurences positions
tTemp = item.label.toLowerCase().split(this.term.toLowerCase());
var CharCount = 0;
tTemp[-1] = '';
for(i=0;i<tTemp.length;i++){
CharCount += tTemp[i-1].length;
tStrPos[i] = CharCount + (i * iTermLength) + tTemp[i].length
}
//Apply style
i=0;
if(tStrPos.length > 0){
while(iPointer < sNeedle.length){
if(i<=tStrPos.length){
//Needle
if(iPointer == tStrPos[i]){
sOutput += sPrefix + sNeedle.substring(iPointer, iPointer + iTermLength) + sSuffix;
iPointer += iTermLength;
i++;
}
else{
sOutput += sNeedle.substring(iPointer, tStrPos[i]);
iPointer = tStrPos[i];
}
}
}
}
return $("<li></li>")
.data("item.autocomplete", item)
.append("<a>" + sOutput + "</a>")
.appendTo(ul);
};
Here is a version that does not require any regular expressions and matches multiple results in the label.
$.ui.autocomplete.prototype._renderItem = function (ul, item) {
var highlighted = item.label.split(this.term).join('<strong>' + this.term + '</strong>');
return $("<li></li>")
.data("item.autocomplete", item)
.append("<a>" + highlighted + "</a>")
.appendTo(ul);
};
Take a look at the combobox demo, it includes result highlighting: http://jqueryui.com/demos/autocomplete/#combobox
The regex in use there also deals with html results.
Here is my version:
function highlightText(text, $node) {
var searchText = $.trim(text).toLowerCase(),
currentNode = $node.get(0).firstChild,
matchIndex,
newTextNode,
newSpanNode;
while ((matchIndex = currentNode.data.toLowerCase().indexOf(searchText)) >= 0) {
newTextNode = currentNode.splitText(matchIndex);
currentNode = newTextNode.splitText(searchText.length);
newSpanNode = document.createElement("span");
newSpanNode.className = "highlight";
currentNode.parentNode.insertBefore(newSpanNode, currentNode);
newSpanNode.appendChild(newTextNode);
}
}
$("#autocomplete").autocomplete({
source: data
}).data("ui-autocomplete")._renderItem = function (ul, item) {
var $a = $("<a></a>").text(item.label);
highlightText(this.term, $a);
return $("<li></li>").append($a).appendTo(ul);
};
you can use folowing code:
lib:
$.widget("custom.highlightedautocomplete", $.ui.autocomplete, {
_renderItem: function (ul, item) {
var $li = $.ui.autocomplete.prototype._renderItem.call(this,ul,item);
//any manipulation with li
return $li;
}
});
and logic:
$('selector').highlightedautocomplete({...});
it creates custom widget that can override _renderItem
without overwriting _renderItem
of original plugin prototype.
in my example also used original render function to some simplify code
it's important thing if you want to use plugin in different places with different view of autocomplete and don't want to break your code.
If you instead use the 3rd party plugin, it has a highlight option: http://docs.jquery.com/Plugins/Autocomplete/autocomplete#url_or_dataoptions
(see the Options tab)
To support multiple values, just simply add following function:
function getLastTerm( term ) {
return split( term ).pop();
}
var t = String(item.value).replace(new RegExp(getLastTerm(this.term), "gi"), "<span class='ui-state-highlight'>$&</span>");
© 2022 - 2024 — McMap. All rights reserved.