Autocomplete

8 Апрель 2008

Prehistory


First of all, I want to say, that this plugin was developed for KyivStar, therefore all fine details were studied and stipulated very carefully. For example, what should occur, when the user would reach last element of the list: whether it should rest against the end or begin a cycle all over again. Or whether the list should disappear when the cursor is out of it. All this passed through business analysts’ and designer and only then came to me.

On the other hand I tried to make autocomplete multifunctional (multipurpose) and as simple as possible for any person to use it on ones site. Remarkable is that even with switched off javascript the site would not lose functionality, and visitor can enter data anyway. Autocomplete was not made as a filtrator of data entered by the user but the tool which helps the user to fill the form more quickly.

Well-known plugin jquery.suggest was taken as an example. Pieces of which can be identified in some methods. In spite of the fact that I took mechanism, I have added some new functions and have removed some errors. So in last version jquery.suggest the code became more readable thanks to the removal of one check. But at the end of the list the cursor (illumination) disappears for one position and then again appears at the first element of the list. It happens because jquery (in traversing) always return itself to the object and doesn’t give value to quantity chosen nodes. Another authors fault that he persistently tries to apply $.fn.bgiframe to the list <ul/>, This invention is certainly noble, but I couldn’t deal with this on practice.

Functions that were added: generation of the list from select-box, the limited visible part of the list with scrolling and prefilling of the list for it to look like a select-box. One more events handling onKeyPress. Well-known algorithm of caching MRU was deleted and replaced with my cache algorithm, this takes a lot of memory but it very fast!



Downloads


Last update 14.09.2009!


HTML file with examples (autocomplete.html)
Plugin itself (jquery.autocomplete.js)
Styles (autocomplete.css)
Little blue arrow (select.gif)
Progress bar backgraund image (progress.gif)
Php backend (search.phps)

Copy all this files to your own local web-server in root directory, rename search.phps to search.php and run http://path.to.your.server/autocomplete.html.



Examples


Example 1: Minimal requirements

To launch plugin you just need to put in one parameter, the url of php script which generats a list of words. Format of the list can be either xml or json.


$("#autocomplete1").autocomplete({
	url:'search.php'
});

Example 2: onSelect event

Plugin has four public events. First of them is onSelect, it fires where you pick element from the list. You can activate it by left clicking or pressing 'enter' button on the selected item. In this example I use one second delay before searching, and string must be at least 2 characters. The ansver from server will be in xml format so you can omit type property.


$("#autocomplete2").autocomplete({
	url:'search.php?type=xml',
	minchar:2,
	delay:1000, // in milliseconds
	onSelect:function(){
		alert(this.ac.val())
	},
	type:'xml'
});

Example 3: onKeyPress

onKeyPress event fires when you are typing. It can be used to filtrate user input. In this example you can use only alphabetical characters and white space, all other characters will be deleted. The fillin flag will fill in list before user start typing, because of input string is empty the request to the server will also contains empty mask value (i.e. search.php?type=json&mask=). In this case you will see list of one item with text "The list is empty!". The ansver from server will be in json format and you must set type property to 'json'.


$("#autocomplete3").autocomplete({
	url:'search.php?type=json',
	onKeyPress:function(){
		var o=this;
		setTimeout(function(){
			o.ac.val(o.ac.val().replace(/[^a-z ]+/g,""))
		},50)
	},
	fillin:true,
	type:'json'
});

Example 4: onError event

onError and onSuccess is the last two events, onError fires when AJAX has a problems otherwise onSuccess fires when you get answer from server. For example onError fires where you request for non-existing page. Also its an example of how to disable autocomplete. onSuccess get the same parametrs and scope.


$("#autocomplete4").autocomplete({
	url:'non_exist.php',
	onError:function(XMLHttpRequest, textStatus, errorThrown){
		this.ac.val(textStatus);
		this.ac.attr({disabled:"disabled"}).css({'background-color':'#d0d0d0'});
		this.ul.hide();
		this.img.unbind("click");
	}
});

Example 5: generation list from <select>

