Created
November 27, 2012 14:30
-
-
Save glideranderson/4154514 to your computer and use it in GitHub Desktop.
Dropkick Js with added Keyboard Search (by Paritosh)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* DropKick | |
* | |
* Highly customizable <select> lists | |
* https://github.com/JamieLottering/DropKick | |
* | |
* © 2011 Jamie Lottering <http://github.com/JamieLottering> | |
* <http://twitter.com/JamieLottering> | |
* | |
* changes added from Paritosh Singh <https://github.com/paritosh90/> | |
* <http://twitter.com/paritoshprakhar> | |
* To get letter search | |
* | |
* added ability to have html in options, tags are stripped for letter search | |
* by Robin Hjelmeir | |
*/ | |
(function ($, window, document) { | |
var ie6 = false; | |
// Help prevent flashes of unstyled content | |
if ($.browser.msie && $.browser.version.substr(0, 1) < 7) { | |
ie6 = true; | |
} else { | |
document.documentElement.className = document.documentElement.className + ' dk_fouc'; | |
} | |
var | |
// Public methods exposed to $.fn.dropkick() | |
methods = {}, | |
// Cache every <select> element that gets dropkicked | |
lists = [], | |
// Convenience keys for keyboard navigation | |
keyMap = { | |
'left' : 37, | |
'up' : 38, | |
'right' : 39, | |
'down' : 40, | |
'enter' : 13 | |
}, | |
// HTML template for the dropdowns | |
dropdownTemplate = [ | |
'<div class="dk_container" id="dk_container_{{ id }}" tabindex="{{ tabindex }}">', | |
'<a class="dk_toggle">', | |
'<span class="dk_label">{{ label }}</span>', | |
'</a>', | |
'<div class="dk_options">', | |
'<ul class="dk_options_inner">', | |
'</ul>', | |
'</div>', | |
'</div>' | |
].join(''), | |
// HTML template for dropdown options | |
optionTemplate = '<li class="{{ current }}"><a data-dk-dropdown-value="{{ value }}" data-dk-dropdown-text="{{ topdata }}">{{ text }}</a></li>', | |
//optionTemplate = '<li class="{{ current }}"><a data-dk-dropdown-value="{{ value }}">{{ text }}</a></li>', | |
// Some nice default values | |
defaults = { | |
startSpeed : 1000, // I recommend a high value here, I feel it makes the changes less noticeable to the user | |
theme : false, | |
change : false | |
}, | |
// Make sure we only bind keydown on the document once | |
keysBound = false | |
; | |
// Called by using $('foo').dropkick(); | |
methods.init = function (settings) { | |
settings = $.extend({}, defaults, settings); | |
return this.each(function () { | |
var | |
// The current <select> element | |
$select = $(this), | |
// Store a reference to the originally selected <option> element | |
$original = $select.find(':selected').first(), | |
// Save all of the <option> elements | |
$options = $select.find('option'), | |
// We store lots of great stuff using jQuery data | |
data = $select.data('dropkick') || {}, | |
// This gets applied to the 'dk_container' element | |
id = $select.attr('id') || $select.attr('name'), | |
// This gets updated to be equal to the longest <option> element | |
width = settings.width || $select.outerWidth(), | |
// Check if we have a tabindex set or not | |
tabindex = $select.attr('tabindex') ? $select.attr('tabindex') : '', | |
// The completed dk_container element | |
$dk = false, | |
theme | |
; | |
// Dont do anything if we've already setup dropkick on this element | |
if (data.id) { | |
return $select; | |
} else { | |
data.settings = settings; | |
data.tabindex = tabindex; | |
data.id = id; | |
data.$original = $original; | |
data.$select = $select; | |
data.value = _notBlank($select.val()) || _notBlank($original.attr('value')); | |
data.label = $original.text(); | |
data.options = $options; | |
} | |
// Build the dropdown HTML | |
$dk = _build(dropdownTemplate, data); | |
// Make the dropdown fixed width if desired | |
$dk.find('.dk_toggle').css({ | |
'width' : width + 'px' | |
}); | |
// Hide the <select> list and place our new one in front of it | |
$select.before($dk); | |
// Update the reference to $dk | |
$dk = $('#dk_container_' + id).fadeIn(settings.startSpeed); | |
// Save the current theme | |
theme = settings.theme ? settings.theme : 'default'; | |
$dk.addClass('dk_theme_' + theme); | |
data.theme = theme; | |
// Save the updated $dk reference into our data object | |
data.$dk = $dk; | |
// Save the dropkick data onto the <select> element | |
$select.data('dropkick', data); | |
// Do the same for the dropdown, but add a few helpers | |
$dk.data('dropkick', data); | |
lists[lists.length] = $select; | |
// Focus events | |
$dk.bind('focus.dropkick', function (e) { | |
$dk.addClass('dk_focus'); | |
}).bind('blur.dropkick', function (e) { | |
$dk.removeClass('dk_open dk_focus'); | |
}); | |
setTimeout(function () { | |
$select.hide(); | |
}, 0); | |
}); | |
}; | |
// Allows dynamic theme changes | |
methods.theme = function (newTheme) { | |
var | |
$select = $(this), | |
list = $select.data('dropkick'), | |
$dk = list.$dk, | |
oldtheme = 'dk_theme_' + list.theme | |
; | |
$dk.removeClass(oldtheme).addClass('dk_theme_' + newTheme); | |
list.theme = newTheme; | |
}; | |
// Reset all <selects and dropdowns in our lists array | |
methods.reset = function () { | |
for (var i = 0, l = lists.length; i < l; i++) { | |
var | |
listData = lists[i].data('dropkick'), | |
$dk = listData.$dk, | |
$current = $dk.find('li').first() | |
; | |
$dk.find('.dk_label').text(listData.label); | |
$dk.find('.dk_options_inner').animate({ scrollTop: 0 }, 0); | |
_setCurrent($current, $dk); | |
_updateFields($current, $dk, true); | |
} | |
}; | |
// Expose the plugin | |
$.fn.dropkick = function (method) { | |
if (!ie6) { | |
if (methods[method]) { | |
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); | |
} else if (typeof method === 'object' || ! method) { | |
return methods.init.apply(this, arguments); | |
} | |
} | |
}; | |
// private | |
function _handleKeyBoardNav(e, $dk) { | |
var | |
code = e.keyCode, | |
data = $dk.data('dropkick'), | |
options = $dk.find('.dk_options'), | |
open = $dk.hasClass('dk_open'), | |
current = $dk.find('.dk_option_current'), | |
first = options.find('li').first(), | |
last = options.find('li').last(), | |
next, | |
prev | |
; | |
switch (code) { | |
case keyMap.enter: | |
if (open) { | |
_updateFields(current.find('a'), $dk); | |
_closeDropdown($dk); | |
} else { | |
_openDropdown($dk); | |
} | |
e.preventDefault(); | |
break; | |
case keyMap.up: | |
prev = current.prev('li'); | |
if (open) { | |
if (prev.length) { | |
_setCurrent(prev, $dk); | |
} else { | |
_setCurrent(last, $dk); | |
} | |
} else { | |
_openDropdown($dk); | |
} | |
e.preventDefault(); | |
break; | |
case keyMap.down: | |
if (open) { | |
next = current.next('li').first(); | |
if (next.length) { | |
_setCurrent(next, $dk); | |
} else { | |
_setCurrent(first, $dk); | |
} | |
} else { | |
_openDropdown($dk); | |
} | |
e.preventDefault(); | |
break; | |
default: | |
/////////////////////////////Changed by Paritosh for keys to work //////////////////////// | |
if ((code>=48 && code<=57) || (code>=65 && code<=90)){ | |
var dk_parent = $(current).parent(); | |
var dk_find = 'a[data-dk-dropdown-text^=\"'+String.fromCharCode(code)+'\"]'; | |
var dk_selects = $(dk_parent).find(dk_find); | |
var dk_find = 'a[data-dk-dropdown-text^=\"'+String.fromCharCode(code).toLowerCase()+'\"]'; | |
var dk_selects1 = $(dk_parent).find(dk_find); | |
dk_selects = dk_selects.add(dk_selects1); | |
var index = $(dk_selects).index($(current).children('a')); | |
var dk_select; | |
if($(dk_selects).length-1 != index){ | |
dk_select = $(dk_selects)[index+1]; | |
} | |
else{ | |
dk_select = $(dk_selects).first(); | |
} | |
if($(dk_select).length){ | |
_setCurrent($(dk_select).parent(), $dk); | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////////////// | |
break; | |
} | |
} | |
// Update the <select> value, and the dropdown label | |
function _updateFields(option, $dk, reset) { | |
var value, label, data; | |
value = option.attr('data-dk-dropdown-value'); | |
label = option.text(); | |
data = $dk.data('dropkick'); | |
$select = data.$select; | |
$select.val(value); | |
$select.trigger('change'); | |
$dk.find('.dk_label').text(label); | |
reset = reset || false; | |
if (data.settings.change && !reset) { | |
data.settings.change.call($select, value, label); | |
} | |
} | |
// Set the currently selected option | |
function _setCurrent($current, $dk) { | |
$dk.find('.dk_option_current').removeClass('dk_option_current'); | |
$current.addClass('dk_option_current'); | |
_setScrollPos($dk, $current); | |
} | |
function _setScrollPos($dk, anchor) { | |
var height = anchor.prevAll('li').outerHeight() * anchor.prevAll('li').length; | |
$dk.find('.dk_options_inner').animate({ scrollTop: height + 'px' }, 0); | |
} | |
// Close a dropdown | |
function _closeDropdown($dk) { | |
$dk.removeClass('dk_open'); | |
} | |
// Open a dropdown | |
function _openDropdown($dk) { | |
var data = $dk.data('dropkick'); | |
$dk.find('.dk_options').css({ top : $dk.find('.dk_toggle').outerHeight() - 1 }); | |
$dk.toggleClass('dk_open'); | |
} | |
/** | |
* Turn the dropdownTemplate into a jQuery object and fill in the variables. | |
*/ | |
function _build (tpl, view) { | |
var | |
// Template for the dropdown | |
template = tpl, | |
// Holder of the dropdowns options | |
options = [], | |
$dk | |
; | |
template = template.replace('{{ id }}', view.id); | |
template = template.replace('{{ label }}', view.label); | |
template = template.replace('{{ tabindex }}', view.tabindex); | |
if (view.options && view.options.length) { | |
for (var i = 0, l = view.options.length; i < l; i++) { | |
var | |
$option = $(view.options[i]), | |
current = 'dk_option_current', | |
oTemplate = optionTemplate | |
; | |
oTemplate = oTemplate.replace('{{ value }}', $option.val()); | |
oTemplate = oTemplate.replace('{{ current }}', (_notBlank($option.val()) === view.value) ? current : ''); | |
//oTemplate = oTemplate.replace('{{ text }}', $option.text()); | |
oTemplate = oTemplate.replace(RegExp('{{ text }}','g'), $option.text()); // This line is edited by paritosh in order to dropkick keypress to work | |
// trim html from text | |
var top_text = $option.text().replace(/(<([^>]+)>)/ig,"").trim(); | |
oTemplate = oTemplate.replace('{{ topdata }}', top_text); | |
options[options.length] = oTemplate; | |
} | |
} | |
$dk = $(template); | |
$dk.find('.dk_options_inner').html(options.join('')); | |
return $dk; | |
} | |
function _notBlank(text) { | |
return ($.trim(text).length > 0) ? text : false; | |
} | |
$(function () { | |
// Handle click events on the dropdown toggler | |
$('.dk_toggle').live('click', function (e) { | |
var $dk = $(this).parents('.dk_container').first(); | |
_openDropdown($dk); | |
if ("ontouchstart" in window) { | |
$dk.addClass('dk_touch'); | |
$dk.find('.dk_options_inner').addClass('scrollable vertical'); | |
} | |
e.preventDefault(); | |
return false; | |
}); | |
// Handle click events on individual dropdown options | |
$('.dk_options a').live(($.browser.msie ? 'mousedown' : 'click'), function (e) { | |
var | |
$option = $(this), | |
$dk = $option.parents('.dk_container').first(), | |
data = $dk.data('dropkick') | |
; | |
_closeDropdown($dk); | |
_updateFields($option, $dk); | |
_setCurrent($option.parent(), $dk); | |
e.preventDefault(); | |
return false; | |
}); | |
// Setup keyboard nav | |
$(document).bind('keydown.dk_nav', function (e) { | |
var | |
// Look for an open dropdown... | |
$open = $('.dk_container.dk_open'), | |
// Look for a focused dropdown | |
$focused = $('.dk_container.dk_focus'), | |
// Will be either $open, $focused, or null | |
$dk = null | |
; | |
// If we have an open dropdown, key events should get sent to that one | |
if ($open.length) { | |
$dk = $open; | |
} else if ($focused.length && !$open.length) { | |
// But if we have no open dropdowns, use the focused dropdown instead | |
$dk = $focused; | |
} | |
if ($dk) { | |
_handleKeyBoardNav(e, $dk); | |
} | |
}); | |
}); | |
})(jQuery, window, document); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment