public
Last active

Bootstrap's Typeahead plugin extended (allowing for AJAX functionality) among other things

  • Download Gist
README.markdown
Markdown

This is a fork of Bootstrap Typeahead that adds minimal but powerful extensions.

For example, process typeahead list asynchronously and return objects

  # This example does an AJAX lookup and is in CoffeeScript
  $('.typeahead').typeahead(
    # source can be a function
    source: (typeahead, query) ->
      # this function receives the typeahead object and the query string
      $.ajax(
        url: "/lookup/?q="+query
        # i'm binding the function here using CoffeeScript syntactic sugar,
        # you can use for example Underscore's bind function instead.
        success: (data) =>
          # data must be a list of either strings or objects
          # data = [{'name': 'Joe', }, {'name': 'Henry'}, ...]
          typeahead.process(data)
      )
    # if we return objects to typeahead.process we must specify the property
    # that typeahead uses to look up the display value
    property: "name"
  )

For example, process typeahead list synchronously and fire a callback on selection

  // This example is in Javascript, collects html in some li's and returns it
  $('.typeahead').typeahead({
    source: function (typeahead, query) {
      var return_list = []
      $("li").each(function(i,v){
        return_list.push($(v).html())
      })
      // here I'm just returning a list of strings
      return return_list
    },
    // typeahead calls this function when a object is selected, and
    // passes an object or string depending on what you processed, in this case a string
    onselect: function (obj) {
      alert('Selected '+obj)
    }

  })

and a very simple example, showing you can pass list of objects as source, and get that object via onselect

  $('.typeahead').typeahead({
    // note that "value" is the default setting for the property option
    source: [{value: 'Charlie'}, {value: 'Gudbergur'}, ...],
    onselect: function(obj) { console.log(obj) }
  })

Note that onselect works without source as a function and vice versa. Events may be a cleaner solution to passing callbacks and using bind all over the place, but I tried to strike a balance between modifying the core source too much and adding functionality, so until further improvements on the original Typeahead source I think these additions are very helpful.

Update 02/23/2012: Fixed a bug

Gudbergur Erlendsson, reach me here or gudbergur at gmail