All previous examples generats autocomplete from AJAX but you also can do this from html code of current page. You can replace existing select-boxes with autocomplete by using source instead of url. You can pass in plugin either string with selectors like '#select.class' or jQuery object $('#select.class'). Pre-filling and digit-only filtrating is enable.


$("#autocomplete5").autocomplete({
	source:"#select",
		onKeyPress:function(){
		var o=this;
		setTimeout(function(){
			o.ac.val(o.ac.val().replace(/[^0-9]+/g,""))
		},50)
	},
	fillin:true
});

Example 6: generation list from array

If you have no need to use AJAX or have no select-box on your page, you can generate autocomplete from array. Sorry but nested arrays are not supported so you can not use [{a:'a'},{b:'b'}]


$("#autocomplete6").autocomplete({
	source:['a','b','c','d','e'],
	fillin:true
});

Example 7: generation list from object

The same as above. Sorry but nested objects are not supported so you can not use [{a:'a'},{b:'b'}] or {a:{b:c}}


$("#autocomplete7").autocomplete({
	source:{a:'a',b:'b',c:'c',d:'d',e:'e'},
	fillin:true
});

Example 8: list position

Example of list position at the top of input box


$("#autocomplete8").autocomplete({
	url:'search.php',
	top:true
});

Example 9: real values

Example of real values in non writeable fields


$("#autocomplete9").autocomplete({
	url:'search.php',
	values : true,
	writable : false,
	onSelect:function(){
		alert(this.pairs[this.ac.val()]);
	}
});

Example 10: alternative setup

By onSetup event you can change all properties exept 'width' or change behaviour of elements after autocomplete created. This example demonstrate smooth animation to options list.


$("#autocomplete10").autocomplete({
	onSetup:function(){
		var self = this;
		self.url = 'search.php';
		self.img.unbind("click")
			.bind("click", function() {
				clearTimeout(self.close);
				self.scroll();
				self.ul.slideToggle("slow")
				self.ac.focus();
			});
	}
});

Example 11: progress animation

Example of real usage with event hndling and animation


$("#autocomplete11").autocomplete({
	url:'/content/polygon/search.php?type=json',
	onSuggest:function(){
		this.ac.css({'background-image': 'url("/content/source/autocomplete/progress.gif")'});
	},
	onError:function(XMLHttpRequest, textStatus, errorThrown){
		this.ac.val(textStatus);
		this.ac.attr({disabled:"disabled"}).css({'background-color':'#d0d0d0','background-image':'none'});
		this.ul.hide();
		this.img.unbind("click");
	},
	onDisplay:function(list){
		this.ac.css({'background-image':'none'});
		if (!list)
		this.ul.append("<div style='line-height:100px;text-decoration:underline;text-align:center;'>[Empty list...]</div>");
	},
	minchar:2,
	type:'json'
});

Example 12: custom data format

Example of how to use custom data format


$("#autocomplete12").autocomplete({
    source : [{a:'a'},{b:'b'},{c:'c'},{d:'d'},{e:'e'}],
    fillin : true,
    dataHandler : function(mask){
        var self = this;
        return function(i, n) {
        for (var key in n){
            self.cache[mask].push(n[key]);
        }
        self.store[mask] += self.mark(n[key],mask);
        if(self.values && !self.pairs[n[key]])
            self.pairs[n[key]] = key;
        };
    }
});

Example 13: cascading selectbox

Example of how create selectbox wich depends on another input field


$("#autocomplete13").autocomplete({
    url:function(self){
        var state = $("#c13").attr("checked");
        if (self.ac.data("c13") != state){
            self.ac.data("c13", state);
            self.cache = {};
            self.store = {};
            self.pairs = {};
        }
        return 'search.php?c13='+state;
    }
});

Example 14: autocomplete form selectbox

Example of how to customize selectbox with plug-in


$("select#autocomplete14").autocomplete();

Notice

There is no simple way to re-render autocomplete created from select-box or array (object). You just need to re-create it.


Donation

If you like this plugin please vote for it on jquery site


