Refactored select plugin.

This commit is contained in:
scottjehl 2010-10-11 14:41:17 -04:00
parent 01b9924f0b
commit effbaa9f58
2 changed files with 180 additions and 190 deletions

View file

@ -12,15 +12,10 @@
label.ui-select { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; display: block; }
/*listbox*/
.ui-listbox { position: relative; margin: 8px; padding: 0; z-index: 100; }
.ui-page-content-hidden, .ui-listbox-hidden { display: none; }
.ui-listbox-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
.ui-listbox-overlay { position: absolute; z-index: 999; width: 300px; margin-left: -150px; }
.ui-listbox-header { background: none; border: 0; }
.ui-listbox-overlay .ui-listbox-header { display: none; }
.ui-listbox-list { list-style: none; margin: 5px; padding: 0; }
.ui-listbox .ui-listbox-option { margin: 0; text-align: left; }
.ui-li .ui-icon { display: none; }
.ui-listbox { position: absolute; padding: 0; z-index: 100; width: 80%; padding: 10px; }
.ui-listbox-hidden { top: -999999px; left: -99999px; }
.ui-listbox-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 99; }
.ui-screen-hidden, .ui-li .ui-icon { display: none; }
.ui-btn-active .ui-icon { display: block; }
/*used in listbox - to be removed/replaced with page-style hiding*/

View file

