diff options
| author | Jacob Thornton <[email protected]> | 2012-01-08 00:49:38 -0800 |
|---|---|---|
| committer | Jacob Thornton <[email protected]> | 2012-01-08 00:49:38 -0800 |
| commit | 4478df768168fe41599508688046612bf5f1e526 (patch) | |
| tree | 9a3c0aacda8b4abce4ef200ef925cdeb55877720 /js | |
| parent | 961c479422f10cb566ade1f3a08d1b807d590b7a (diff) | |
| download | bootstrap-4478df768168fe41599508688046612bf5f1e526.tar.xz bootstrap-4478df768168fe41599508688046612bf5f1e526.zip | |
first pass at ultra basic autocomplete
Diffstat (limited to 'js')
| -rw-r--r-- | js/bootstrap-typeahead.js | 147 | ||||
| -rw-r--r-- | js/tests/unit/bootstrap-typeahead.js | 22 |
2 files changed, 112 insertions, 57 deletions
diff --git a/js/bootstrap-typeahead.js b/js/bootstrap-typeahead.js index 52ced3fef..4933f9605 100644 --- a/js/bootstrap-typeahead.js +++ b/js/bootstrap-typeahead.js @@ -34,80 +34,102 @@ constructor: Typeahead - , matcher: function(item, query) { + , matcher: function (item, query) { return ~item.indexOf(query) } - , select: function(event) { - this.$element.val($(event.target).attr('data-value')) - this.hide() + , select: function () { + var val = this.$menu.find('.active').attr('data-value') + this.$element.val(val) + return this.hide() } , show: function () { - this.shown = true + 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.shown = false this.$menu.hide() + this.shown = false return this } , lookup: function (event) { var query = this.$element.val() , that = this + , items + + if (!query) { + return this.shown ? this.hide() : this + } - var items = this.data.filter(function (item) { - if (that.matcher(item, query)) { - return item - } + items = this.data.filter(function (item) { + if (that.matcher(item, query)) return item }) if (!items.length) { return this.shown ? this.hide() : this } - return this.render(items).show() + return this.render(items.slice(0, this.options.items)).show() } - , render: function(items) { + , render: function (items) { var that = this items = $(items).map(function (i, item) { - return $(that.options.item) - .text(item) - .attr('data-value', item)[0] + i = $(that.options.item).attr('data-value', item) + i.find('a').text(item) + return i[0] }) items.first().addClass('active') - - this.$menu.append(items) - + this.$menu.html(items) return this } , next: function (event) { var active = this.$menu.find('.active').removeClass('active') - , next = active.next() || $(this.$menu.find('li')[0]) + , 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') - , next = active.prev() || this.$menu.find('li').last() + , prev = active.prev() - next.addClass('active') + if (!prev.length) { + prev = this.$menu.find('li').last() + } + + prev.addClass('active') } - , keyup: function () { - event - .stopPropagation() - .preventDefault() + , keyup: function (e) { + e.stopPropagation() + e.preventDefault() + + switch(e.keyCode) { + case 40: // down arrow + case 38: // up arrow + break - switch(event.keyCode) { case 9: // tab case 13: // enter this.select() @@ -120,51 +142,68 @@ default: this.lookup() } + } - , keypress: function (event) { - event.stopPropagation() - switch(event.keyCode) { + , keypress: function (e) { + e.stopPropagation() + + switch(e.keyCode) { case 9: // tab case 13: // enter case 27: // escape - event.preventDefault() + e.preventDefault() break case 38: // up arrow + e.preventDefault() this.prev() - event.preventDefault() break case 40: // down arrow + e.preventDefault() this.next() - event.preventDefault() 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') + } , listen: function () { this.$element - .on('focus', this.show) - .on('blur', $.proxy(this.hide, this)) + .on('blur', $.proxy(this.blur, this)) .on('keypress', $.proxy(this.keypress, this)) - .on('keyup', this.keyup) - .on('change', $.proxy(this.lookup, this)) + .on('keyup', $.proxy(this.keyup, this)) if ($.browser.webkit || $.browser.msie) { - this.$element.on('keydown', this.keypress) + this.$element.on('keydown', $.proxy(this.keypress, this)) } this.$menu - .on('click', '* > *', $.proxy(this.select, this)) - .on('mouseenter', function () { - /* remove selected class, add to mouseover */ - }) + .on('click', $.proxy(this.click, this)) + .on('mouseenter', 'li', $.proxy(this.mouseenter, this)) } } + /* TYPEAHEAD PLUGIN DEFINITION - * ============================== */ + * =========================== */ $.fn.typeahead = function ( option ) { return this.each(function () { @@ -177,14 +216,24 @@ } $.fn.typeahead.defaults = { - data: null - , items: 8 - , empty: false - , noresults: false - , menu: '<ul class="dropdown-menu"></ul>' - , item: '<li></li>' + items: 8 + , menu: '<ul class="typeahead dropdown-menu"></ul>' + , item: '<li><a href="#"></a></li>' } $.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 )
\ No newline at end of file diff --git a/js/tests/unit/bootstrap-typeahead.js b/js/tests/unit/bootstrap-typeahead.js index dc46a7990..10b2a3f17 100644 --- a/js/tests/unit/bootstrap-typeahead.js +++ b/js/tests/unit/bootstrap-typeahead.js @@ -13,11 +13,9 @@ $(function () { test("should listen to an input", function () { var $input = $('<input />') $input.typeahead() - ok($input.data('events').focus, 'has a focus event') ok($input.data('events').blur, 'has a blur event') ok($input.data('events').keypress, 'has a keypress event') ok($input.data('events').keyup, 'has a keyup event') - ok($input.data('events').change, 'has a change event') if ($.browser.webkit || $.browser.msie) { ok($input.data('events').keydown, 'has a keydown event') } else { @@ -44,7 +42,8 @@ $(function () { }) , typeahead = $input.data('typeahead') - $input.val('a').change() + $input.val('a') + typeahead.lookup() ok(typeahead.$menu.is(":visible"), 'typeahead is visible') equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu') @@ -54,12 +53,14 @@ $(function () { }) test("should hide menu when query entered", function () { + stop() var $input = $('<input />').typeahead({ data: ['aa', 'ab', 'ac'] }) , typeahead = $input.data('typeahead') - $input.val('a').change() + $input.val('a') + typeahead.lookup() ok(typeahead.$menu.is(":visible"), 'typeahead is visible') equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu') @@ -67,7 +68,10 @@ $(function () { $input.blur() - ok(!typeahead.$menu.is(":visible"), "typeahead is no longer visible") + setTimeout(function () { + ok(!typeahead.$menu.is(":visible"), "typeahead is no longer visible") + start() + }, 200) typeahead.$menu.remove() }) @@ -78,7 +82,8 @@ $(function () { }) , typeahead = $input.data('typeahead') - $input.val('a').change() + $input.val('a') + typeahead.lookup() ok(typeahead.$menu.is(":visible"), 'typeahead is visible') equals(typeahead.$menu.find('li').length, 3, 'has 3 items in menu') @@ -110,9 +115,10 @@ $(function () { }) , typeahead = $input.data('typeahead') - $input.val('a').change() + $input.val('a') + typeahead.lookup() - $(typeahead.$menu.find('li')[2]).trigger('click') + $(typeahead.$menu.find('li')[2]).mouseover().click() equals($input.val(), 'ac', 'input value was correctly set') ok(!typeahead.$menu.is(':visible'), 'the menu was hidden') |
