123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- /**
- * DropKick
- *
- * Highly customizable <select> lists
- * https://github.com/JamieLottering/DropKick
- *
- * © 2011 Jamie Lottering <http://github.com/JamieLottering>
- * <http://twitter.com/JamieLottering>
- *
- */
- (function ($, window, document) {
- var ie6 = false;
- 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 }}">{{ 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');
- });
- $select.bind('change', function (e) {
- text = $('option:selected', this).text();
- methods.set.call(this, text);
- });
- setTimeout(function () {
- $select.hide();
- }, 0);
- });
- };
- methods.set = function (text) {
- var
- $select = $(this),
- list = $select.data('dropkick'),
- $dk = list.$dk;
- $('.dk_label', $dk).text(text);
- $(".dk_options_inner li", $dk).each(function(){
- $(this).removeAttr('class');
- if ($(this).text() == text){
- $(this).attr('class', 'dk_option_current');
- }
- });
- }
- // 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:
- 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);
- $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());
- 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
- $(document).on('click', '.dk_toggle', 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
- $(document).on('click', '.dk_options a', 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);
|