I ran into the same problem, I made some corrections for the jquery-dropkick-1.0.0 script to solve this problem. I added the forceSyncWithSelect method for the dropkick object.
Here is a list of the changes I made:
- add tag support
- add tire settings to leave width for css
- add method for open, close, forceSyncWithSelect and isDropkicked
- save default tabindex 0
- add the optgroup class option with the data-dkgroupclass attribute in the optgroup element.
- fix forceSyncWithSelect method, if the value is displayed a multiple of time, takes only the first occurrence
- switch to menu selection
- prevent IE from closing the menu when the scrollbar interacts (the blur event is fired when interacting with the scrollbar, this behavior is incorrect)
You may not need all these changes, but you can make the diff patch and take what you need (we will be glad if you call the change log or add its source to the header, I want to suggest these changes to Jamie Lottery, but for this I need to make an h2> account.
/** * DropKick * * Highly customizable <select> lists * https://github.com/JamieLottering/DropKick * * © 2011 Jamie Lottering <http://github.com/JamieLottering> * <http://twitter.com/JamieLottering> * * Patch: * - 2012-03-30 godboutj, add support for <optgroup> tag * - 2012-03-30 godboutj, add autoWidth settings to leave width to css * - 2012-05-25 godboutj, add method for open, close, forceSyncWithSelect and isDropkicked * - 2012-05-25 godboutj, keep default tabindex 0 * - 2012-08-09 godboutj, add optgroup class option with the data-dkgroupclass attribute on optgroup element * - 2012-08-15 godboutj, fix forceSyncWithSelect method if value appear multiple of time, take the first occurrence only * - 2012-09-07 godboutj, toggle on click on the menu selection * - 2012-09-25 godboutj, prevent IE from closing the menu on scroll bar interaction (a blur event is launched on scroll bar interaction, this behavior is so wrong) */ (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 }}">{{ text }}</a></li>', optionGroupTemplate = '<li class="dk_option_group {{ dataclass }}"><div class="dk_option_group_text">{{ text }}</div></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, autoWidth: true }, // 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> and <optgroup> elements $options = $select.children('option,optgroup'), // 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(), // Keep tabindex, even default value tabindex = ($select.attr('tabindex') != null && $select.attr('tabindex') != undefined) ? $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 if (settings.autoWidth) { $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) { // Prevent IE from closing the menu on focus loose, // this make the menu close all the time when using the scroll bar if (!$.browser.msie) { $dk.removeClass('dk_open'); } $dk.removeClass('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; }; // Force Value sync methods.forceSyncWithSelect = function () { var $select = $(this), $dk = $select.data('dropkick').$dk, $current = $dk.find("li [data-dk-dropdown-value='" + $select.val() + "']").first() ; _updateFields($current, $dk, true); }; // 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); } }; methods.close = function () { var $select = $(this), $dk = $select.data('dropkick').$dk ; _closeDropdown($dk); } methods.open = function () { var $select = $(this), $dk = $select.data('dropkick').$dk ; _openDropdown($dk); } methods.isOpen = function () { var $select = $(this), $dk = $select.data('dropkick').$dk ; return _isDropdownOpenned($dk); } methods.toggleOpen = function () { var $select = $(this), $dk = $select.data('dropkick').$dk ; _toggleOpenCloseDropDown($dk); } methods.isDropkicked = function () { var $select = $(this); return $select.data('dropkick') != undefined; } // 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); } // Is dropdown openned function function _isDropdownOpenned($dk) { return $dk.hasClass('dk_open'); } // 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 }); if (!$dk.hasClass('dk_open')) { $dk.addClass('dk_open'); } } // Toggle dropdown function _toggleOpenCloseDropDown($dk) { if (_isDropdownOpenned($dk)) { _closeDropdown($dk); } else { _openDropdown($dk); } } /** * 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, gTemplate = optionGroupTemplate ; if ($option.is('optgroup')) { gTemplate = gTemplate.replace('{{ text }}', $option.attr('label')); if ($option.attr('data-dkgroupclass') != undefined) { gTemplate = gTemplate.replace('{{ dataclass }}', $option.attr('data-dkgroupclass')); } // Support only one level as per W3C standard $option.children('option').each( function (index, element) { oTemplate = optionTemplate, oTemplate = oTemplate.replace('{{ value }}', $(element).val()); oTemplate = oTemplate.replace('{{ current }}', (_notBlank($(element).val()) === view.value) ? current : ''); oTemplate = oTemplate.replace('{{ text }}', $(element).text()); gTemplate += oTemplate; } ); options[options.length] = gTemplate; } else { 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 $('.dk_toggle').live('click', function (e) { var $dk = $(this).parents('.dk_container').first(); _toggleOpenCloseDropDown($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);