/**************************************************************************************** Script to make select-multiple inputs respond better to mouse-only interaction Written by Mark Wilton-Jones, 27/01/2008-06/02/2008 Version 1.0 ***************************************************************************************** Please see http://www.howtocreate.co.uk/jslibs/ for details and a demo of this script Please see http://www.howtocreate.co.uk/jslibs/termsOfUse.html for terms of use Recommended: use checkboxes instead of this script. To use: Inbetween the <head> tags, put: <script src="PATH TO SCRIPT/mousemultiselect.js" type="text/javascript"></script> The script offers three functions for adding more convenient mouse interaction to a select-multiple input: mouseMultiSelect(object: selectInput) selectInput = a reference to a select-multiple input. Prepares the given select-multiple input. mouseMultiSelects() Searches for all select-multiple inputs that are currently on the page, and prepares them. onloadMouseMultiSelects() Tells the script to search for all select-multiple inputs as soon as the page has loaded, or as soon as the HTML has been parsed (depending on the browser). Can be called at any time before the page completes loading. If the options for an input are going to be created by a script, and some of them may have their .selected property set by script, then it is important not to tell this script to set them up until all of their options have been correctly set up. Each select-multiple input will only be set up once. Repeated attempts to set up an input will be ignored. ___________________________________________________________________________________________*/ function mouseMultiSelect(oSel) { function updateSelect(oSel) { //store statuses and set colours to reduce flicker for( i = 0; i < oSel.options.length; i++ ) { oSel.selectedList[i] = oSel.options[i].selected; //does not style in Opera - dynamic changes to option styles do not work after initial paint - pity if( !window.opera ) { //could use Highlight/HighlightText but it's nicer to see which ones will change oSel.options[i].style.background = oSel.selectedList[i] ? 'teal' : ''; oSel.options[i].style.color = oSel.selectedList[i] ? 'white' : ''; } } oSel.className += ''; //relieve IE 8 redraw bug } function selectChange(e) { var oSel = e.currentTarget ? e.currentTarget : event.srcElement; while( !oSel.options ) { oSel = oSel.parentNode; } //just in case IE starts making the OPTION or OPTGROUP the srcElement if( e.ctrlKey || e.altKey || e.shiftKey || e.metaKey || ( e.target && e.target != oSel && !e.target.selected ) ) { //don't change it if the user is using modifier keys already //browsers (like NetFront) can already deselect when clicking selected options - detect if they unselected the target option //also makes clicks and drags on optgroup slightly more resilient //also prevents accidental mouseup from clearing the selection except when over the only selected option updateSelect(oSel); return; } //for drag-select and multi-click selection (like NetFront), make it OR instead of XOR - more intuitive and avoids breaking multi-click var multi = 0, i; for( i = 0; i < oSel.options.length && multi < 2; i++ ) { if( oSel.options[i].selected ) { multi++; } } multi = multi > 1; //set .selected as needed for( i = 0; i < oSel.options.length; i++ ) { if( multi ) { oSel.options[i].selected = oSel.selectedList[i] || oSel.options[i].selected; } else { oSel.options[i].selected = !oSel.selectedList[i] != !oSel.options[i].selected; } } updateSelect(oSel); } function updateWithoutChange(e) { e = e.currentTarget ? e.currentTarget : event.srcElement; while( !e.options ) { e = e.parentNode; } //just in case IE starts making the OPTION or OPTGROUP the srcElement updateSelect(e); } if( oSel.multiple && !oSel.selectedList ) { oSel.selectedList = []; updateSelect(oSel); if( oSel.addEventListener ) { //click does not fire on drag-select for many browsers oSel.addEventListener('mouseup',selectChange,false); oSel.addEventListener('keydown',updateWithoutChange,false); oSel.addEventListener('keypress',updateWithoutChange,false); oSel.addEventListener('keyup',updateWithoutChange,false); } else if( oSel.attachEvent ) { //IE has trouble with mouseup but click also catches drag-select oSel.attachEvent('onclick',selectChange); oSel.attachEvent('onkeydown',updateWithoutChange); oSel.attachEvent('onkeypress',updateWithoutChange); oSel.attachEvent('onkeyup',updateWithoutChange); } } } function mouseMultiSelects() { for( var i = 0, s = document.getElementsByTagName('select'); i < s.length; i++ ) { mouseMultiSelect(s[i]); } } function onloadMouseMultiSelects() { if( window.addEventListener ) { window.addEventListener('DOMContentLoaded',mouseMultiSelects,true); window.addEventListener('load',mouseMultiSelects,true); } else if( document.addEventListener ) { document.addEventListener('DOMContentLoaded',mouseMultiSelects,true); document.addEventListener('load',mouseMultiSelects,true); } else if( window.attachEvent ) { window.attachEvent('onload',mouseMultiSelects); } }