I have a select
element with grouped option
s. I need to select (or deselect) all option
s in an optgroup
when an option
is clicked. I also need to be able to have multiple optgroup
s being selected at once.
The way I wish it to work is this:
Looking at other answers on Stack Overflow I created the following:
HTML:
<select multiple="multiple" size="10">
<optgroup label="Queen">
<option value="Mercury">Freddie</option>
<option value="May">Brian</option>
<option value="Taylor">Roger</option>
<option value="Deacon">John</option>
</optgroup>
<optgroup label="Pink Floyd">
<option value="Waters">Roger</option>
<option value="Gilmour">David</option>
<option value="Mason">Nick</option>
<option value="Wright">Richard</option>
</optgroup>
</select>
jQuery:
$('select').click(selectSiblings);
function selectSiblings(ev) {
var clickedOption = $(ev.target);
var siblings = clickedOption.siblings();
if (clickedOption.is(":selected")) {
siblings.attr("selected", "selected");
} else {
siblings.removeAttr("selected");
}
}
I made an example here: http://jsfiddle.net/mflodin/Ndkct/ (Sadly jsFiddle doesn't seem to support IE8 any more.)
This works as expected in Firefox (16.0), but in IE8 it doesn't work at all. From other answers I have found out that IE8 can't handle the click
event on optgroup
or option
, which is the reason I bind it to the select
and then use $(ev.target)
. But in IE8 the $(ev.target)
still points to the entire select
and not to the option
that was clicked. How can I find out which option
(or containing optgroup
) that was clicked and whether it was selected or deselected?
Another unexpected behaviour, but minor in comparison, is that in Chrome (20.0) the deselect doesn't happen until the mouse leaves the select
. Does anybody know a workaround for this?
not an answer to the specific question of select option cross browser issues, I think that's been answered already, but a solution to the problem of selecting the data and passing it back to the server would be to change your mark up to something which better suits multi selection
if you use checkboxes instead, you can put them in a list and add enhanching js to select all and deselect all
http://jsfiddle.net/Ndkct/43/
<dl>
<dt>Queen</dt>
<dd>
<ul>
<li>
<input id="Mercury" name="bandmembers" value="Mercury" type="checkbox" />
<label for="Mercury">Freddie</label>
</li>
<li>
<input id="May" name="bandmembers" value="May" type="checkbox" />
<label for="May">Brian</label>
</li>
<li>
<input id="Taylor" name="bandmembers" value="Taylor" type="checkbox" />
<label for="Taylor">Roger</label>
</li>
<li>
<input id="Deacon" name="bandmembers" value="Deacon" type="checkbox" />
<label for="Deacon">John</label>
</li>
</ul>
</dd>
<dt>Pink Floyd</dt> ...
The js is now simple enough
$("dt")
.css("cursor","pointer")
.click(function(){
var $el = $(this).next();
var $checks = $el.find(":checkbox");
if($checks.is(":not(:checked)")){
$checks.attr("checked",true);
}else{
$checks.attr("checked",false);
}
});
(this fiddle http://jsfiddle.net/Ndkct/44/ adds a parent checkbox to make it more usable)
Since Internet Explorer as of version 10 still does not support any useful events on optgroup
or option
, any cross-browser solution to this issue can't depend on direct events from those elements.
Note: MSDN documentation claims that optgroup
supports click
events, but as of IE 10 that still does not seem to be a reality: http://msdn.microsoft.com/en-us/library/ie/ms535876(v=vs.85).aspx
I tested the following in Chrome 27, Firefox 20, Safari 6, IE 9, and IE 10 (I don't have access to IE 8 currently).
Here's an example you can play with: http://jsfiddle.net/potatosalad/Ndkct/38/
// Detect toggle key (toggle selection)
$('select')
.on('mousedown', function(event) {
// toggleKey = Command for Mac OS X; Control for others
var toggleKey = (!event.metaKey && event.ctrlKey && !(/Mac OS/.test(navigator.userAgent))) ? event.ctrlKey : event.metaKey;
if (toggleKey === true) {
$(this).data('toggleKey', true);
}
$(this).data('_mousedown', true);
})
.on('mouseup', function() {
$(this).data('_mousedown', null);
});
// Chrome fix for mouseup outside select
$(document).on('mouseup', ':not(select)', function() {
var $select = $('select');
if ($select.data('_mousedown') === true) {
$select.data('_mousedown', null);
$select.trigger('change');
}
});
$('select')
.on('focus', storeState)
.on('change', changeState);
function storeState() {
$(this).data('previousGroup', $('option:selected', this).closest('optgroup'));
$(this).data('previousVal', $(this).val());
};
function changeState() {
var select = this,
args = Array.prototype.slice.call(arguments);
var previousGroup = $(select).data('previousGroup'),
previousVal = $(select).data('previousVal');
var currentGroup = null,
currentVal = $(select).val();
var selected = $('option:selected', select),
groups = selected.closest('optgroup');
console.log("[change] %o -> %o", previousVal, currentVal);
if ((previousVal === currentVal) && (groups && groups.length == 1)) {
// selected options have not changed
// AND
// only 1 optgroup is selected
// -> do nothing
} else if (currentVal === null || currentVal.length === 0) {
// zero options selected
// -> store state and do nothing
storeState.apply(select, args);
} else { // a select/deselect change has occurred
if ($(select).data('toggleKey') === true
&& (previousVal !== null && previousGroup !== null && groups !== null)
&& (previousVal.length > currentVal.length)
&& (previousGroup.length === groups.length)
&& (previousGroup.get(0) === groups.get(0))) {
// options within the same optgroup have been deselected
// -> deselect all and store state
$(select).val(null);
storeState.apply(select, args);
} else if (currentVal.length === 1) {
// single option selected
// -> use groups
currentGroup = groups;
} else if (groups.length === 1) {
// multiple options selected within same optgroup
// -> use groups
currentGroup = groups;
} else {
// multiple options selected spanning multiple optgroups
// -> use last optgroup
currentGroup = groups.last();
}
}
$(select).data('toggleKey', null);
if (currentGroup !== null) {
$('optgroup', select).not(currentGroup).children(':selected').attr('selected', false);
$(currentGroup).children(':not(:selected)').attr('selected', true);
storeState.apply(select, args);
}
};
It works by storing the previously selected group and options on the select
element and determining which optgroup
should be selected (or deselected).
Some behaviors can be changed based on the developers needs. For example in the case of selecting multiple options
that span multiple optgroups
(by clicking and dragging down as seen in screenshot):
The default conflict resolution for this case is to always use the last optgroup
, but could be changed to always use the first one, or the optgroup
with the largest number of already selected options.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With