bootstrap-typeahead.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
/* =============================================================
* bootstrap-typeahead.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#typeahead
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
 
!function( $ ){
 
"use strict"
 
var Typeahead = function ( element, options ) {
this.$element = $(element)
this.options = $.extend({}, $.fn.typeahead.defaults, options)
this.matcher = this.options.matcher || this.matcher
this.sorter = this.options.sorter || this.sorter
this.highlighter = this.options.highlighter || this.highlighter
this.$menu = $(this.options.menu).appendTo('body')
this.source = this.options.source
this.onselect = this.options.onselect
this.strings = true
this.shown = false
this.listen()
}
 
Typeahead.prototype = {
 
constructor: Typeahead
 
, select: function () {
var val = JSON.parse(this.$menu.find('.active').attr('data-value'))
, text
 
if (!this.strings) text = val[this.options.property]
else text = val
 
this.$element.val(text)
 
if (typeof this.onselect == "function")
this.onselect(val)
 
return this.hide()
}
 
, show: function () {
var pos = $.extend({}, this.$element.offset(), {
height: this.$element[0].offsetHeight
})
 
this.$menu.css({
top: pos.top + pos.height
, left: pos.left
})
 
this.$menu.show()
this.shown = true
return this
}
 
, hide: function () {
this.$menu.hide()
this.shown = false
return this
}
 
, lookup: function (event) {
var that = this
, items
, q
, value
 
this.query = this.$element.val()
 
if (typeof this.source == "function") {
value = this.source(this, this.query)
if (value) this.process(value)
} else {
this.process(this.source)
}
}
 
, process: function (results) {
var that = this
, items
, q
 
if (results.length && typeof results[0] != "string")
this.strings = false
 
this.query = this.$element.val()
 
if (!this.query) {
return this.shown ? this.hide() : this
}
 
items = $.grep(results, function (item) {
if (!that.strings)
item = item[that.options.property]
if (that.matcher(item)) return item
})
 
items = this.sorter(items)
 
if (!items.length) {
return this.shown ? this.hide() : this
}
 
return this.render(items.slice(0, this.options.items)).show()
}
 
, matcher: function (item) {
return ~item.toLowerCase().indexOf(this.query.toLowerCase())
}
 
, sorter: function (items) {
var beginswith = []
, caseSensitive = []
, caseInsensitive = []
, item
, sortby
 
while (item = items.shift()) {
if (this.strings) sortby = item
else sortby = item[this.options.property]
 
if (!sortby.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
else if (~sortby.indexOf(this.query)) caseSensitive.push(item)
else caseInsensitive.push(item)
}
 
return beginswith.concat(caseSensitive, caseInsensitive)
}
 
, highlighter: function (item) {
return item.replace(new RegExp('(' + this.query + ')', 'ig'), function ($1, match) {
return '<strong>' + match + '</strong>'
})
}
 
, render: function (items) {
var that = this
 
items = $(items).map(function (i, item) {
i = $(that.options.item).attr('data-value', JSON.stringify(item))
if (!that.strings)
item = item[that.options.property]
i.find('a').html(that.highlighter(item))
return i[0]
})
 
items.first().addClass('active')
this.$menu.html(items)
return this
}
 
, next: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, next = active.next()
 
if (!next.length) {
next = $(this.$menu.find('li')[0])
}
 
next.addClass('active')
}
 
, prev: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, prev = active.prev()
 
if (!prev.length) {
prev = this.$menu.find('li').last()
}
 
prev.addClass('active')
}
 
, listen: function () {
this.$element
.on('blur', $.proxy(this.blur, this))
.on('keypress', $.proxy(this.keypress, this))
.on('keyup', $.proxy(this.keyup, this))
 
if ($.browser.webkit || $.browser.msie) {
this.$element.on('keydown', $.proxy(this.keypress, this))
}
 
this.$menu
.on('click', $.proxy(this.click, this))
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
}
 
, keyup: function (e) {
e.stopPropagation()
e.preventDefault()
 
switch(e.keyCode) {
case 40: // down arrow
case 38: // up arrow
break
 
case 9: // tab
case 13: // enter
if (!this.shown) return
this.select()
break
 
case 27: // escape
this.hide()
break
 
default:
this.lookup()
}
 
}
 
, keypress: function (e) {
e.stopPropagation()
if (!this.shown) return
 
switch(e.keyCode) {
case 9: // tab
case 13: // enter
case 27: // escape
e.preventDefault()
break
 
case 38: // up arrow
e.preventDefault()
this.prev()
break
 
case 40: // down arrow
e.preventDefault()
this.next()
break
}
}
 
, blur: function (e) {
var that = this
e.stopPropagation()
e.preventDefault()
setTimeout(function () { that.hide() }, 150)
}
 
, click: function (e) {
e.stopPropagation()
e.preventDefault()
this.select()
}
 
, mouseenter: function (e) {
this.$menu.find('.active').removeClass('active')
$(e.currentTarget).addClass('active')
}
 
}
 
 
/* TYPEAHEAD PLUGIN DEFINITION
* =========================== */
 
$.fn.typeahead = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('typeahead')
, options = typeof option == 'object' && option
if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
if (typeof option == 'string') data[option]()
})
}
 
$.fn.typeahead.defaults = {
source: []
, items: 8
, menu: '<ul class="typeahead dropdown-menu"></ul>'
, item: '<li><a href="#"></a></li>'
, onselect: null
, property: 'value'
}
 
$.fn.typeahead.Constructor = Typeahead
 
 
/* TYPEAHEAD DATA-API
* ================== */
 
