/* Stylish Select 0.4.1 - $ plugin to replace a select drop down box with a stylable unordered list http://github.com/sko77sun/Stylish-Select Requires: jQuery 1.3 or newer Contributions from Justin Beasley: http://www.harvest.org/ & Anatoly Ressin: http://www.artazor.lv/ Dual licensed under the MIT and GPL licenses. */ (function($){ //add class to html tag $('html').addClass('stylish-select'); //create cross-browser indexOf Array.prototype.indexOf = function (obj, start) { for (var i = (start || 0); i < this.length; i++) { if (this[i] == obj) { return i; } } } //utility methods $.fn.extend({ getSetSSValue: function(value){ if (value){ //set value and trigger change event $(this).val(value).change(); return this; } else { return $(this).find(':selected').val(); } }, //added by Justin Beasley resetSS: function(){ var oldOpts = $(this).data('ssOpts'); $this = $(this); $this.next().remove(); //unbind all events and redraw $this.unbind('.sSelect').sSelect(oldOpts); } }); $.fn.sSelect = function(options) { return this.each(function(){ var defaults = { defaultText: 'Please select', animationSpeed: 0, //set speed of dropdown ddMaxHeight: '', //set css max-height value of dropdown containerClass: '' //additional classes for container div }; //initial variables var opts = $.extend(defaults, options), $input = $(this), $containerDivText = $('
'), $containerDiv = $('
'), $newUl = $(''), itemIndex = -1, currentIndex = -1, keys = [], prevKey = false, prevented = false, $newLi; //added by Justin Beasley $(this).data('ssOpts',options); //build new list $containerDiv.insertAfter($input); $containerDiv.attr("tabindex", $input.attr("tabindex") || "0"); $containerDivText.prependTo($containerDiv); $newUl.appendTo($containerDiv); $input.hide(); //added by Justin Beasley (used for lists initialized while hidden) $containerDivText.data('ssReRender',!$containerDivText.is(':visible')); //test for optgroup if ($input.children('optgroup').length == 0){ $input.children().each(function(i){ var option = $(this).html(); var key = $(this).val(); //add first letter of each word to array keys.push(option.charAt(0).toLowerCase()); if ($(this).attr('selected') == true){ opts.defaultText = option; currentIndex = i; } $newUl.append($('
  • '+option+'
  • ').data('key', key)); }); //cache list items object $newLi = $newUl.children().children(); } else { //optgroup $input.children('optgroup').each(function(){ var optionTitle = $(this).attr('label'), $optGroup = $('
  • '+optionTitle+'
  • '); $optGroup.appendTo($newUl); var $optGroupList = $(''); $optGroupList.appendTo($optGroup); $(this).children().each(function(){ ++itemIndex; var option = $(this).html(); var key = $(this).val(); //add first letter of each word to array keys.push(option.charAt(0).toLowerCase()); if ($(this).attr('selected') == true){ opts.defaultText = option; currentIndex = itemIndex; } $optGroupList.append($('
  • '+option+'
  • ').data('key',key)); }) }); //cache list items object $newLi = $newUl.find('ul li a'); } //get heights of new elements for use later var newUlHeight = $newUl.height(), containerHeight = $containerDiv.height(), newLiLength = $newLi.length; //check if a value is selected if (currentIndex != -1){ navigateList(currentIndex, true); } else { //set placeholder text $containerDivText.text(opts.defaultText); } //decide if to place the new list above or below the drop-down function newUlPos(){ var containerPosY = $containerDiv.offset().top, docHeight = jQuery(window).height(), scrollTop = jQuery(window).scrollTop(); //if height of list is greater then max height, set list height to max height value if (newUlHeight > parseInt(opts.ddMaxHeight)) { newUlHeight = parseInt(opts.ddMaxHeight); } containerPosY = containerPosY-scrollTop; if (containerPosY+newUlHeight >= docHeight){ $newUl.css({ top: '-'+newUlHeight+'px', height: newUlHeight }); $input.onTop = true; } else { $newUl.css({ top: containerHeight+'px', height: newUlHeight }); $input.onTop = false; } } //run function on page load newUlPos(); //run function on browser window resize $(window).bind('resize.sSelect scroll.sSelect', newUlPos); //positioning function positionFix(){ $containerDiv.css('position','relative'); } function positionHideFix(){ $containerDiv.css('position','static'); } $containerDivText.bind('click.sSelect',function(event){ event.stopPropagation(); //added by Justin Beasley if($(this).data('ssReRender')) { newUlHeight = $newUl.height('').height(); containerHeight = $containerDiv.height(); $(this).data('ssReRender',false); newUlPos(); } //hide all menus apart from this one $('.newList').not($(this).next()).hide() .parent() .css('position', 'static') .removeClass('newListSelFocus'); //show/hide this menu $newUl.toggle(); positionFix(); //scroll list to selected item $newLi.eq(currentIndex).focus(); }); $newLi.bind('click.sSelect',function(e){ var $clickedLi = $(e.target); //update counter currentIndex = $newLi.index($clickedLi); //remove all hilites, then add hilite to selected item prevented = true; navigateList(currentIndex); $newUl.hide(); $containerDiv.css('position','static');//ie }); $newLi.bind('mouseenter.sSelect', function(e) { var $hoveredLi = $(e.target); $hoveredLi.addClass('newListHover'); } ).bind('mouseleave.sSelect', function(e) { var $hoveredLi = $(e.target); $hoveredLi.removeClass('newListHover'); } ); function navigateList(currentIndex, init){ $newLi.removeClass('hiLite') .eq(currentIndex) .addClass('hiLite'); if ($newUl.is(':visible')){ $newLi.eq(currentIndex).focus(); } var text = $newLi.eq(currentIndex).html(); var val = $newLi.eq(currentIndex).parent().data('key'); //page load if (init == true){ $input.val(val); $containerDivText.text(text); return false; } try { $input.val(val) } catch(ex) { // handle ie6 exception $input[0].selectedIndex = currentIndex; } $input.change(); $containerDivText.text(text); } $input.bind('change.sSelect',function(event){ $targetInput = $(event.target); //stop change function from firing if (prevented == true){ prevented = false; return false; } $currentOpt = $targetInput.find(':selected'); //currentIndex = $targetInput.find('option').index($currentOpt); currentIndex = $targetInput.find('option').index($currentOpt); //navigateList(currentIndex, true); }); //handle up and down keys function keyPress(element) { //when keys are pressed $(element).unbind('keydown.sSelect').bind('keydown.sSelect',function(e){ var keycode = e.which; //prevent change function from firing prevented = true; switch(keycode) { case 40: //down case 39: //right incrementList(); return false; break; case 38: //up case 37: //left decrementList(); return false; break; case 33: //page up case 36: //home gotoFirst(); return false; break; case 34: //page down case 35: //end gotoLast(); return false; break; case 13: case 27: $newUl.hide(); positionHideFix(); return false; break; } //check for keyboard shortcuts keyPressed = String.fromCharCode(keycode).toLowerCase(); var currentKeyIndex = keys.indexOf(keyPressed); if (typeof currentKeyIndex != 'undefined') { //if key code found in array ++currentIndex; currentIndex = keys.indexOf(keyPressed, currentIndex); //search array from current index if (currentIndex == -1 || currentIndex == null || prevKey != keyPressed) currentIndex = keys.indexOf(keyPressed); //if no entry was found or new key pressed search from start of array navigateList(currentIndex); //store last key pressed prevKey = keyPressed; return false; } }); } function incrementList(){ if (currentIndex < (newLiLength-1)) { ++currentIndex; navigateList(currentIndex); } } function decrementList(){ if (currentIndex > 0) { --currentIndex; navigateList(currentIndex); } } function gotoFirst(){ currentIndex = 0; navigateList(currentIndex); } function gotoLast(){ currentIndex = newLiLength-1; navigateList(currentIndex); } $containerDiv.bind('click.sSelect',function(e){ e.stopPropagation(); keyPress(this); }); $containerDiv.bind('focus.sSelect',function(){ $(this).addClass('newListSelFocus'); keyPress(this); }); $containerDiv.bind('blur.sSelect',function(){ $(this).removeClass('newListSelFocus'); }); //hide list on blur $(document).bind('click.sSelect',function(){ $containerDiv.removeClass('newListSelFocus'); $newUl.hide(); positionHideFix(); }); //add classes on hover $containerDivText.bind('mouseenter.sSelect', function(e) { var $hoveredTxt = $(e.target); $hoveredTxt.parent().addClass('newListSelHover'); } ).bind('mouseleave.sSelect', function(e) { var $hoveredTxt = $(e.target); $hoveredTxt.parent().removeClass('newListSelHover'); } ); //reset left property and hide $newUl.css({ left: '0', display: 'none', visibility: 'visible' }); }); }; })(jQuery);