From 0a9f98fbed2a89e0a2531ee1f89c733cc323c1c5 Mon Sep 17 00:00:00 2001 From: Philippe Masset Date: Sun, 20 Jan 2013 22:29:07 +0100 Subject: [PATCH] Initial commit --- examples/index.html | 72 +++++++++ examples/main.js | 7 + jquery.simpleselect.css | 92 +++++++++++ jquery.simpleselect.js | 302 +++++++++++++++++++++++++++++++++++++ jquery.simpleselect.min.js | 8 + 5 files changed, 481 insertions(+) create mode 100644 examples/index.html create mode 100644 examples/main.js create mode 100644 jquery.simpleselect.css create mode 100644 jquery.simpleselect.js create mode 100644 jquery.simpleselect.min.js diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 0000000..58dfe57 --- /dev/null +++ b/examples/index.html @@ -0,0 +1,72 @@ + + + + + + + + + + + + +
+

Regular select

+ +
+ +
+

Select with optgroups

+ +
+ +
+

Select with optgroups and optgroup labels

+ +
+ + \ No newline at end of file diff --git a/examples/main.js b/examples/main.js new file mode 100644 index 0000000..f573f25 --- /dev/null +++ b/examples/main.js @@ -0,0 +1,7 @@ +$(document).bind("ready", function(){ + $("select") + .simpleselect() + .bind("change.simpleselect", function(){ + console.log('change; new value='+ $(this).val()); + }); +}); \ No newline at end of file diff --git a/jquery.simpleselect.css b/jquery.simpleselect.css new file mode 100644 index 0000000..eaf5641 --- /dev/null +++ b/jquery.simpleselect.css @@ -0,0 +1,92 @@ +.hidden_select_container { + width: 0; + height: 0; + overflow: hidden; +} + +.simpleselect { + position: relative; + color: #333; +} + +.simpleselect, +.simpleselect .options { + width: 200px; +} + +/* compensation for the border */ +.simpleselect .options { + width: 198px; +} + +.simpleselect, +.simpleselect .placeholder, +.simpleselect .options .option { + height: 40px; + line-height: 40px; +} + +.simpleselect .placeholder, +.simpleselect .options .option, +.simpleselect .options .optgroup .optgroup-label { + padding: 0 10px; + cursor: pointer; +} + + +.simpleselect .options .optgroup .optgroup-label { + cursor: default; + font-weight: bold; +} + +.simpleselect .options .optgroup .option, +.simpleselect.has-optgroup .placeholder { + padding: 0 20px; +} + +.simpleselect .placeholder, +.simpleselect .options { + background: #e3e3e3; + border: 1px solid #bbb; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: inset 0 0 1px 1px #f6f6f6; + -moz-box-shadow: inset 0 0 1px 1px #f6f6f6; + box-shadow: inset 0 0 1px 1px #f6f6f6; +} + +.simpleselect .options { + display: none; + position: absolute; + top: 0; + left: 0; +} + +.simpleselect .options .option, +.simpleselect .options .optgroup, +.simpleselect .options .optgroup .optgroup-label { + border-bottom: 1px solid #bbb; +} + +.simpleselect .options .option { + -webkit-box-shadow: inset 0 0 1px 1px #f6f6f6; + -moz-box-shadow: inset 0 0 1px 1px #f6f6f6; + box-shadow: inset 0 0 1px 1px #f6f6f6; +} + +.simpleselect .placeholder:hover, +.simpleselect.active .placeholder, +.simpleselect .options .option.active { + background: #d9d9d9; +} + +.simpleselect.disabled .placeholder, +.simpleselect.disabled .placeholder:hover { + background: #f3f3f3; + color: #aaa; + border-color: #eee; + cursor: default; +} \ No newline at end of file diff --git a/jquery.simpleselect.js b/jquery.simpleselect.js new file mode 100644 index 0000000..d867760 --- /dev/null +++ b/jquery.simpleselect.js @@ -0,0 +1,302 @@ +/* + * jQuery Simple Select + * http://pioul.fr/jquery-simpleselect + * + * Copyright 2013, Philippe Masset + * Dual licensed under the MIT or GPL Version 2 licenses. + */ +(function($){ + $.fn.simpleselect = function(mixed){ + + var t = $(this); + + // first plugin call + if(typeof(mixed) == 'object' || !mixed){ + + options = $.extend({}, { fadeSpeed: 0 }, mixed); + + t.each(function(){ + var t = $(this); + t.addClass("simpleselected"); + // create the simpleselect + var simpleselect = $('
'), + simpleplaceholder = $('
').appendTo(simpleselect); + if(t.is("[id]")){ + simpleselect.attr("id", 'simpleselect_'+ t.attr("id")); + } + // simpleselect data + var simpleselectdata = { + ref: t, + simpleselect: simpleselect, + can_be_closed: true, // flag used to know when to (not) close the select + set_active: function(){ + if(!this.simpleselect.is(".active") && !this.ref.prop("disabled")){ + this.last_value = this.ref.val(); + this.simpleselect.addClass("active"); + var option_to_activate = this.get_option_to_activate(); + this.set_option_active(option_to_activate); + var simpleselectoptions = this.simpleselect.children(".options"), + document_height = $(document).height(); + simpleselectoptions.fadeTo(0, 0); + simpleselectoptions.fadeTo(options.fadeSpeed, 1); + if(!this.ref.is(":focus")){ + this.ref.focus(); + } + this.position_around_active_option(option_to_activate, document_height); + this.simpleselect.hide().show(0); // force a redraw to avoid a bug found in Webkit + $(document).bind("click.simpleselect", function(){ + var active_simpleselects = $(".simpleselect.active"); + active_simpleselects.each(function(){ + $(this).data("simpleselect").set_inactive(); + }); + $(document).unbind(".simpleselect"); + }); + } + }, + set_inactive: function(){ + if(this.simpleselect.is(".active")){ + this.simpleselect + .removeClass("active") + .children(".options").fadeOut(options.fadeSpeed); + if(this.ref.is(":focus")){ + this.ref.blur(); + } + if(this.last_value != this.ref.val()){ + this.ref.trigger("change.simpleselect"); + } + } + }, + set_option_active: function(simpleoption){ + this.simpleselect.find(".option").removeClass("active"); + simpleoption.addClass("active"); + }, + get_option_to_activate: function(){ + var index_option = this.ref.data("simpleselectref").get_index_selected(); + return this.simpleselect.find(".option:eq("+ index_option +")"); + }, + get_option_active: function(){ + return this.simpleselect.find(".option.active"); + }, + choose_option: function(simpleoption){ + var select_options = this.ref.find("option"), + target_option = select_options.filter(":eq("+ this.simpleselect.find(".options .option").index(simpleoption) +")"), + target_option_index = select_options.index(target_option), + target_value = target_option.val(); + this.ref.val(target_value); + this.simpleselect.children(".placeholder").text(target_option.text()); + }, + position_around_active_option: function(simpleoption, document_height){ + var simpleoptions = this.simpleselect.children(".options"); + // get initial properties back + simpleoptions.css({ + height: 'auto', + 'overflow-y': 'visible' + }); + var simpleoptions_height = simpleoptions_future_height = simpleoptions.height(), + pos = this.simpleselect.offset(), + options_option_height = this.simpleselect.outerHeight(), + optionpos = simpleoption.position(), + free_space_top = pos.top, + free_space_bot = document_height - pos.top - options_option_height, + options_max_height = (free_space_top < free_space_bot)? free_space_top : free_space_bot, + options_max_height_window = ($(window).height() - options_option_height) / 2; + options_max_height = (options_max_height_window < options_max_height)? options_max_height_window : options_max_height; + // if .options maybe needs a scrollbar + var scrollable = false; + if(options_max_height < simpleoptions_height){ + var simpleoptions_top_absolute = options_max_height - options_option_height / 2, + simpleoptions_top = - simpleoptions_top_absolute, + simpleoptions_padding_top = simpleoptions_top_absolute, + simpleoptions_padding_bottom = simpleoptions_top_absolute; + if(optionpos.top < simpleoptions_padding_top){ + simpleoptions_top += simpleoptions_padding_top - optionpos.top; + simpleoptions_padding_top = optionpos.top; + } else { + optionpos.bot = simpleoptions_height - optionpos.top - options_option_height; + if(optionpos.bot < simpleoptions_padding_top){ + simpleoptions_top = - simpleoptions_padding_top; + simpleoptions_padding_top = optionpos.bot; + } + } + var scrollable_optionpos_top = optionpos.top - simpleoptions_padding_top; + if(scrollable_optionpos_top < 0) scrollable_optionpos_top = 0; + simpleoptions_future_height = options_option_height + simpleoptions_padding_top + simpleoptions_padding_bottom; + simpleoptions.css({ + top: simpleoptions_top +'px' + }); + // if .options definitely needs a scrollbar + if(simpleoptions_future_height < simpleoptions_height){ + scrollable = true; + simpleoptions.css({ + height: simpleoptions_future_height +'px', + 'overflow-y': 'scroll' + }); + setTimeout(function(){ // queue the scrolltop to buy some time for some browsers (IE) to render the above code + simpleoptions.scrollTop(scrollable_optionpos_top); + }, 0); + simpleoptions.addClass("scrollable"); + } + } + // if .options needs a scrollbar + if(!scrollable){ + simpleoptions.css({ + top: '-'+ optionpos.top +'px' + }); + simpleoptions.removeClass("scrollable"); + } + } + }; + // add some behaviors to the simpleselect + simpleselect + .data("simpleselect", simpleselectdata) + .bind({ + mousedown: function(e){ + $(this).data("simpleselect").can_be_closed = false; + }, + click: function(e){ + e.stopPropagation(); + var simpleselectdata = $(this).data("simpleselect"); + // handle clicks on the whole select + simpleselectdata.set_active(); + // handle the click on an option + if($(e.target).is(".option")){ + simpleselectdata.choose_option($(e.target)); + simpleselectdata.set_inactive(); + } + }, + mouseup: function(){ + $(this).data("simpleselect").can_be_closed = true; + }, + mouseover: function(e){ + // handle the mouseover on an option + if($(e.target).is(".option")){ + $(this).data("simpleselect").set_option_active($(e.target)); + } + } + }); + // simpleselectref data + var simpleselectrefdata = { + ref: t, + simpleselect: simpleselect, + get_index_selected: function(){ + var select_options = this.ref.find("option"), + option_selected = select_options.filter(":selected"); + if(option_selected.length > 0){ + return select_options.index(option_selected.first()); + } else { + return select_options.index(select_options.first()); + } + }, + disable: function(){ + this.ref.prop("disabled", true); + this.simpleselect.addClass("disabled"); + }, + enable: function(){ + this.ref.prop("disabled", false); + this.simpleselect.removeClass("disabled"); + } + }; + // add some behaviors to the ref simpleselect + t + .data("simpleselectref", simpleselectrefdata) + .bind({ + keydown: function(e){ + var selectrefdata = $(this).data("simpleselectref"), + simpleselectdata = selectrefdata.simpleselect.data("simpleselect"); + // key up || key down + if(e.keyCode == 38 || e.keyCode == 40){ + e.preventDefault(); + var options = selectrefdata.simpleselect.find(".options .option"), + current_option_index = options.index(simpleselectdata.get_option_active()), + last_option_index = options.index(options.last()), + following_option_index = false; + if(e.keyCode == 38 && current_option_index > 0){ + following_option_index = current_option_index - 1; + } else if(e.keyCode == 40 && current_option_index < last_option_index){ + following_option_index = current_option_index + 1; + } + if(following_option_index !== false){ + var following_option = options.eq(following_option_index), + following_option_pos = following_option.position(); + simpleselectdata.set_option_active(following_option); + simpleselectdata.choose_option(following_option); + var simpleoptions = selectrefdata.simpleselect.children(".options"); + if(simpleoptions.is(".scrollable")){ + simpleoptions.scrollTop(following_option_pos.top + simpleoptions.scrollTop()); + } + } + // key enter || key tab + } else if(e.keyCode == 13 || e.keyCode == 9){ + simpleselectdata.choose_option(simpleselectdata.get_option_active()); + if(e.keyCode == 13){ + e.preventDefault(); + simpleselectdata.set_inactive(); + $(this).trigger("pressedEnter.simpleselect"); + } + } + }, + focus: function(){ + $(this).data("simpleselectref").simpleselect.data("simpleselect").set_active(); + }, + blur: function(){ + var simpleselectdata = $(this).data("simpleselectref").simpleselect.data("simpleselect") + if(simpleselectdata.can_be_closed){ + simpleselectdata.set_inactive(); + } + } + }); + // container for options + var simpleoptions = '
'; + // add options to the container + var add_option_to_simpleselect = function(option){ + simpleoptions += '
'+ option.text() +'
'; + }; + // add optgroups to the container + var simpleselect_children = t.children("optgroup, option"); + if(simpleselect_children.filter("optgroup").length){ + simpleselect.addClass("has-optgroup"); + } + simpleselect_children.each(function(){ + var t = $(this); + if(t.is("optgroup")){ + simpleoptions += '
'; + var label = t.attr("label"); + if(label){ + simpleoptions += '
'+ label +'
'; + } + t.children("option").each(function(){ + add_option_to_simpleselect($(this)); + }); + simpleoptions += '
'; + } else { + add_option_to_simpleselect(t); + } + }); + simpleoptions += '
'; + // append the container to the simpleselect + simpleselect.append(simpleoptions); + // fill the placeholder + simpleplaceholder.text(simpleselect.data("simpleselect").get_option_to_activate().text()); + // add the simpleselect to the dom + t.after(simpleselect); + + // hide the select + var hidden_select_container = $('
'); + t.after(hidden_select_container).appendTo(hidden_select_container); + }); + + // additional plugin call (function call) + } else { + + t.each(function(){ + var t = $(this); + if(typeof(t.data('simpleselectref')[mixed]) == 'function'){ + t.data('simpleselectref')[mixed](Array.prototype.slice.call(arguments, 1)); + } + }); + + } + + return t; + }; +})(jQuery); \ No newline at end of file diff --git a/jquery.simpleselect.min.js b/jquery.simpleselect.min.js new file mode 100644 index 0000000..234a6dc --- /dev/null +++ b/jquery.simpleselect.min.js @@ -0,0 +1,8 @@ +/* + * jQuery Simple Select + * http://pioul.fr/jquery-simpleselect + * + * Copyright 2013, Philippe Masset + * Dual licensed under the MIT or GPL Version 2 licenses. + */ +(function(a){a.fn.simpleselect=function(b){var c=a(this);return"object"!=typeof b&&b?c.each(function(){var c=a(this);"function"==typeof c.data("simpleselectref")[b]&&c.data("simpleselectref")[b](Array.prototype.slice.call(arguments,1))}):(options=a.extend({},{fadeSpeed:0},b),c.each(function(){var b=a(this);b.addClass("simpleselected");var c=a('
'),d=a('
').appendTo(c);b.is("[id]")&&c.attr("id","simpleselect_"+b.attr("id"));var e={ref:b,simpleselect:c,can_be_closed:!0,set_active:function(){if(!this.simpleselect.is(".active")&&!this.ref.prop("disabled")){this.last_value=this.ref.val(),this.simpleselect.addClass("active");var b=this.get_option_to_activate();this.set_option_active(b);var c=this.simpleselect.children(".options"),d=a(document).height();c.fadeTo(0,0),c.fadeTo(options.fadeSpeed,1),this.ref.is(":focus")||this.ref.focus(),this.position_around_active_option(b,d),this.simpleselect.hide().show(0),a(document).bind("click.simpleselect",function(){var b=a(".simpleselect.active");b.each(function(){a(this).data("simpleselect").set_inactive()}),a(document).unbind(".simpleselect")})}},set_inactive:function(){this.simpleselect.is(".active")&&(this.simpleselect.removeClass("active").children(".options").fadeOut(options.fadeSpeed),this.ref.is(":focus")&&this.ref.blur(),this.last_value!=this.ref.val()&&this.ref.trigger("change.simpleselect"))},set_option_active:function(a){this.simpleselect.find(".option").removeClass("active"),a.addClass("active")},get_option_to_activate:function(){var a=this.ref.data("simpleselectref").get_index_selected();return this.simpleselect.find(".option:eq("+a+")")},get_option_active:function(){return this.simpleselect.find(".option.active")},choose_option:function(a){var b=this.ref.find("option"),c=b.filter(":eq("+this.simpleselect.find(".options .option").index(a)+")"),e=(b.index(c),c.val());this.ref.val(e),this.simpleselect.children(".placeholder").text(c.text())},position_around_active_option:function(b,c){var d=this.simpleselect.children(".options");d.css({height:"auto","overflow-y":"visible"});var e=simpleoptions_future_height=d.height(),f=this.simpleselect.offset(),g=this.simpleselect.outerHeight(),h=b.position(),i=f.top,j=c-f.top-g,k=j>i?i:j,l=(a(window).height()-g)/2;k=k>l?l:k;var m=!1;if(e>k){var n=k-g/2,o=-n,p=n,q=n;p>h.top?(o+=p-h.top,p=h.top):(h.bot=e-h.top-g,p>h.bot&&(o=-p,p=h.bot));var r=h.top-p;0>r&&(r=0),simpleoptions_future_height=g+p+q,d.css({top:o+"px"}),e>simpleoptions_future_height&&(m=!0,d.css({height:simpleoptions_future_height+"px","overflow-y":"scroll"}),setTimeout(function(){d.scrollTop(r)},0),d.addClass("scrollable"))}m||(d.css({top:"-"+h.top+"px"}),d.removeClass("scrollable"))}};c.data("simpleselect",e).bind({mousedown:function(){a(this).data("simpleselect").can_be_closed=!1},click:function(b){b.stopPropagation();var c=a(this).data("simpleselect");c.set_active(),a(b.target).is(".option")&&(c.choose_option(a(b.target)),c.set_inactive())},mouseup:function(){a(this).data("simpleselect").can_be_closed=!0},mouseover:function(b){a(b.target).is(".option")&&a(this).data("simpleselect").set_option_active(a(b.target))}});var f={ref:b,simpleselect:c,get_index_selected:function(){var a=this.ref.find("option"),b=a.filter(":selected");return b.length>0?a.index(b.first()):a.index(a.first())},disable:function(){this.ref.prop("disabled",!0),this.simpleselect.addClass("disabled")},enable:function(){this.ref.prop("disabled",!1),this.simpleselect.removeClass("disabled")}};b.data("simpleselectref",f).bind({keydown:function(b){var c=a(this).data("simpleselectref"),d=c.simpleselect.data("simpleselect");if(38==b.keyCode||40==b.keyCode){b.preventDefault();var e=c.simpleselect.find(".options .option"),f=e.index(d.get_option_active()),g=e.index(e.last()),h=!1;if(38==b.keyCode&&f>0?h=f-1:40==b.keyCode&&g>f&&(h=f+1),h!==!1){var i=e.eq(h),j=i.position();d.set_option_active(i),d.choose_option(i);var k=c.simpleselect.children(".options");k.is(".scrollable")&&k.scrollTop(j.top+k.scrollTop())}}else(13==b.keyCode||9==b.keyCode)&&(d.choose_option(d.get_option_active()),13==b.keyCode&&(b.preventDefault(),d.set_inactive(),a(this).trigger("pressedEnter.simpleselect")))},focus:function(){a(this).data("simpleselectref").simpleselect.data("simpleselect").set_active()},blur:function(){var b=a(this).data("simpleselectref").simpleselect.data("simpleselect");b.can_be_closed&&b.set_inactive()}});var g='
',h=function(a){g+='
'+a.text()+"
"},i=b.children("optgroup, option");i.filter("optgroup").length&&c.addClass("has-optgroup"),i.each(function(){var b=a(this);if(b.is("optgroup")){g+='
';var c=b.attr("label");c&&(g+='
'+c+"
"),b.children("option").each(function(){h(a(this))}),g+="
"}else h(b)}),g+="
",c.append(g),d.text(c.data("simpleselect").get_option_to_activate().text()),b.after(c);var j=a('
');b.after(j).appendTo(j)})),c}})(jQuery); \ No newline at end of file