$(function () {
$('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
var $this = $(this)
if ($this.data('typeahead')) return
e.preventDefault()
$this.typeahead($this.data())
})
})
 
}( window.jQuery );
gistfile1.diff
Diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
/* =============================================================
* bootstrap-typeahead.js v2.0.0
* http://twitter.github.com/bootstrap/javascript.html#typeahead
* =============================================================
* Copyright 2012 Twitter, Inc.
@@ -29,6 +29,8 @@
this.highlighter = this.options.highlighter || this.highlighter
this.$menu = $(this.options.menu).appendTo('body')
this.source = this.options.source
+ this.onselect = this.options.onselect
+ this.strings = true
this.shown = false
this.listen()
}
@@ -38,8 +40,17 @@
constructor: Typeahead
, select: function () {
- var val = this.$menu.find('.active').attr('data-value')
- this.$element.val(val)
+ var val = JSON.parse(this.$menu.find('.active').attr('data-value'))
+ , text
+
+ if (!this.strings) text = val[this.options.property]
+ else text = val
+
+ this.$element.val(text)
+
+ if (typeof this.onselect == "function")
+ this.onselect(val)
+
return this.hide()
}
@@ -68,6 +79,25 @@
var that = this
, items
, q
+ , value
+
+ this.query = this.$element.val()
+
+ if (typeof this.source == "function")
+ value = this.source(this, this.query)
+ if (value)
+ this.process(value)
+ else
+ this.process(this.source)
+ }
+
+ , process: function (results) {
+ var that = this
+ , items
+ , q
+
+ if (results.length && typeof results[0] != "string")
+ this.strings = false
this.query = this.$element.val()
@@ -75,7 +105,9 @@
return this.shown ? this.hide() : this
}
- items = $.grep(this.source, function (item) {
+ items = $.grep(results, function (item) {
+ if (!that.strings)
+ item = item[that.options.property]
if (that.matcher(item)) return item
})
@@ -97,10 +129,14 @@
, caseSensitive = []
, caseInsensitive = []
, item
+ , sortby
while (item = items.shift()) {
- if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
- else if (~item.indexOf(this.query)) caseSensitive.push(item)
+ if (this.strings) sortby = item
+ else sortby = item[this.options.property]
+
+ if (!sortby.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
+ else if (~sortby.indexOf(this.query)) caseSensitive.push(item)
else caseInsensitive.push(item)
}
@@ -117,7 +153,9 @@
var that = this
items = $(items).map(function (i, item) {
- i = $(that.options.item).attr('data-value', item)
+ i = $(that.options.item).attr('data-value', JSON.stringify(item))
+ if (!that.strings)
+ item = item[that.options.property]
i.find('a').html(that.highlighter(item))
return i[0]
})
@@ -251,6 +289,8 @@
, items: 8
, menu: '<ul class="typeahead dropdown-menu"></ul>'
, item: '<li><a href="#"></a></li>'
+ , onselect: null
+ , property: 'value'
}
$.fn.typeahead.Constructor = Typeahead

Is there any way I can stop the first item from being automatically selected? At the moment the onselect event is fired when a user tabs out of the typeahead element. (posted this to a fork of the original by mistake originally)

I've actually answered my own question now. See my Gist here

Hi. Thanks for doing this, it is really useful.

I used an array of objects as a source and it worked like a charm. For 40 objects is great, but when there are 3000 it is a little slow so I wanted to make it asynchronous using ajax. But I am having some problem there, there is something I am doing wrong but I don't what it is. Please take a look:

https://gist.github.com/1978187

Thanks!

I think it would make sense for this functionality to be included in the official Typeahead Plugin. I guess many people require this functionality.

Thanks for this. Added a few other additions (comma-separated support, disabling auto-select of the first element, and auto-setting the width): https://gist.github.com/2030277

Hi. Thanks for doing this patch , it is really useful.
I used an array of objects as a source by doing ajax request to the server and getting element. but once i selected the item and refreshed the page , then users session got expired and user going to login page .I don't know why it is happening .
please take look into this :

$('[data-provide="typeahead"]').typeahead({
source: function (typeahead, query) {
return $.post($('#data-url').val(), { q: query }, function (data) {
return typeahead.process(data);
});
}
});

There's a bug if the returned json list is empty.
to fix, I added:

  if (!results)return;

to the line 1572

Thank you nerdlobe for the fix. its working now

Are you going to make a pull request to https://github.com/twitter/bootstrap? Your improvements are really useful and should be in the original source.

Agreed. This got me out of a tight spot - well worth a pull request :)

I agree with all of you, but can't get it to work properly:

Here's what I came up with:

var brands_el = $("#brands");
    brands_el.typeahead({
        source: function(typeahead, query) {
            if(this.ajax_call)
                this.ajax_call.abort();
            this.ajax_call = $.ajax({
                data: {
                    keyword: query,
                    store_id: $("#store_id").val()
                },
                url: brands_el.data('autocomplete_source'),
                success: function(data) {
                    typeahead.process(data);
                }
            });
        },
        property: 'brand',
        onselect: function (obj) {
            console.log(obj);
        }
    });

Returned JSON when input is 'm':

[{"brand_id":"2","value":"Maison Martin Margiela"},{"brand_id":"21","value":"Frankie Morello"},{"brand_id":"25","value":"Ben Sherman"}]

Same code here: http://pastie.org/3799234

The problem is that if I write 'm' in my input, the dropdown shows:
M
M
M
M
m

Which would be 3 upper Ms from 'Maison Martin Margiela', 1 upper 'M' from 'Frankie Morello', 1 lower m from 'Ben Sherman'.

The final goal is having the 'brand' property in the dropdown, and the 'brand_id' property in a hidden input when I select a result.

I can't understand what is going on...

Could you help me?

Thank you

On line 7, add in

dataType : 'json',

So it becomes

        this.ajax_call = $.ajax({
            dataType : 'json',
            data: {
                keyword: query,
                store_id: $("#store_id").val()
            },
            url: brands_el.data('autocomplete_source'),
            success: function(data) {
                typeahead.process(data);
            }
        });

Hope that helps!

Thank you, I'll try :)

Hi gudbergur, The script works great for me.

Me and a friend just added a new feature, to wait while user is typing and to fire the ajax search only when he stops to type, to make things better to the server. We've just added two new vars, one for the timer and other to keep the time that it would wait (in the Typeahead declaration):

this.timer_lookup = false
this.timer_timeout = this.options.timeout || 400

and before calling the lookup method, (in the 'default' statement in the keyUp function:

var that = this;
clearTimeout(this.timer_lookup);
this.timer_lookup = setTimeout(function(){that.lookup()}, this.timer_timeout);

It works like a charm :)

This adds a default timeout of 400 mls, but user can define in the typeahead declaration, with the 'timeout' parameter :)

@lucastex that would be very useful, nice!

@gudbergur: What do you think about creating a repo for his project? That way it would be easier to handle contributions.

@lucastex great addition!

@lucastex very nice!
@Nerian I think I'll do that soon, there seems to be a lot of interest in this functionality

Cool! Hope it can be added in your fork :)

@koichirose can you send me your script to update also the id field ? thanks

@trippo sorry I did not do it yet, I used a normal select for my brands for now :/
In the 'onselect' function anyway, you should be able to console.log(obj) and check what you need to update the id field.

@koichirose it's true.. it's very simple using in onselect function the obj.id field thanks a lot

Hey guys, need some help.

On my ajax call, I'm returning JSON format like this

[{'id':'1', 'label': 'apple'}, {'id':'2', 'label':'banana'}, {'id':'3', 'label':'orange'}]

And on my front-end, I have 2 input fields, one is visible with the typeahead event and the other one is a hidden field.

I'd like to show the label on the field with typeahead and at the same time save the id of the selected option to the hidden field. Any ideas how this can be done?

@jpdelatorre: it's simple you can use a hidden field and use this code:
....,
property: 'label',
onselect: function (obj) {
$('#field_id').val(obj.id);
}

@trippo thank you. I didn't know about the 'property' property.

@jpdelatorre i use this code can you copy:

$('#city-search').typeahead({
source: function(typeahead, query){
$.ajax({
url: "/ajax/geoautocomplete/"+$('#language_id').val()+"/"+$('#region_id').val(),
type: "POST",
data: "query="+query ,
dataType: "JSON",
async: false,
success: function(data){
typeahead.process(data);
}
})
},
property: 'name',
items:11,
onselect: function (obj) {
if(obj.region_id){
$('#region_id').val(obj.region_id);
}
else{
if(obj.province_id){
val=$('#province_id').val();
$('#province_id').val(val+obj.province_id+"~");
}
$('#town_id').val(obj.town_id);
}
}
});

@gudbergur , @lucastex - thanks guys, this has really helped me. Would be nice to have a proper repo.
Maybe when Twitter officially add this to bootstrap they'll use this. (If they don't I'd be interested to see how their version differs)

Note that this functionality has now been committed to the 2.1.0-wip branch of Bootstrap, will keep this gist around for older versions though!

It doesn't look like the 2.1.0-wip one supports objects/id...

@coli Yup, the pull request that was added to 2.1.0-wip only adds the function-as-a-source part of my fork and not the rest, so until that happens you have to keep using the fork if you want to populate the typeahead with objects.

So I liked your gist, but it doesn't quite do everything i need. Here is my branch:

https://gist.github.com/2960885

Enhancements:

  • minLength parameter- ala jquery ui autocomplete. Requires textbox to have a set length before calling source.

  • By default no options are selected. If you tab while no option is selected it will leave your textbox value as is.

  • Up/Down - when it reaches the start or finish the next press will deselect all options, then next press will start scroll over options again. This is needed to allow tab off of input.

  • Left/Right no longer refresh datasource. There will probably be other keys we want to ignore.

Full Repo @ https://github.com/thorst/Bootstrap-Typeahead-Fork

I am a bit stuck as I keep on getting:

Uncaught TypeError: Cannot call method 'toLowerCase' of undefined

see http://stackoverflow.com/questions/11235644/twitter-bootstrap-typeahead-method-tolowercase-of-undefined for full question.

basically I am following @trippo code and I keep getting the error mentioned above.

any thoughts?!

I have tried minified jQUery non minified jquery, linking google jQuery and nothing...

@mmoscosa i have create a little example for you :)
download from this link http://cl.ly/3w1B0w2m2P0O2p1B250p
to try write v , va , val , vald in text box.
I hope this work for you

@trippo Thank you very much!

I tried and it works when the array is as you put it on the example you kindly shared with me.

However, the array I get as a result is as you can see below, so I try to do:

    typeahead.process(results.data.manufacturers);

and I get the same error as mentioned above,

Uncaught TypeError: Cannot call method 'toLowerCase' of undefined
{"data":{"manufacturers":[{"Manufacturer":{"id":"1","manufacturer":"Bowtech","bow":true,"string":false,"release":false,"scope":false,"sight":false,"long_rod_stabilizer":false,"side_rod_stabilizer":false,"riser":false,"limbs":false,"tabs":false,"buttons":false,"clicker":false,"arrow":null,"nock":null,"point":null,"vane":null,"created":"2012-06-03 13:30:23","modified":"2012-06-03 13:30:26"},"Arrow":[],"Bow":[{"id":"1","name":"Mio","user_id":"4fca0cbb-1750-4bc4-af12-60f3bd6152ee","active":true,"type_id":"1","axle_to_axle":null,"peep_to_sight":null,"peep_to_arrow":null,"peep_to_knocking_point":null,"draw_length":"17.00","draw_weight":"50.00","brace_height":null,"created":"2012-06-03 13:26:02","modified":"2012-06-03 13:26:06","BowsManufacturer":{"id":"1","bow_id":"1","manufacturer_id":"1","created":"2012-06-03 13:46:32","modified":"2012-06-03 13:46:34"}}]},{"Manufacturer":{"id":"3","manufacturer":"Hoyt","bow":true,"string":null,"release":null,"scope":null,"sight":null,"long_rod_stabilizer":null,"side_rod_stabilizer":null,"riser":true,"limbs":true,"tabs":null,"buttons":null,"clicker":null,"arrow":null,"nock":null,"point":null,"vane":null,"created":"2012-06-06 23:10:11","modified":"2012-06-06 23:10:13"},"Arrow":[],"Bow":[]}]},"meta":{"status":"ok","feedback":[{"message":"Unable to establish class","level":"warning"}],"request":{"http_host":"archertrainer.mmoscosa.homelinux.net","http_user_agent":"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit\/536.11 (KHTML, like Gecko) Chrome\/20.0.1132.47 Safari\/536.11","server_addr":"127.0.0.1","remote_addr":"127.0.0.1","server_protocol":"HTTP\/1.1","request_method":"GET","request_uri":"\/bows\/get_manufacturers.json","request_time":1341179217},"credentials":{"class":null,"apikey":null,"username":null},"time_epoch":"1341179222","time_local":"Sun, 01 Jul 2012 22:47:02 +0100","version":"0.3"}}

Hello everyone, I'm having a little issue getting this to work.

The server responses with the correct json request however typeahead only displays the first letter of my returned result. For example if I type k it will display:
k
k
K
k
K

Could you help me figure out why?

Here is my js

$('.user').typeahead({
    source : function(typeahead, query) {
        return $.post('getUser', {
            query : query
        }, function(data) {
            return typeahead.process(data);
        });
    }
});

and my html

http://pastie.org/private/gr1byesbjgmrzxvrgp4a

Thank you.

@rayshinn, it's the same problem I had, just read @richardsheperd's answer above...
I did not try it though, in the end I didn't use the typeahead plugin at all.

@koichirose I have already tried @richardshepherd solution with no luck. Perhaps I am implementing it wrong? Could you provide me with an example?

Thanks

Fantastic work! I used this to write a Knockout.js binding (http://blogs.msdn.com/b/rebond/archive/2012/07/18/knockout-js-binding-for-bootstrap-typeahead-plugin.aspx) Feedback on that is welcome, I'm probably doing it wrong.

Great plugin.

Is it possible to set the href location? Now it's just # and I want to use it for quick navigation (when you select the result you are redirected to that specific page).

Totally dumb question, but if I'm using the Twitter-Bootstrap gem, where do I put this to override it?

Not sure if anyone has encountered this problem but I've followed @trippo example and everything works fine... except that my options didn't show up so I used Chrome's js debugger and noticed that the process method was invoked twice: one time via the success callback and the second time via the lookup function wich seems to be the default to the keyup event...

The problem with this is that the first one has the response items, the other has an empty array...

Noone ever had this issue?

Only way to prevent that is to comment out the line as such:

, lookup: function (event) {
      var that = this
        , items
        , q
        , value

      this.query = this.$element.val()

      if (typeof this.source == "function") {
        value = this.source(this, this.query)
        //if (value) this.process(value) <-- this will trigger process twice...
      } else {
        this.process(this.source)
      }
    }

I'm being really lazy here, but... Any plans for a new version? ( I mean, forking the new typeahead, 2.1)

That's a bug due to lax syntax, I think. The 'else' is associated with the inner 'if (value)'. Try adding a semicolon after 'this.process(value)'?

I had some trouble on Chrome. When clicking on menu, the input wasn't fill.
I have modified line 257 :

setTimeout(function () { that.hide() }, 150)

in

setTimeout(function () { that.hide() }, 300)

thanks
(Chrome V 22.0.1229.94, Mac OS X 10.8.2)

This is great, @gudberger have you considered making this an actual repo, so issues can be logged and uses can fork and do pull requests? Seems like it would get some traction

I'd love to see this in Bootstrap 2.1!

To those who get the error message "Uncaught TypeError: Cannot call method 'toLowerCase'", I resolved this by sending to the process callback a simple array with the values instead of the full JSON.
So instead of

[{'name': 'Joe', }, {'name': 'Henry'}, ...]

You have to send

['Joe', 'Henry', ...]

I get an error on line 147 when using the character '['. :
Invalid regular expression: missing terminating ] for character class

(due to the regular expression) ;(

Not sure if this helps anyone but for v2.2.2 the asynchronous data retrieval seems to be built-in without the need of a forked ajax version:

http://www.lukeschreur.com/posts/bootstrap-asynchronous-auto-complete-input-fields

I've updated your code to use it with Boostrap v2.2.2. https://gist.github.com/4502186

Didn't check for any improvements, does work fine for me. Very much appreciated, this is a great enhancement. Very much appreciated.

Loved this one, though I only got it to work on Firefox.

but under IE9, Opera and Chrome, it does not seem to capture scroll key presses for scrolling across dropdown items.

Also, in no browser was it accepting selection by mouse click, unlike the plain non-Ajax typeahead.

I might have done something wrong though. The only other script running on the page is jquery-latest.

Does anyone have a clue? I can provide whatever code sample I used for this plugin.

Ok I'm beginning to figure out - it's the use of the deprecated $.browser for detecting keydown event support...

https://gist.github.com/mrgcohen/5062352

Here's an additional example where it does not search for all entries when a user types fast. I needed something for a project but don't have time to fork. Hope someone finds this useful. Also it will cache searches so you won't be making a ton of calls (easy to disable if you choose).

So if you search for

Supercalifragilisticexpialidocious

and you are a fast typer and type superca really fast instead of making ajax calls for..

s
su
sup
super
superca

It will just do

superca

https://gist.github.com/mrgcohen/5062352

cheers :smiley:

I just attempted to integrate this again and notice the gist is broken with jquery 1.9. $.browser.webkit call was removed.

Quick solution is to use jquery migrate:

http://stackoverflow.com/questions/9638247/is-jquery-browser-deprecated

It's useful to update the gist.

Great fork!!! I try to autocomplete two days to do, but only solved the problem of installing a fork

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.