Thanks to

  • Damir
  • Connected
  • Joey
  • Pasha
  • Morleydots
  • Gianfrasoft
  • Andrea
  • Ztalker
  • WooYek
  • Naden
  • Andrea Riciputi


Advanced options

Properties

  • url - [String|Function] ajax url
  • source - [String|jQuery|Array|Object] css selector or raw html, jQuery object, array [], object {}
  • minchar - [Integer] The minimum number of characters a user has to type before the autocompleter activates (default 1)
  • delay - [Integer] The delay in milliseconds the autocompleter waits after a keystroke to activate itself (default 50 miliseconds)
  • fillin - [Boolean] Pre-fill in (default false)
  • width - [Integer] width of autocomplete (default 200)
  • type - [String] type of server responce xml or json (default xml)
  • top - [Boolean] position of the list (default false to the bottom)
  • writable - [Boolean] allows to user write his own option (default true)
  • values - [Boolean] keep values for options (default false)
  • partial - [Boolean] makes request to server after each new letter (default false)

Events

  • onKeyPress - fires when user press a button
  • onSetup - fires once when autocomplit created
  • onSuggest - fires after onKeyPress but before ajax request send
  • onSuccess - fires when AJAX retuns result
  • onError - fires when AJAX retuns error
  • onDisplay - fires before list of items shows
  • onSelect - fires when user picks up from list

