aboutsummaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorJacob Thornton <[email protected]>2012-01-08 00:49:38 -0800
committerJacob Thornton <[email protected]>2012-01-08 00:49:38 -0800
commit4478df768168fe41599508688046612bf5f1e526 (patch)
tree9a3c0aacda8b4abce4ef200ef925cdeb55877720 /js
parent961c479422f10cb566ade1f3a08d1b807d590b7a (diff)
downloadbootstrap-4478df768168fe41599508688046612bf5f1e526.tar.xz
bootstrap-4478df768168fe41599508688046612bf5f1e526.zip
first pass at ultra basic autocomplete
Diffstat (limited to 'js')
-rw-r--r--js/bootstrap-typeahead.js147
-rw-r--r--js/tests/unit/bootstrap-typeahead.js22
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')