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()
};
This entry was posted in General. Bookmark the permalink.

5 Responses to Overriding bootstrap typeahead to not initially select

  1. Peter says:

    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. Krani says:

    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. jrochkind says:

    Nice, thanks a lot for sharing, Krani, that’s a nice way to do it.

  4. Dominic says:

    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,

  5. Ashish Gupta says:

    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 Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s