@ -7,210 +7,205 @@
(function($){
$.fn.customSelect = function(options){
return $(this).each(function(){
var select = $(this)
.attr( "tabindex", "-1" )
.wrap( "<div class='ui-select'>" ),
selectID = select.attr( "id" ),
label = $( "label[for="+ selectID +"]" )
.addClass( "ui-select" ),
//extendable options
var o = $.extend({
closeText: 'close',
chooseText: 'Choose one:'
}, options);
var select = $(this),
label = $('label[for='+ select.attr('id') +']').addClass('ui-select'),
buttonId = select.attr('id')+'-button',
menuId = select.attr('id')+'-menu',
thisPage = $(this).closest('.ui-page'),
//create menu button
button = $('<a href="#">'+ $(this.options.item(this.selectedIndex)).text() +'</a>')
.buttonMarkup({
iconpos: 'right',
icon: 'arrow-d'
})
.attr({
'role': 'button',
'title': 'select menu',
'id': buttonId,
'aria-haspopup': 'true',
'aria-owns': menuId
})
// FIXME why mousedown? doesn't seem to work on desktop browsers
.mousedown(function(){
select.trigger('showmenu');
return false;
})
.insertBefore(select),
menuPage = $( "<div data-role='dialog'>"+
"<div data-role='header' data-theme='b'>"+
"<a href='#' class='ui-btn-left' data-icon='delete' data-iconpos='notext'>"+ o.closeText +"</a><div class='ui-title'>" + o.chooseText + "</div></div>"+
"<div data-role='content'></div></div>"+
"</div>" )
.appendTo( "body" )
.page(),
menuPageContent = menuPage.find('.ui-content'),
listbox = $('<div class="ui-listbox ui-listbox-overlay ui-body-a ui-overlay-shadow ui-corner-all pop"></div>'),
list = $('<ul class="ui-listbox-list" id="'+ menuId +'" role="listbox" aria-labelledby="'+ buttonId +'"></ul>')
.appendTo(listbox);
o = $.extend({
closeText: "close",
chooseText: label.text()
}, options),
buttonId = selectID + "-button",
menuId = selectID + "-menu",
thisPage = select.closest( ".ui-page" ),
menuType,
currScroll,
button = $( "<a>", {
"href": "#",
"role": "button",
"title": "select menu",
"id": buttonId,
"aria-haspopup": "true",
"aria-owns": menuId
})
.text( $( this.options.item(this.selectedIndex) ).text() )
.insertBefore( select )
.buttonMarkup({
iconpos: 'right',
icon: 'arrow-d'
}),
menuPage = $( "<div data-role='dialog' data-theme='a'>" +
"<div data-role='header' data-theme='b'>" +
"<a href='#' class='ui-btn-left' data-icon='delete' data-iconpos='notext'>"+ o.closeText +"</a>"+
"<div class='ui-title'>" + o.chooseText + "</div>"+
"</div>"+
"<div data-role='content'></div>"+
"</div>" )
.appendTo( "body" )
.page(),
menuPageContent = menuPage.find( ".ui-content" ),
screen = $( "<div>", {
"class": "ui-listbox-screen ui-overlay ui-screen-hidden fade"
})
.appendTo( thisPage ),
listbox = $( "<div>", { "class": "ui-listbox ui-listbox-hidden ui-body-a ui-overlay-shadow ui-corner-all pop"} )
.insertAfter(screen),
list = $( "<ul>", {
"class": "ui-listbox-list",
"id": menuId,
"role": "listbox",
"aria-labelledby": buttonId
})
.appendTo( listbox );
//populate menu
select.find('option').each(function(i){
var thisclass = select[0].selectedIndex == i ? 'ui-btn-active' : '';
var thisselected = select[0].selectedIndex == i ? ' aria-selected="true"' : '';
$('<li></li>')
.addClass(thisclass)
.attr("data-icon","checkbox-on")
.append( $('<a href="#" class="ui-listbox-option"'+thisselected+' role="option">'+ $(this).text() +'</a>') )
.appendTo(list);
select.find( "option" ).each(function( i ){
var selected = (select[0].selectedIndex == i),
anchor = $("<a>", {
"aria-selected": selected,
"role": "option",
"href": "#"
})
.text( $(this).text() );
$( "<li>", {
"class": selected ? "ui-btn-active" : '',
"data-icon": "checkbox-on"
})
.append( anchor )
.appendTo( list );
});
//now populated, create listview
list.listview();
//select properties,events
function showmenu(){
var menuHeight = list.outerHeight();
currScroll = [ $(window).scrollLeft(), $(window).scrollTop() ];
if( menuHeight > window.innerHeight - 80 ){
menuType = "page";
$.changePage(thisPage, menuPage, "slideup", false);
menuPageContent.append( list );
}
else {
menuType = "overlay";
screen
.height( $(document).height() )
.removeClass('ui-screen-hidden');
listbox
.append( list )
.removeClass( "ui-listbox-hidden" )
.css({
top: $(window).scrollTop() + (window.innerHeight/2),
"margin-top": -menuHeight/2,
left: window.innerWidth/2,
"margin-left": -1* listbox.outerWidth() / 2
});
}
};
function hidemenu(){
if(menuType == "page"){
$.changePage(menuPage, thisPage, "slideup", true);
}
else{
screen.addClass( "ui-screen-hidden" );
listbox.addClass( "ui-listbox-hidden" ).removeAttr( "style" );
}
};
//page show/hide events
menuPage
.bind("pageshow", function(){
list.find( ".ui-btn-active" ).focus();
return false;
})
.bind("pagehide", function(){
window.scrollTo(currScroll[0], currScroll[1]);
//select.focus();
return false;
});
//select properties,events
select
.change(function(){
var ele = select.get(0);
button.find('.ui-btn-text').text($(ele.options.item(ele.selectedIndex)).text());
var $el = select.get(0);
button.find( ".ui-btn-text" ).text( $($el.options.item($el.selectedIndex)).text() );
})
.focus(function(){
$(this).blur();
button.focus();
});
//button events
button
.tap(function(){
showmenu();
return false;
})
.wrap('<div class="ui-select"></div>')
.attr('tabindex','-1')
.bind('showmenu',function(){
$(document).data('currScroll', [$(window).scrollLeft(), $(window).scrollTop()]);
if(menuHeight > window.innerHeight - 80){
menuType = "page";
}
else {
menuType = "overlay";
listbox.css({
top: $(window).scrollTop() + (window.innerHeight/2),
'margin-top': -menuHeight/2,
left: window.innerWidth/2
});
}
screen.css({width: $(window).width(), height: $(document).height()}).removeClass('ui-helper-hidden out').addClass('in');
listbox.removeClass('ui-listbox-hidden');
var selectedLI = list.find('.ui-btn-active').focus();
if(menuType == "page"){
list.appendTo(menuPageContent);
$.changePage(thisPage, menuPage, 'slideup', false);
}
else{
list.appendTo(listbox).addClass('in');
}
})
.bind('hidemenu',function(){
if(menuType == "page"){
$.changePage(menuPage, thisPage, 'slideup', true);
}
else{
screen.addClass('ui-helper-hidden out');
listbox.removeClass('in').addClass('ui-listbox-hidden out');
}
setTimeout(function(){
button.focus();
if(menuType == "page"){
$(window)[0].scrollTo($(document).data('currScroll')[0], $(document).data('currScroll')[1]);
if(button.offset().top > window.innerHeight){
$(window)[0].scrollTo(0, button.offset().top);
}
}
}, 50);
});
//apply click events for items
list.find('a')
.click(function(){
list.find('[aria-selected=true]').removeClass('ui-btn-active').attr('aria-selected',false);
$(this).addClass('ui-btn-active').attr('aria-selected', true);
var newIndex = list.find('a').index(this),
prevIndex = select[0].selectedIndex;
select[0].selectedIndex = list.find('a').index(this);
if(newIndex != prevIndex){
select.trigger('change');
}
select.trigger('hidemenu');
return false;
});
//back button
menuPage.find('.ui-header a').click(function(){
select.trigger('hidemenu');
//apply click events for items
list
.click(function(e){
e.stopImmediatePropagation();
})
.find("li")
.click(function(e){
return false; //prevent listview click behavior
})
.tap(function(){
//deselect active
list.find( "li" )
.removeClass( "ui-btn-active" )
.children(0)
.attr( "aria-selected", "false");
//select this one
$(this)
.addClass( "ui-btn-active" )
.find( "a" )
.attr( "aria-selected", "true");
//update select
var newIndex = list.find( "li" ).index( this ),
prevIndex = select[0].selectedIndex;
select[0].selectedIndex = newIndex;
//trigger change event
if(newIndex !== prevIndex){
select.trigger( "change" );
}
//hide custom select
hidemenu();
return false;
});
//menu page back button
menuPage.find( ".ui-btn-left" ).click(function(){
hidemenu();
return false;
});
//keyboard events for menu items
list.keydown(function(event){
if( !$(this).is('.ui-btn') ){
//switch logic based on which key was pressed
switch(event.keyCode){
//up or left arrow keys
case 37:
case 38:
//if there's a previous option, focus it
if( $(event.target).parent().prev().length ){
$(event.target).blur().parent().prev().find('a').eq(0).focus();
}
//prevent native scroll
return false;
break;
//down or right arrow keys
case 39:
case 40:
//if there's a next option, focus it
if( $(event.target).parent().next().length ){
$(event.target).blur().parent().next().find('a').eq(0).focus();
}
//prevent native scroll
return false;
break;
//if enter or space is pressed in menu, trigger click
case 13:
case 32:
$(event.target).trigger('click'); //should trigger select
return false;
break;
//tab returns focus to the menu button, and then automatically shifts focus to the next focusable element on the page
case 9:
select.trigger('hidemenu');
break;
event.preventDefault();
}
}
});
//add list to page
var screen = $('<div class="ui-listbox-screen ui-overlay ui-helper-hidden fadeto"></div>')
.insertAfter(thisPage);
listbox.insertAfter(screen);
//get height for figuring out overlay vs. paged
var menuHeight = list.outerHeight();
//menu type
var menuType = "page";
//hide it
listbox.addClass('ui-listbox-hidden');
//hide on outside click
screen.click(function(){
listbox.addClass('ui-listbox-hidden out');
screen.addClass('ui-helper-hidden out');
hidemenu();
return false;
});
});
};