├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── demo ├── data.json └── index.html ├── dist ├── remote-list.js └── remote-list.min.js ├── package.json ├── remote-list.jquery.json └── src └── remote-list.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.un~ 3 | *.tmp_*~ 4 | todo.txt 5 | _extends.css 6 | tests/performance.html 7 | dev-playground/* 8 | node_modules/* 9 | demos/demo-js/node_modules/* 10 | 11 | /tests/form-implementation.html 12 | /build/yuicompressor.jar 13 | /tests/tmp_tests.html 14 | .DS_Store 15 | .sass-cache 16 | .idea 17 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function(grunt){ 3 | 4 | 5 | // Project configuration. 6 | grunt.initConfig({ 7 | pkg: grunt.file.readJSON('package.json'), 8 | bower: grunt.file.readJSON('bower.json'), 9 | jq: grunt.file.readJSON('remote-list.jquery.json'), 10 | meta: { 11 | banner: '/*! v<%= pkg.version %> - ' + 12 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 13 | '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %> */' 14 | }, 15 | copy: { 16 | demo: { 17 | src: 'src/remote-list.js', 18 | dest: 'dist/remote-list.js' 19 | } 20 | }, 21 | 22 | uglify: { 23 | options: { 24 | beautify: { 25 | ascii_only : true 26 | }, 27 | preserveComments: 'some' 28 | }, 29 | demo: { 30 | src: 'dist/remote-list.js', 31 | dest: 'dist/remote-list.min.js' 32 | } 33 | }, 34 | watch: { 35 | js: { 36 | files: ['src/**/*.js'], 37 | tasks: ['copy', 'uglify', 'bytesize'] 38 | } 39 | }, 40 | bytesize: { 41 | all: { 42 | src: [ 43 | 'dist/*' 44 | ] 45 | } 46 | } 47 | }); 48 | 49 | 50 | // Default task. 51 | 52 | 53 | 54 | grunt.loadNpmTasks('grunt-contrib-copy'); 55 | grunt.loadNpmTasks('grunt-contrib-uglify'); 56 | grunt.loadNpmTasks('grunt-contrib-watch'); 57 | grunt.loadNpmTasks('grunt-bytesize'); 58 | 59 | grunt.registerTask('default', ['copy', 'uglify', 'bytesize', 'watch']); 60 | 61 | }; 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Alexander Farkas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Remote-List 2 | ## An ultra lightweight autosuggest / autocomplete plugin 3 | 4 | An ultra lightweight autocomplete / autosuggest plugin with a simple but powerful API. Leverages the HTML5 `datalist` element to build an extreme lightweight autosuggest plugin. Can use webshim to polyfill old browsers or enhance modern ones. 5 | 6 | * extreme lightweight (~1.5kb compressed/gzipped) 7 | * simple, intuitive API 8 | * includes powerful caching 9 | * performs server friendly AJAX requests (no multiple requests at once, no requests for older value hints) 10 | * allows customized rich markup content (not only value and label) * 11 | * allows different filtering or other behavior customization * 12 | 13 | *In conjunction with [webshims datalist polyfill](http://afarkas.github.io/webshim/demos/demos/cfgs/list-datalist.html) 14 | 15 | **[DEMO](http://afarkas.github.io/remote-list/demo/index.html)** 16 | 17 | ## Simple Usage example 18 | 19 | ``` 20 | 21 | 22 | 25 | ``` 26 | 27 | ## API 28 | 29 | ### Options 30 | The options can be set using the `jQuery.fn.remoteList()` method or by using the data-remote-list attribute on the given element. 31 | 32 | The `$.fn.remoteList` plugin has the following options: 33 | 34 | * minLength 35 | * maxLength 36 | * param 37 | * source 38 | * select 39 | * renderItem 40 | 41 | Examples: 42 | 43 | ``` 44 | $('input.autosuggest').remoteList({ 45 | minLength: 4, 46 | source: "my-ajax-service.json" 47 | }); 48 | 49 | //or 50 | 51 | 52 | 53 | //in case the data-remote-list is not a JSON object, but a simple string, it is considered as the `source` option 54 | 55 | 56 | ``` 57 | 58 | #### `minLength`: (Number default: 2) 59 | The minimum number of characters a user must type before a search is performed. Zero is useful for local data with just a few items, but a higher value should be used when a single character search could match a few thousand items. 60 | 61 | Example: 62 | 63 | ``` 64 | $('.autosuggest').remoteList({minLength: 3}); 65 | 66 | //or 67 | 68 | ``` 69 | 70 | #### `maxLength`: (Number default: -1) 71 | The maximum number of characters a new search should be tried. Otherwise the data from cache is used. If the user copies more characters at once into the input field and there is no cached data, the string will be shortened and then requested. 72 | 73 | In case of static data this number should be set to 0 so a search is only done once. 74 | 75 | ```js 76 | $('.autosuggest').remoteList({ 77 | minLength: 0, 78 | maxLength: 0, 79 | source: function(value, response){ 80 | response(['Options 1', 'Option 2', {value: "Option 3", label: "This option with a label"}]); 81 | } 82 | }); 83 | ``` 84 | 85 | #### `param`: (String default: name of the input or 'q') 86 | The name of the query parameter used for AJAX service. 87 | 88 | ```html 89 | 90 | 91 | ``` 92 | 93 | #### `source`: (mixed: String or Function) 94 | 95 | The data source for the suggestions. The data will requested with each user input as long as the user input satisfies the `minLength` and the `maxLength` option. 96 | 97 | **String**: When a string is used, the plugin expects that string to point to a URL resource that will return JSON (or JSONP) data. 98 | 99 | **Function**: The function callback offers much flexibility. It serves as a simple data provider. And should be used, if the request or response has to be modified. 100 | 101 | The function receives the following arguments: 102 | 103 | * value: The value the suggestions should be based on 104 | * response: A callback function which expects a single argument: the data to suggest to the user. 105 | * reset/fail: A callback function, which should be invoked, if the data couldn't be retrieved 106 | * request: An object with a single property with name of `param` and the `value`. 107 | * source: Either the source url or the function itself. 108 | 109 | 110 | ```js 111 | $('.autosuggest').remoteList({ 112 | minLength: 0, 113 | maxLength: 0, 114 | source: function(value, response){ 115 | response(['Options 1', 'Option 2', {value: "Option 3", label: "This option with a label"}]); 116 | } 117 | }); 118 | ``` 119 | 120 | The data provided by the either the `source URL` or the `source function` should be either an array of strings representing the values or an array of objects with the key `value` and an optional `label` key. 121 | 122 | #### select: (Function) 123 | 124 | A callback function, which is invoked, if the user selects an item of the suggestion list. The selected item can be retrieved with the method `selectedOption` and the selected data can be retrieved with method `selectedData`. 125 | 126 | ```js 127 | $('.autosuggest').remoteList({ 128 | minLength: 0, 129 | maxLength: 0, 130 | source: function(value, response){ 131 | response(['Options 1', 'Option 2', {value: "Option 3", label: "This option with a label"}]); 132 | }, 133 | select: function(){ 134 | if(window.console){ 135 | console.log('selectedOption:', $(this).remoteList('selectedOption')); 136 | console.log('selectedData:', $(this).remoteList('selectedData')); 137 | } 138 | } 139 | }); 140 | ``` 141 | 142 | As a more flexible solution, the `listselect` event can be bound to the input element. 143 | 144 | ```js 145 | $('.autosuggest').on('listselect', function(){ 146 | if(window.console){ 147 | console.log('selectedOption:', $(this).remoteList('selectedOption')); 148 | console.log('selectedData:', $(this).remoteList('selectedData')); 149 | } 150 | }); 151 | ``` 152 | 153 | #### renderItem: (Function) 154 | 155 | A callback function which can be used to enhance the rendered markup for a suggestion item. This works only in conjunction with [webshims](https://github.com/aFarkas/webshim). 156 | 157 | The callback function should return HTML markup for the given option receives the following arguments: 158 | 159 | * The value string, which should be used to represent the value for the suggestion 160 | * The label string (can be empty), which should be used to represent the label for the suggestion 161 | * The full data associated with the suggestion item 162 | 163 | ```js 164 | $('.autosuggest').remoteList({ 165 | minLength: 0, 166 | maxLength: 0, 167 | source: function(value, response){ 168 | var source = [ 169 | { 170 | img: "src/option-1.jpg", 171 | value: "Option 1" 172 | }, 173 | { 174 | img: "src/option-2.jpg", 175 | value: "Option 2" 176 | }, 177 | { 178 | img: "src/option-3.jpg", 179 | value: "Option 3" 180 | } 181 | ]; 182 | response(source); 183 | }, 184 | renderItem: function(value, label, data){ 185 | return ''+ value +' '+ label; 186 | } 187 | }); 188 | ``` 189 | 190 | ### Methods 191 | 192 | All methods can be invoked by passing the method name as a string to the `$.fn.remoteList` plugin. Additional parameters are passed as an array as the second parameter: 193 | 194 | ```js 195 | $('.autosuggest').remoteList('search', ['new yo']); 196 | ``` 197 | 198 | #### `selectedOption` 199 | 200 | Returns the first option element in the datalist, which has the same value, than the associated input element. If no option is found, it will return `null`. Normally this method is used in a `listselect` event listener or the `select` callback function. 201 | 202 | ```js 203 | var option = $('.autosuggest').remoteList('selectedOption'); 204 | ``` 205 | 206 | #### `selectedData` 207 | 208 | Returns data associated with the `selectedOption`. If no option/data is found, it will return `null`. Normally this method is used in a `listselect` event listener or the `select` callback function. 209 | 210 | ```js 211 | var data = $('.autosuggest').remoteList('selectedData'); 212 | ``` 213 | 214 | ```js 215 | $('.autosuggest').on('listselect', function(){ 216 | if(window.console){ 217 | console.log('selectedOption:', $(this).remoteList('selectedOption')); 218 | console.log('selectedData:', $(this).remoteList('selectedData')); 219 | } 220 | }); 221 | ``` 222 | 223 | #### `search`: (params: searchValue) 224 | 225 | Builds a new suggestion list for the given `searchValue`, if `minLength` and `maxLength` options are met. The `searchValue` parameter has to be wrapped into an array. 226 | 227 | ```js 228 | $('.autosuggest').remoteList('search', ['new yo']); 229 | ``` 230 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remote-list", 3 | "version": "0.1.3", 4 | "homepage": "https://github.com/aFarkas/remote-list", 5 | "authors": [ 6 | "aFarkas " 7 | ], 8 | "description": "A super-lightweight autocomplete / autosuggest plugin with a simple but powerfull API", 9 | "main": ["dist/remote-list.js"], 10 | "keywords": [ 11 | "ajax", 12 | "datalist", 13 | "list", 14 | "autosuggest", 15 | "autosuggestion", 16 | "autocomplete", 17 | "widget", 18 | "UI", 19 | "component" 20 | ], 21 | "license": "MIT", 22 | "ignore": [ 23 | "**/.*", 24 | "node_modules", 25 | "bower_components", 26 | "test", 27 | "tests", 28 | "build", 29 | "src", 30 | "demos", 31 | "Gruntfile.js", 32 | "webshims.jquery.json" 33 | ], 34 | "dependencies": { 35 | "jquery": ">= 1.8.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /demo/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "value": "Alabama" 4 | }, 5 | { 6 | "value": "Alaska", 7 | "label": "State of Alaska" 8 | }, 9 | { 10 | "value": "Arizona", 11 | "label": "State of Arizona" 12 | }, 13 | { 14 | "value": "Arkansas", 15 | "label": "State of Arkansas" 16 | }, 17 | { 18 | "value": "California", 19 | "label": "State of California" 20 | }, 21 | { 22 | "value": "Colorado", 23 | "label": "State of Colorado" 24 | }, 25 | { 26 | "value": "Connecticut", 27 | "label": "State of Connecticut" 28 | }, 29 | { 30 | "value": "Delaware", 31 | "label": "State of Delaware" 32 | }, 33 | { 34 | "value": "Florida", 35 | "label": "State of Florida" 36 | }, 37 | { 38 | "value": "Georgia", 39 | "label": "State of Georgia" 40 | }, 41 | { 42 | "value": "Hawaiʻi", 43 | "label": "State of Hawaiʻi" 44 | }, 45 | { 46 | "value": "Idaho", 47 | "label": "State of Idaho" 48 | }, 49 | { 50 | "value": "Illinois", 51 | "label": "State of Illinois" 52 | }, 53 | { 54 | "value": "Indiana", 55 | "label": "State of Indiana" 56 | }, 57 | { 58 | "value": "Iowa", 59 | "label": "State of Iowa" 60 | }, 61 | { 62 | "value": "Kansas", 63 | "label": "State of Kansas" 64 | }, 65 | { 66 | "value": "Kentucky", 67 | "label": "Commonwealth of Kentucky" 68 | }, 69 | { 70 | "value": "Louisiana", 71 | "label": "State of Louisiana" 72 | }, 73 | { 74 | "value": "Maine", 75 | "label": "State of Maine" 76 | }, 77 | { 78 | "value": "Maryland", 79 | "label": "State of Maryland" 80 | }, 81 | { 82 | "value": "Massachusetts", 83 | "label": "Commonwealth of Massachusetts" 84 | }, 85 | { 86 | "value": "Michigan", 87 | "label": "State of Michigan" 88 | }, 89 | { 90 | "value": "Minnesota", 91 | "label": "State of Minnesota" 92 | }, 93 | { 94 | "value": "Mississippi", 95 | "label": "State of Mississippi" 96 | }, 97 | { 98 | "value": "Missouri", 99 | "label": "State of Missouri" 100 | }, 101 | { 102 | "value": "Montana", 103 | "label": "State of Montana" 104 | }, 105 | { 106 | "value": "Nebraska", 107 | "label": "State of Nebraska" 108 | }, 109 | { 110 | "value": "Nevada", 111 | "label": "State of Nevada" 112 | }, 113 | { 114 | "value": "New Hampshire", 115 | "label": "State of New Hampshire" 116 | }, 117 | { 118 | "value": "New Jersey", 119 | "label": "State of New Jersey" 120 | }, 121 | { 122 | "value": "New Mexico", 123 | "label": "State of New Mexico" 124 | }, 125 | { 126 | "value": "New York", 127 | "label": "State of New York" 128 | }, 129 | { 130 | "value": "North Carolina", 131 | "label": "State of North Carolina" 132 | }, 133 | { 134 | "value": "North Dakota", 135 | "label": "State of North Dakota" 136 | }, 137 | { 138 | "value": "Ohio", 139 | "label": "State of Ohio" 140 | }, 141 | { 142 | "value": "Oklahoma", 143 | "label": "State of Oklahoma" 144 | }, 145 | { 146 | "value": "Oregon", 147 | "label": "State of Oregon" 148 | }, 149 | { 150 | "value": "Pennsylvania", 151 | "label": "Commonwealth of Pennsylvania" 152 | }, 153 | { 154 | "value": "Rhode Island", 155 | "label": "State of Rhode Island and Providence Plantations" 156 | }, 157 | { 158 | "value": "South Carolina", 159 | "label": "State of South Carolina" 160 | }, 161 | { 162 | "value": "South Dakota", 163 | "label": "State of South Dakota" 164 | }, 165 | { 166 | "value": "Tennessee", 167 | "label": "State of Tennessee" 168 | }, 169 | { 170 | "value": "Texas", 171 | "label": "State of Texas" 172 | }, 173 | { 174 | "value": "Utah", 175 | "label": "State of Utah" 176 | }, 177 | { 178 | "value": "Vermont", 179 | "label": "State of Vermont" 180 | }, 181 | { 182 | "value": "Virginia", 183 | "label": "Commonwealth of Virginia" 184 | }, 185 | { 186 | "value": "Washington", 187 | "label": "State of Washington" 188 | }, 189 | { 190 | "value": "West Virginia", 191 | "label": "State of West Virginia" 192 | }, 193 | { 194 | "value": "Wisconsin", 195 | "label": "State of Wisconsin" 196 | }, 197 | { 198 | "value": "Wyoming", 199 | "label": "State of Wyoming" 200 | } 201 | ] 202 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | lightweight remote autocomplete / autosuggest with HTML5 datalist 9 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |

Tiny lightweight autosuggest / typeahead plugin for jQuery

71 |

This example is build on top of the HTML5 datalist element and uses webshims to polyfill and enhance the user interface.

72 |

Download/Fork/Repository

73 |

Documentation and API

74 |
75 | 82 | 95 | 96 |

 97 | <input data-remote-list="data.json"
 98 |    data-list-highlight="true"
 99 |    data-list-value-completion="true"
100 |    class="static-remote" type="text" />
101 | 
102 | 103 |

104 | $('input.static-remote').remoteList({
105 | 	minLength: 0,
106 | 	maxLength: 0,
107 | 	select: function(){
108 | 		if(window.console){
109 | 			console.log($(this).remoteList('selectedOption'), $(this).remoteList('selectedData'))
110 | 		}
111 | 	}
112 | });
113 | 
114 |
115 | 116 |
117 | 118 |
119 | 125 | 154 | 155 |

156 | 	<input
157 | 	data-list-filter="!"
158 | 	class="geo-autocomplete" type="text" />
159 | 
160 | 161 |

162 | $('input.geo-autocomplete').remoteList({
163 | 	maxLength: 9,
164 | 	source: function(val, response){
165 | 		$.ajax({
166 | 			url: 'http://geo-autocomplete.com/api/country',
167 | 			dataType: 'jsonp',
168 | 			data: {
169 | 				q: val,
170 | 				key: '37693c',
171 | 				nl: true
172 | 			},
173 | 			success: function(data){
174 | 				$.each(data, function(i, item){
175 | 					item.value = item.country_name;
176 | 				});
177 | 				response(data);
178 | 			}
179 | 		});
180 | 	},
181 | 	select: function(){
182 | 		if(window.console){
183 | 			console.log($(this).remoteList('selectedOption'), $(this).remoteList('selectedData'))
184 | 		}
185 | 	}
186 | });
187 | 
188 |
189 | 190 | 198 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /dist/remote-list.js: -------------------------------------------------------------------------------- 1 | (function(undefined){ 2 | "use strict"; 3 | var id = 0; 4 | var $ = window.webshims && webshims.$ || window.jQuery || window.$; 5 | 6 | function RemoteList(element, options){ 7 | id++; 8 | this.element = $(element); 9 | this.options = $.extend({}, RemoteList.defaults, options); 10 | this.cache = {}; 11 | this.id = 'remotelist-'+id; 12 | 13 | if(!this.options.param){ 14 | this.options.param = this.element.prop('name') || 'q'; 15 | } 16 | 17 | this._createDatalist(); 18 | this._bindEvents(this); 19 | } 20 | 21 | RemoteList.defaults = { 22 | minLength: 2, 23 | maxLength: -1, 24 | source: '', 25 | param: '', 26 | select: $.noop, 27 | renderItem: null 28 | }; 29 | 30 | 31 | RemoteList.createOption = function(option){ 32 | var ret = $(document.createElement('option')); 33 | if(!option || typeof option == 'string'){ 34 | option = {value: option}; 35 | } 36 | ret.prop('value', option.value); 37 | if(option.label){ 38 | ret.prop('label', option.label); 39 | } 40 | ret.data('optionData', option); 41 | return ret[0]; 42 | }; 43 | 44 | RemoteList.prototype = { 45 | 46 | selectedData: function(){ 47 | var elem = this.selectedOption(); 48 | return elem && $(elem).data('optionData'); 49 | }, 50 | selectedOption: function(){ 51 | var selectedOption = null; 52 | var val = this.val(); 53 | if(val){ 54 | selectedOption = $('[value="'+ val +'"]', this.element.prop('list'))[0] || null; 55 | } 56 | return selectedOption; 57 | }, 58 | search: function(val){ 59 | var dataObj, source, response, reset; 60 | var that = this; 61 | var o = this.options; 62 | var cVal = val; 63 | 64 | if(o.maxLength > -1){ 65 | cVal = cVal.substr(o, o.maxLength); 66 | } 67 | 68 | 69 | this.doAjax = false; 70 | 71 | if(this.cache[cVal]){ 72 | this.setList(this.cache[cVal], cVal); 73 | } else if(!this.currentAjax){ 74 | this.element.addClass('list-search'); 75 | dataObj = {}; 76 | dataObj[o.param] = val; 77 | this.currentAjax = true; 78 | 79 | 80 | reset = function(){ 81 | if(reset.xhr && reset.xhr.abort){ 82 | reset.xhr.abort(); 83 | } 84 | that.currentAjax = false; 85 | 86 | clearTimeout(reset.timer); 87 | 88 | if(that.doAjax){ 89 | that.search(that.doAjax); 90 | } else { 91 | that.element.removeClass('list-search'); 92 | } 93 | }; 94 | 95 | source = $.isFunction(o.source) ? 96 | o.source : 97 | function(val, response, fail, dataObj, url){ 98 | return $.ajax({ 99 | dataType: 'json', 100 | url: url, 101 | data: dataObj, 102 | context: this, 103 | error: fail, 104 | success: response 105 | }); 106 | } 107 | ; 108 | 109 | 110 | response = function(data){ 111 | that.setList(data, cVal); 112 | reset(); 113 | }; 114 | 115 | reset.timer = setTimeout(reset, 999); 116 | reset.xhr = source(val, response, reset, dataObj, o.source); 117 | 118 | } else { 119 | this.doAjax = val; 120 | } 121 | }, 122 | setList: function(options, val){ 123 | if(!options){ 124 | options = []; 125 | } 126 | 127 | if(this.currentOptions != options && this.currentVal !== val){ 128 | this.currentOptions = options; 129 | this.currentVal = val; 130 | this.cache[val] = options; 131 | options = $.map(options, RemoteList.createOption); 132 | this.datalistSelect.html(options); 133 | if($.fn.updatePolyfill){ 134 | this.datalistSelect.updatePolyfill(); 135 | } 136 | } 137 | 138 | }, 139 | _createDatalist: function(){ 140 | this.datalistSelect = this.element.prop('list'); 141 | 142 | if(!this.datalistSelect){ 143 | this.datalistSelect = $(''),this.element.attr("list",this.id),this.element.after(this.datalistSelect)),this.datalistSelect=d("select",this.datalistSelect)},val:function(){return window.webshims&&webshims.getDataListVal?webshims.getDataListVal(this.element[0]):this.element.prop("value")},widget:function(){return this},_bindEvents:function(a){var b,c,e,f=a.options,g=function(){var c;return function(g){var h=a.val();if(h!==c&&(c=h,"change"==g||!e||e.toLowerCase()!=h.charAt(h.length-1).toLowerCase()))return a.selectedOption()?(clearTimeout(b),f.select&&f.select.call(a.element[0],d.Event("listselect")),a.element.trigger("listselect"),!0):void 0}}();a.element.on({"input focus":function(){var c=function(){var b=a.val();b.length>=f.minLength&&a.search(b)};return function(){clearTimeout(b),b=setTimeout(c,99)}}(),"input change":function(d){clearTimeout(c),"change"==d.type?(clearTimeout(b),a.element.is(":focus")&&g("change")):c=setTimeout(g,9)},keypress:function(){var a=function(){e=""};return function(b){e=String.fromCharCode(b.charCode),setTimeout(a,20)}}(),getoptioncontent:function(a,b){return f.renderItem?f.renderItem(''+b.item.value+"",b.item.label&&''+b.item.label+"",d.data(b.item.elem,"optionData")):void 0}})}},d.fn.remoteList=function(c,e){var f=c,g=this;return this.each(function(){var h,i,j=d(this).data("remoteList");if("string"==typeof j&&(i={source:j},j=!1),j&&d.isPlainObject(j)&&(i=j),j&&"string"==typeof f&&f in j){if(h=j[f]&&j[f].apply?j[f].apply(j,e||[]):j[f],h!==a)return g=h,!1}else d(this).data("remoteList",new b(this,d.extend({},c,i)))}),g},d.fn.remoteList.constructorFn=b}(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remote-list", 3 | "version": "0.1.3", 4 | "title": "A super-lightweight autocomplete / autosuggest plugin", 5 | 6 | "author": { 7 | "name": "Alexander Farkas", 8 | "url": "https://github.com/aFarkas/" 9 | }, 10 | "keywords": [ 11 | "webshims", 12 | "polyfill", 13 | "HTML5", 14 | "forms", 15 | "ajax", 16 | "datalist", 17 | "list", 18 | "autosuggest", 19 | "autosuggestion", 20 | "autocomplete", 21 | "typeahead", 22 | "widget", 23 | "UI", 24 | "component" 25 | ], 26 | "description": "A lightweight", 27 | "docs": "http://afarkas.github.com/webshim/demos/index.html", 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/aFarkas/remote-list.git" 31 | }, 32 | "main": "dist/", 33 | "devDependencies": { 34 | "grunt-bytesize": ">=0.1.0", 35 | "grunt-contrib-watch": ">=0.3.0", 36 | "grunt-contrib-copy": ">=0.4.0", 37 | "grunt-contrib-uglify": ">=0.2.7", 38 | "grunt": ">=0.4.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /remote-list.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remote-list", 3 | "version": "0.1.3", 4 | "title": "superlightweight autosuggest / autocomplete plugin", 5 | "licenses": [ 6 | { 7 | "type": "MIT", 8 | "url": "https://raw.github.com/aFarkas/webshim/master/MIT-LICENSE.txt" 9 | } 10 | ], 11 | "dependencies" : { 12 | "jquery" : "1.8.3 - 2.1.x" 13 | }, 14 | "author": { 15 | "name": "Alexander Farkas", 16 | "url": "https://github.com/aFarkas/" 17 | }, 18 | 19 | "keywords": [ 20 | "ajax", 21 | "datalist", 22 | "list", 23 | "autosuggest", 24 | "autosuggestion", 25 | "autocomplete", 26 | "typeahead", 27 | "widget", 28 | "UI", 29 | "component", 30 | "webshim", 31 | "HTML5" 32 | ], 33 | "description": "A super-lightweight autocomplete / autosuggest plugin with a simple but powerful API. Leverages the HTML5 datalist element to build a lightweight autosuggest solution. Can use webshim to polyfill old browsers or enhance modern ones.", 34 | "bugs": "https://github.com/aFarkas/remote-list/issues", 35 | "demo": "http://afarkas.github.io/remote-list/demo/index.html", 36 | "docs": "https://github.com/aFarkas/remote-list#simple-usage-example", 37 | "download": "https://raw.githubusercontent.com/aFarkas/remote-list/gh-pages/dist/remote-list.min.js", 38 | "repository": { 39 | "type" : "git", 40 | "url" : "https://github.com/aFarkas/remote-list.git" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/remote-list.js: -------------------------------------------------------------------------------- 1 | (function(undefined){ 2 | "use strict"; 3 | var id = 0; 4 | var $ = window.webshims && webshims.$ || window.jQuery || window.$; 5 | 6 | function RemoteList(element, options){ 7 | id++; 8 | this.element = $(element); 9 | this.options = $.extend({}, RemoteList.defaults, options); 10 | this.cache = {}; 11 | this.id = 'remotelist-'+id; 12 | 13 | if(!this.options.param){ 14 | this.options.param = this.element.prop('name') || 'q'; 15 | } 16 | 17 | this._createDatalist(); 18 | this._bindEvents(this); 19 | } 20 | 21 | RemoteList.defaults = { 22 | minLength: 2, 23 | maxLength: -1, 24 | source: '', 25 | param: '', 26 | select: $.noop, 27 | renderItem: null 28 | }; 29 | 30 | 31 | RemoteList.createOption = function(option){ 32 | var ret = $(document.createElement('option')); 33 | if(!option || typeof option == 'string'){ 34 | option = {value: option}; 35 | } 36 | ret.prop('value', option.value); 37 | if(option.label){ 38 | ret.prop('label', option.label); 39 | } 40 | ret.data('optionData', option); 41 | return ret[0]; 42 | }; 43 | 44 | RemoteList.prototype = { 45 | 46 | selectedData: function(){ 47 | var elem = this.selectedOption(); 48 | return elem && $(elem).data('optionData'); 49 | }, 50 | selectedOption: function(){ 51 | var selectedOption = null; 52 | var val = this.val(); 53 | if(val){ 54 | selectedOption = $('[value="'+ val +'"]', this.element.prop('list'))[0] || null; 55 | } 56 | return selectedOption; 57 | }, 58 | search: function(val){ 59 | var dataObj, source, response, reset; 60 | var that = this; 61 | var o = this.options; 62 | var cVal = val; 63 | 64 | if(o.maxLength > -1){ 65 | cVal = cVal.substr(o, o.maxLength); 66 | } 67 | 68 | 69 | this.doAjax = false; 70 | 71 | if(this.cache[cVal]){ 72 | this.setList(this.cache[cVal], cVal); 73 | } else if(!this.currentAjax){ 74 | this.element.addClass('list-search'); 75 | dataObj = {}; 76 | dataObj[o.param] = val; 77 | this.currentAjax = true; 78 | 79 | 80 | reset = function(){ 81 | if(reset.xhr && reset.xhr.abort){ 82 | reset.xhr.abort(); 83 | } 84 | that.currentAjax = false; 85 | 86 | clearTimeout(reset.timer); 87 | 88 | if(that.doAjax){ 89 | that.search(that.doAjax); 90 | } else { 91 | that.element.removeClass('list-search'); 92 | } 93 | }; 94 | 95 | source = $.isFunction(o.source) ? 96 | o.source : 97 | function(val, response, fail, dataObj, url){ 98 | return $.ajax({ 99 | dataType: 'json', 100 | url: url, 101 | data: dataObj, 102 | context: this, 103 | error: fail, 104 | success: response 105 | }); 106 | } 107 | ; 108 | 109 | 110 | response = function(data){ 111 | that.setList(data, cVal); 112 | reset(); 113 | }; 114 | 115 | reset.timer = setTimeout(reset, 999); 116 | reset.xhr = source(val, response, reset, dataObj, o.source); 117 | 118 | } else { 119 | this.doAjax = val; 120 | } 121 | }, 122 | setList: function(options, val){ 123 | if(!options){ 124 | options = []; 125 | } 126 | 127 | if(this.currentOptions != options && this.currentVal !== val){ 128 | this.currentOptions = options; 129 | this.currentVal = val; 130 | this.cache[val] = options; 131 | options = $.map(options, RemoteList.createOption); 132 | this.datalistSelect.html(options); 133 | if($.fn.updatePolyfill){ 134 | this.datalistSelect.updatePolyfill(); 135 | } 136 | } 137 | 138 | }, 139 | _createDatalist: function(){ 140 | this.datalistSelect = this.element.prop('list'); 141 | 142 | if(!this.datalistSelect){ 143 | this.datalistSelect = $('