Dependences

  • jQuery - requires jQuery 1.2.6 or higher
  • bgiframe - use bgiframe if installed
  1. 30 Апрель 2009 в 10:57 | #1
    русские буквы в урлах это ваще моветон
  2. 30 Апрель 2009 в 13:33 | #2
    I am trying to get autocomplete working with such JSON:
    
    [
    {"test 1": "agV0YmRwbHIWCxIQdG9iZWRvbmVfcHJvamVjdBgMDA"}, 
    {"test 2": "agV0YmRwbHIWCxIQdG9iZWRvbmVfcHJvamVjdBgSDA"}
    ]
    
    I jumping through hoops to figure out what's wrong, maybe the format is wrong? Thx. WooYek.
  3. 30 Апрель 2009 в 13:48 | #3
    Example 7 says that you cannt use such arrays BUT you can override method 'prepare'
  4. 30 Апрель 2009 в 18:00 | #4
    Oh, thank's. I've noticed the nested objects, but must have missed the arrays part. Have someone done it - the array support?
  5. 1 Май 2009 в 14:46 | #5
    try to replace lines 258-274 with something like
    
    $.each(xml, function(i, n) {
        for (var key in n){
            map.push(n[key]);
            list.push(self.mark(n[key],mask));
            if(self.values && !self.pairs[n[key]])
                self.pairs[n[key]] = key;
        }
    });
    
    in case you won't use ajax or 'select' to create autocomplete
  6. 6 Май 2009 в 08:33 | #6
    Thank you ... this tutorial has me very helped.
  7. ztalker
    7 Май 2009 в 04:07 | #7
    Еще былобы хорошо чтобы использование кнопки TAB было опционально. Так как в большинстве случаев при нажатии TAB юрез ожидает перехода в следующее поле. Пока пришлось грубо исправить в самом автокомплите.
  8. 7 Май 2009 в 07:12 | #8
    угу, ты не первый
  9. ztalker
    14 Май 2009 в 10:20 | #9
    Еще предложение: по щелчку на стрелочку - выводить полный список элементов, независимо от вписанных значений, это более логично (или хотябы сделать опционально). Пока реализовал изменив метод "suggest". + при при этом проматывать до текущего выбранного элемента. Это у себя пока не смог сходу сделать =(
  10. 14 Май 2009 в 10:28 | #10
    нет это я делать не буду
  11. 4 Август 2009 в 16:55 | #11
    I tried about 5 different jQuery lib's until I found this one. It's simply the best and does exactly what I was looking for. I did not had to change a single line. Small Suggestion: apply "cursor: pointer;" to ".ac_img" to have a better look and feel and maybe an optional hover effect for the image.
  12. 4 Август 2009 в 17:01 | #12
    @naden thank you for reply. I will include your small suggestion in next release :) PS nice blog
  13. Andrea
    19 Август 2009 в 13:35 | #13
    Hi, I was trying out your plugin but I have a problem with all the examples on this page. When I click on the down-arrow on the right side of the field the resulting dropdown menu is always empty. No matter if I try out the AJAX or "local" examples. Any suggestion? I tried to run the demos with both Firefox 3.5.2 and Safari 4.0.2. Hope you can help me. TIA, Andrea
  14. 19 Август 2009 в 13:42 | #14
    thx for report. i will look at plugin tonight.
  15. 4 Сентябрь 2009 в 15:34 | #15
    i have test it ,if the options is more than 200,when it excute the autocomplete function,the browser cost cpu much... it haven't meet my need...though its function is good... hope to improve
  16. 4 Сентябрь 2009 в 16:02 | #16
    @L well. lets be honest. my thoughts about caching algorithm was described here but in russian http://mabp.kiev.ua/2008/05/08/autocache/ you can use google translate ;) also if you can (and want) help me to improve caching i will be very happy.
  17. 23 Октябрь 2009 в 13:13 | #17
    Hi ! First at all...thanks for sharing it... nice coding style...! I would like to help...and I think I have found a small bug... bug: in "check: function (mask)" saids "if (it && !mask.indexOf(it.toLowerCase()))" fix: "if (it && mask && !it.indexOf(mask.toLowerCase()))" Regards
  18. Den
    19 Ноябрь 2009 в 17:53 | #18
    hi. I've tryed to use your control no my website. It looks very nice. But I found a little problem in markup. When I use firefox the results window SOMETIMES renders in incorrect place. Sometimes it's in left top corner of the screen, sometimes far at the bottom. But the same page sorks perfect in ie. do you have any ideas about this? TIA
  19. 19 Ноябрь 2009 в 19:10 | #19
    @Francisco "mask" is a value of your input, the "check" function is called when you check at least one character at input. so its impossible to have empty mask. dont you think so?
  20. 19 Ноябрь 2009 в 19:10 | #20
    @Den can you show me your page?
  21. Den
    20 Ноябрь 2009 в 11:51 | #21
    the site is in development state. I've created a new account for you username : CTAPbIu_MABP password: qwerty In combobox at the top choose a customer "appolo", then follow to the Groups page. Select any group except "No group" or create a new one if you want. Then press Add button below the user grid
  22. Den
    20 Ноябрь 2009 в 11:54 | #22
    NOTE: Don't foget that the bug occures if you use firefox only
  23. 20 Ноябрь 2009 в 12:30 | #23
    @CTAPbIu_MABP
    CTAPbIu_MABP : @Francisco «mask» is a value of your input, the check function is called when you ензув at least one character at input. so its impossible to have empty mask. dont you think so?
    Yes it is correct !. The important is change !it.indexOf(mask.toLowerCase()) instead of !mask.indexOf(it.toLowerCase()) (I included all to make it clear), regards
  24. 20 Ноябрь 2009 в 16:35 | #24
    @Francisco i see :) you miss one little detail if you type word "ab" at first letter this.cache = {a:[...]} and at second you trying to check "a".indexOf("ab"). you will have nothing so you should check "ab".indexOf("a") and get array to filter. do we understand each other?
  25. 20 Ноябрь 2009 в 16:41 | #25
    @Den i don't see any link to a site :) and what version of FF do you use 2, 3, 3.5?
  26. Den
    20 Ноябрь 2009 в 16:45 | #26
    @CTAPbIu_MABP Ohh.... So silly (((( here is a link: http://deskopy.caddiesoft.com/ I use ff v 3.5
  27. 20 Ноябрь 2009 в 17:20 | #27
    ok, i have reproduce your bug. i dont have much time to test it but i think that i know the answer you create AC in a hidden element, i know that all hidden elements have elem.offsetHeight/Width === 0 . When AC set coordinates to list it uses formula "container.offset().top + height + border". May be this offset is also equal to 0 when element is hidden. So you can create AC or trigger window "resize" event ($(window).trigger("resize")) after container gets visible
  28. 20 Ноябрь 2009 в 18:55 | #28
    CTAPbIu_MABP : @Francisco i see :) you miss one little detail if you type word «ab» at first letter this.cache = {a:[...]} and at second you trying to check «a».indexOf(»ab»). you will have nothing so you should check «ab».indexOf(»a») and get array to filter. do we understand each other?
    That's correct, but think in you are using a real query service in the server (not a simple array like the search.php that you include)...which behavior implicate that the results depends on the mask and one proximity algorithm, and addtionaly you want to match the mask in any position of strings (in the server results)...then in this case my code change works better for the user experience, because in this case when the server does not return result (for "ab") is very likely that «a».indexOf(»ab») return a good result for the user (I tested it) Regards
  29. 20 Ноябрь 2009 в 19:17 | #29
    ohhhh... ok, you have a point. But x.indexOf(y) [in line 240] will not solve the problem. I have another 2 place in code where i check whether words in the list begins with mask, [this are lines 250-255 and 304-305] or not. you made me think... i need some time for tests with different proximity algorithms and cache algorithms
  30. 20 Ноябрь 2009 в 21:23 | #30
    CTAPbIu_MABP : ohhhh… ok, you have a point. But x.indexOf(y) [in line 240] will not solve the problem. I have another 2 place in code where i check whether words in the list begins with mask, [this are lines 250-255 and 304-305] or not. you made me think… i need some time for tests with different proximity algorithms and cache algorithms
    Yes I know...I have ready working revision fixing all of that...is adapted for me case so include other modifications...if you provide me with you email I can send you it for your covenience...
  31. Nike
    7 Декабрь 2009 в 08:09 | #31
    Have been using your autocomplete plugin for a while, and now encountered a problem - i need to re-attach autocomplete to an element. The situation is that it caches old values when I do it, but I need to make something so that it acts like it was just attached. Any suggestions? P.S. The plugin is very good :) I like it :)
  32. 7 Декабрь 2009 в 11:22 | #32
    If you need just to clear cache you can do some thing like this
    
    $("#my_input").autocomplete({onSetup:function(){
        this.ac.bind("clearCache",this,function(e){
            var self = e.data; // autocomplete object
            self.cache = {};
            self.store = {};
            self.pairs = {};
        })
    }});
    
    $("#my_input").trigger("clearCache");
    
  33. ump
    19 Декабрь 2009 в 03:05 | #33
    Уже в комментах писали об этом, но вопрос остался без ответа автора... Как сделать так, чтобы слово находило не по первым символам, а в любом месте строки. Т.е. чтобы при вводе "use" находилось слово house ?
  34. 19 Декабрь 2009 в 13:31 | #34
    @ump We have discuss it with Francisco, plz read this carefully
  35. fabio
    18 Февраль 2010 в 11:42 | #35
    Sorry but i've an error with the second letter in input firebug give me this error.. self.cache[array[item]][word].toLowerCase is not a function i've implemented it with json and xml but it's the same..anyone have a solution?
  36. daykobold
    18 Февраль 2010 в 18:25 | #36
    Privet. Ludi skazhite kak sdelat sdes tak shtobi v spiske vihodilo ne 7 strok isnachalno a skazhem 20 ? THX.
  37. 20 Февраль 2010 в 11:10 | #37
    @daykobold надо в CSS добавить классу ac_results высоту, например 100px.
  38. 20 Февраль 2010 в 11:33 | #38
    @fabio please provide en example of setup script and data which cause an error
  39. daykobold
    22 Февраль 2010 в 19:18 | #39
    Privet vsem. Snaet li kto nibud kak mozhno etot script nastroit tak, shtobi poisk shel ne sleva, a po vsemu soderzhaniju? Naprimer est paru slov v array (v odnom kluche): "Nintendo N64 Basic Set Blau", v etom Scripte eto slovo proignoriruetsja t.k. ono nachinaetsja s ne "N64", a mne nado shtobi ono tozhe otobrasilos t.k. N64 predsutstvuet. THX. Cu.
  40. daykobold
    22 Февраль 2010 в 19:19 | #40
    daykobold : Privet vsem. Snaet li kto nibud kak mozhno etot script nastroit tak, shtobi poisk shel ne sleva, a po vsemu soderzhaniju? Naprimer est paru slov v array (v odnom kluche): "Nintendo N64 Basic Set Blau", v etom Scripte eto slovo proignoriruetsja t.k. ono nachinaetsja s ne "N64", a mne nado shtobi ono tozhe otobrasilos t.k. N64 predsutstvuet. @CTAPbIu_ spasibo. THX. Cu.
  41. 22 Февраль 2010 в 21:06 | #41
    смотри мой диалог с @Francisco
  42. daykobold
    23 Февраль 2010 в 12:03 | #42
    @CTAPbIu_ esli ja pravilno ponil to ti eshe rasmotrish etot sluchay ;=) ? I eshe nashel neobichniy bug, esli sanesti skazhem slovo n64 potom stiret ego i nabrat N64 (bolshimi bukvami), to bolshe ne vihodit okno suggest, cache kak ti vishe napisal tozhe vstroil ne pomoglo. V chem tut delo?
  43. daykobold
    23 Февраль 2010 в 13:35 | #43
    Po povodu Cacha - isvenjaus teper vrode pashet, stranizu nado bilo perestartanut ;=)
  44. Дмитрий
    18 Май 2010 в 14:33 | #44
    Здравствуйте, как можно в этом автокомплите сделать поиск по всей стоке, а не только по началу?
  45. Дмитрий
    19 Май 2010 в 15:06 | #45
    У Меня 2 автокомплита, один генерится из массиа (source), второй с аякс запросом. Почему в первом случае можно вводить значения любые.. которых нет в селекте, а второй блокирует ввод букв значений которых нету ???
  46. 25 Май 2010 в 11:14 | #46
    еще раз выкладываю как сделать поиск по всей строке
    
           check: function (mask){
               var self = this;
               if (self.cache[mask])
                   return true; // quick return
               if(self.partial)
                   return false;
               mask = mask.toLowerCase();
               for(var it in self.cache)
                   if (it && !it.toLowerCase().indexOf(mask))
                       return true;
               return false;
           },
    
           grab: function (mask){
               var self = this, map = [], array = [];
               if (self.cache[mask])
                   return self.cache[mask]; // quick return
               for(var it in self.cache)
                   array.push(it);
               array = array.reverse();
               mask = mask.toLowerCase();
               for(var item in array)
                   if(!array[item].toLowerCase().indexOf(mask)){
                       for(var word in self.cache[array[item]])
                           if
    (!self.cache[array[item]][word].toLowerCase().indexOf(mask))
                               map.push(self.cache[array[item]][word]);
                       break;
                   }
               return map;
           },
    
           mark : function(text, mask){
               return new RegExp(mask, 'ig').test(text) ?
                   '<div>' + text.replace(new RegExp(mask, 'ig'), function(mask) {
                       return '<span class="ac_match">' + mask + '</span>';
                   }) + '</div>' : '';
           },
    
  47. rembrant
    7 Ноябрь 2010 в 02:00 | #47
    Если я добавляю через javascript элемент в select, например window.document.getElementById("autocomplete14").options[0] = new Option("ля-ля", "ля"); как можно после этого корректно обновить автоселект??
  48. -=ALEX=-
    7 Ноябрь 2010 в 09:58 | #48
    А я могу как-то задать значение по умолчанию, чтобы оно сразу выводилось в верхней строчке?
  49. 7 Ноябрь 2010 в 11:36 | #49
    @rembrant насколько я помню - никак, разве что удалить его и сделать новый. @-=ALEX=- да надо на onDisplay повесить функцию которая подставит первый элемент в поле, так?
  50. 11 Ноябрь 2010 в 11:04 | #50
    я придумал как обновлять существующий автокомплит.
    
    $("input#id").autocomplete({
    	onSetup : function(){
    		var self = this;
    		self.ac.bind("refresh",null,function(){
    			self.suggest();
    		});
    	}
    });
    
    использовать
    
    $("input#id").trigger("refresh")
    
Страницы комментариев
Комментирование отключено.