Extending Twitter Bootstrap Typeahead Plugin

Update: also read this post

In twitter bootstrap there is a JQuery plugin for auto completion. It’s called bootstrap-typeahaed. Typeahead uses an array of strings as datasource and is easily activated with in it’s simplest form 1 line of code.

$("#inputboxselector").typeahead({ "source":arrayOfStrings})

It’s very easy to use but somewhat limited. We needed a slightly different functionality.

Our use case:

  • 2 inputboxes, one for postal code (the numeric form) and one inputbox for the community that has that postal code.
  • In Belgium different communities can have the same postal code, and the right community should be selected on selecting a postal code. Or the right postal code should be selected on selecting a community.
  • Show both the code an city in the autocomplete but only do autocomplete and highlighting on one of the fields depending on the inputbox.

The typeahead plugin is not suited for that. It only takes an array of strings and sets the selected value in the input box. We have an array of objects.
Objects like

{ "code":"9000", "city":"Gent"}

We wanted to use typeahead instead of any other existing JQuery plugin since we’re already using twitter bootstrap.

To achieve this I extended, (actually more copied and altered) typeahead. The result is here. I called it typeaheadmap. It works just like bootstrap typeahead except that you have to add 2 more parameters to typeahead options object. Include bootstrap-typeaheadmap.js (+ JQuery 1.7+) and typeaheadmap.css (+ bootstraps base css) to use it.

View a demo
Read on for a usage example
Considering the example of the cities and postalcodes the javascript would be like

$("#postalcodes").typeaheadmap({ "source":arrayOfObjects, "key": "code", "value": "city"})

for the postalcode inputbox and

$("#cities").typeaheadmap({ "source":arrayOfObjects, "key": "city", "value": "code"})

for the citynames inputbox. In this example the arrayOfObjects is an array of objects shown above.

To allow either input to automagically show the correct code or city name I’ve added an option to add listener function. The listener is called on change of the typeaheadmap. Then it becomes

$("#cities").typeaheadmap({ "source":arrayOfObjects, 
    "key": "city", 
    "value": "code", 
    "listener": function(k, v) {
        $("#postalcodes").val(v)
})

11 Replies to “Extending Twitter Bootstrap Typeahead Plugin”

  1. So, I changed the iterator i to j in the outer for loop:

    Although the list with options to choose from is shown when starting to type, when you click one, only the input corresponding to the current one is filled. Hence, not the ploeg or rugnr when I select a rider.

    My new code is below. Any ideas?

    for (var j = 1; j < = 10; j++) {
    $("#renner_"+j).typeaheadmap({
    "source" : rennersList,
    "key" : "k",
    "value" : "v",
    "items": 10,
    "listener" : function(k, v) {
    $("#ploeg_"+j).val(v)
    $(rennersList).each(function(i, itm) {
    if (itm["k"] == k && itm["v"]==v) {
    $("#rugnr_"+j).val(itm["d"]);
    }
    })
    },
    "displayer": function(that, item, highlighted) {
    return highlighted + ' (' + item[that.value] + ' )'
    }
    })
    $("#ploeg_"+j).typeaheadmap({
    "source" : rennersList,
    "key" : "v",
    "value" : "k",
    "items": 9,
    "listener" : function(k, v) {
    $("#renner_"+j).val(v)
    $(rennersList).each(function(i, itm) {
    if (itm["v"] == k && itm["k"]==v) {
    $("#rugnr_"+j).val(itm["d"]);
    }
    })
    },
    "displayer": function(that, item, highlighted) {
    return highlighted + ' (' + item[that.value] + ' )'
    }
    })
    }
    1. It is kind of hard to tell without the relevant HTML. I can give you one tip. Go through it with a debugger, then you can see what is happening inside your code.

  2. you’re defining a new i in you’re each(function(i, …… If you want #rugnr_i to have the i value of the for loop, don’t use i in the each.

  3. Hi Balder,

    typeaheadmap works fine in my site too, however I tried to extend on it a little in the following way: Instead of having one combination of (country, capital), my form has the following kind of combinations (it is for a cycling-pool):

    (rider-name-1, rider-squad-1)
    (rider-name-2, rider-squad-2)
    (rider-name-3, rider-squad-3)

    for example (Gesink, Rabobank)…

    So, I gave all the riders an id, like rider_1, rider_2 etc. and also the squads (squad_1, squad_2…). Then I used your typeaheadmap in the following loop over var i=1,…,10:

    for (var i = 1; i < = 10; i++) {
       $("#renner_"+i).typeaheadmap({
          "source" : rennersList,
          "key" : "k",
          "value" : "v",
          "items": 10,
          "listener" : function(k, v) {
                 $("#ploeg_"+i).val(v)
                 $(rennersList).each(function(i, itm) {
                    if (itm["k"] == k && itm["v"]==v) {
                       $("#rugnr_"+i).val(itm["d"]);
                    }
                })
            },
           "displayer": function(that, item, highlighted) {
                return highlighted + ' (' + item[that.value] + ' )'
              }
     })
    $("#ploeg_"+i).typeaheadmap({
          "source" : rennersList,
          "key" : "v",
          "value" : "k",
          "items": 9,
          "listener" : function(k, v) {
                 $("#renner_"+i).val(v)
                 $(rennersList).each(function(i, itm) {
                         if (itm["v"] == k && itm["k"]==v) {
                             $("#rugnr_"+i).val(itm["d"]);
                         }
                  })
             },
             "displayer": function(that, item, highlighted) {
                  return highlighted + ' (' + item[that.value] + ' )'
              }
        })
    }

    The problem is this does not work, and I can't find out why. I have one suspicion though, that passing the variable i to the listener function may screw up results.

    Any help is appreciated…

    Pim

  4. I’m using your Typeaheadmap, and it works fine. Still figuring out how to make it work with json. But one other thing: How can I add a “sticky” last item in the items dropdown? Something like this:

    var $ultimo = “My postal code is not here!
    items.last().append($ultimo)

    But the above code doesn’t really work. I put it in the render function, but it puts the new last item inside the previous last item. Adding parent() doesn’t work. And I would prefer to have the code outside the whole code block, but that doesn’t work either. ( $(“ul.typehead”).append($ultimo) does nothing)

    How can I fix it?

    1. Hi Martino

      If you want it shown when the typed characters are not a valid option. I’m currently on holidays and haven’t done much programming yet, I took a quick look and I think you can do what you want when you add the function you want to call to typeaheadmap and call it in

       lookup: function (event) function { 
         ....
         if (!items.length) {
              // add some stuff that checks if there are chars typed and if so set new items to display in an overridable function.
              return this.shown ? this.hide() : this
            }

      If you want it to show it always you will also have to add it to the items in the lookup after the sorting.

      The ideas are nice, if I find some time in my holidays I see to get it implemented cleanly, or you could always fork the github repo and send a pull request 😀

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: