Overriding bootstrap typeahead to not initially select

Bootstrap typeahead (ie autocomplete) component has, like most of Bootstrap, a great visual design.

But it’s got a very limited feature set, it lacks some of the customization features found in other autocomplete widgets (like JQuery-UI) — and it lacks event hooks to let you easily add them back in yourself.

My particular road block was, the bootstrap typeahead initially appears with the first element in the suggest list already selected.

typeahead

If the user presses ‘enter’ at this point, it’ll put that first selected item into the text box. In my particular app and use, for various reasons, this really annoys the users. In my previous JQuery-UI autocomplete solution, I managed to hook into the right event to change this, so when the suggest menu initially appeared it was with no items selected — users could use mouse or up/down arrows to select if they wanted.

There was no initial clear way to do this with bootstrap typeahead. I found various apparently unmaintained forks of bootstrap typeahead, some of which added the option I needed. But I didn’t really want to use an entire copy-and-paste fork of bootstrap’s code (my own fork or someone elses unmaintained one), nor did I want to use a ginormous feature-complete replacement of bootstrap typeahead that worked how I want (like the huge and powerful non-bootstrap twitter typeahead).

I eventually figured out that even though bootstrap typeahead doesn’t provide much documentation for it, it does structure it’s code into individual methods in a jquery extension structure that lets you redefine certain ones, using $.fn.typeahead.Constructor.prototype. (I discovered that googling around for other people’s typeahead hacks, I still don’t entirely understand it myself and would never have figured it out on my own!).

I just need to override the render function to remove the line items.first().addClass('active').

No item will be initially selected, but up and down arrow will still work, because the next/prev methods were already robust enough to handle no currently selected item.

I didn’t figure out any good way to make this customizable on a per-typeahead basis, changing the prototype changes it globally, better than nothing for me.

I also tried to figure out a way to call the original implementation as ‘super’, so I wouldn’t need to copy and paste the entire render method and then change it — but no dice. Oh well, it’s only a few lines. It does mean there are some potential forward-compatibility problems, but not as many as if I’d forked the entire component, I think/hope.

 var newRender = function(items) {
      var that = this

      items = $(items).map(function (i, item) {
        i = $(that.options.item).attr('data-value', item)
        i.find('a').html(that.highlighter(item))
        return i[0]
      })

      this.$menu.html(items)
      return this
 };
 $.fn.typeahead.Constructor.prototype.render = newRender;

typeahead-none-selected

I did this just now, I haven’t tested it enough to be sure it will work perfectly without undesired bugs or side effects, but it looks like it should.

20 minutes later spoke too soon, had to over-ride ‘select’ to handle no item selected too, so hitting return just hides the menu but doens’t zero out your current input, if nothing is selected.

$.fn.typeahead.Constructor.prototype.select = function() {
    var val = this.$menu.find('.active').attr('data-value');
    if (val) {
      this.$element
        .val(this.updater(val))
        .change();
    }
    return this.hide()
};

5 thoughts on “Overriding bootstrap typeahead to not initially select

  1. Thank you for sharing! Although there is still a “bug” I think, but when you start selecting an item with the down arrow key and then you don’t want any anyone and press the up arrow key to return to the the one you were typing, it doesn’t work, it gets stuck with the first item in the list.

    How do you think it could be fixed?

    Thank you!

  2. Hi, thank you for sharing this! Inspired by your code I found a solution without copying the original implementation. This is what I came up with:

    // Initialize typeahead
    $("input").typeahead(...);
    // Get the current typeahead instance
    var typeaheadInstance = $("input").data("typeahead");
    // Save the reference to the original implementation of the render() function
    var origRenderFunc = typeaheadInstance.render;
    
    // Overwrite the render() function
    typeaheadInstance.render = function() {
    	// Execute the original implementation
    	var result = origRenderFunc.apply(this, arguments);
    	// Remove the 'active' class from the first item
    	result.$menu.children().first().removeClass("active")
    	
    	return result;
    }
    
  3. Thanks a lot for posting that. Almost perfect. Only remaining thing is the following: if I type something I’d like “enter” to submit whatever I typed, as opposed to just hiding the matches. Any clues? In any case this is already a huge improvement,

  4. I am getting Uncaught TypeError: Cannot read property ‘Constructor’ of undefined in console when i am adding the script after including ‘ui-bootstrap-tpls-0.11.2.js’

Leave a comment