I need to combine the functionality of single selection and multiple select into a single control. Specifically, I have a number of options. The first one is mutually exclusive to the others. So, if I select the first one, it needs to uncheck all the others. If one of the others is selected, it must uncheck the first one (if selected). The other options should have no effect on each other.
<select id="myGroup" data-native-menu="false" multiple="multiple" >
<option value="" >Select Group(s)</option>
<option value="-1" selected="selected" >I am alone</option>
<option value="1" >I am not alone 1</option>
<option value="2" >I am not alone 2</option>
<option value="3" >I am not alone 3</option>
</select>
I installed an onchange() handler. So, I know when selections are made. But I can't seem to tell which option just got selected. So, in the example above, if the user select option 3, $(this).val() becomes -1,3. How can I tell that is was "3" that just got selected?
The only thing that I've come up with so far is to keep an array of selected options and then diff the arrays when a new option is selected.
$('select[id=myGroup]').change(function() {
// At this point, I know the sum total of what's been selectec.
// But I don't know which one just got added to the list.
// I want logic that says:
// if -1 just got added, then unselect all the others
// if something else was just added, make sure that -1 is not selected
var selected = $(this).val();
alert(JSON.stringify(selected));
});
Is there a better way?
You only need to keep state for the first option, not all of them:
var firstOption = $("#myGroup > option[value=-1]");
var firstSelectedBefore = firstOption.prop("selected");
$("#myGroup").on("change", function(event) {
if (firstOption.prop("selected") && this.selectedOptions.length > 1) {
if (firstSelectedBefore) { // non-first option just selected
firstOption.prop("selected", false);
} else { // first option just selected
$(this).find("option:not([value=-1])").prop("selected", false);
}
}
firstSelectedBefore = firstOption.prop("selected");
});
Related
I have a jquery code which changes according to the previously selected value of drop-down.
I use it when I have two drop-downs and it works flawlessly.
Now the problem is that I am working with 3 drop-downs and I am unable to modify the code according to 3 drop-downs (reason my being new to jquery).
This is the code:
Jquery:
jQuery(function(){
var $cat = $('select[name=coursename]'),
$items = $('select[name=semno]');
$cat.change(function(){
var $this = $(this).find(':selected'),
rel = $this.attr('rel'),
$set = $items.find('option.' + rel);
if ($set.size() < 0) {
$items.hide();
return;
}
$items.show().find('option').hide();
$set.show().first().prop('selected', true);});});
I used two drop-downs namely, coursename and semno, with this code and it works perfectly fine.
Now I want to add another dropdown, subnm which comes after semno.
So what I exactly want is that when a person makes a particular selection in coursename the relevant items should appear in semno and among those relevant items, when a value is selected, the items are listed on subnm accordingly.
I have used rel and class in the option element.
HTML Code
Course:
<select name="coursename" id="coursename">
<option value="xyz" rel="xyz">XYZ</option>
<option value="abc" rel="abc">ABC</option>
</select>
Semester:
<select name="semno" id="sem">
<option value="one" class="xyz">I</option>
<option value="two" class="xyz">II</option>
<option value="three" class="abc">III</option>
</select>
Subject:
<select name="subnm" id="subnm">
<option value="p">p</option>
<option value="q">q</option>
<option value="r">r</option>
</select>
I guess I will need a rel option on the semno drop-down and then class on the subnm drop-down in accordance to the semno rel.
Forgive me if I am not 100% comprehensible. I am new to this site and I really need help.
Thank You in advance!
Hope this is what you want. I have used the same function for change event for the second select menu.
$items.change(function(){
var $this = $(this).find(':selected'),
rel = $this.attr('rel'),
$set = $third.find('option.' + rel);
if ($set.size() < 0) {
$third.hide();
return;
}
$third.show().find('option').hide();
$set.show().first().prop('selected', true);
});
Also I have triggered the change event for second select in change event handler of first select.
$items.trigger("change");
Please refer this fiddle
I would like to do a select option dependent of another select, i saw there's a way using array with fixed values, but my array is reloaded every time we add a new form field on the form. I would like something like when i select op1, then it just show op1 options on second select.
<select id="id1" name="optionshere">
<option relone="op1">opt one</option>
<option relone="op2">opt two</option>
</select>
<select id="id2" name="resulthere">
<option relone="op1">ans 1 op1</option>
<option relone="op1">ans 2 op2</option>
<option relone="op2">ans 1 op2</option>
</select>
Any idea?
thanks all
Here's a method without jQuery:
When you select an option in the first selectbox, it will hide everything that doesn't match its relone.
var id1 = document.getElementById("id1");
var id2 = document.getElementById("id2");
id1.addEventListener("change", change);
function change() {
for (var i = 0; i < id2.options.length; i++)
id2.options[i].style.display = id2.options[i].getAttribute("relone") == id1.options[id1.selectedIndex].getAttribute("relone") ? "block" : "none";
id2.value = "";
}
change();
<select id="id1" name="optionshere">
<option relone="op1">opt one</option>
<option relone="op2">opt two</option>
</select>
<select id="id2" name="resulthere">
<option relone="op1">ans 1 op1</option>
<option relone="op1">ans 2 op1</option>
<option relone="op2">ans 1 op2</option>
</select>
If Jquery is an option you may go with something like this:
<script type='text/javascript'>
$(function() {
$('#id1').change(function() {
var x = $(this).val();
$('option[relone!=x]').each(function() {
$(this).hide();
});
$('option[relone=x]').each(function() {
$(this).show();
});
});
});
</script>
Then to expand:
There really are many ways in which you can solve this predicament, depending on how variable your pool of answers is going to be.
If you're only interested in using vanilla javascript then let's start with the basics. You're going to want to look into the "onchange" event for your html, so as such:
<select onchange="myFunction()">
Coming right out of the w3schools website, on the Html onchange event attribute:
The onchange attribute fires the moment when the value of the element
is changed.
This will allow you to make a decision based on this element's value. Then inside your js may branch out from here:
You may use Ajax and pass to it that value as a get variable to obtain those options from a separate file.
You may get all options from the second div through a combination of .getElementbyId("id2") and .getElementsByTagName("option") then check for their individual "relone" attribute inside an each loop, and hide those that don't match, and show those that do.
Really, it's all up to what you want to do from there, but I personally would just go for the Jquery approach
I am in the process of building an e-Commerce shop and have hit a small bump in the road for my actual product page. Based on any product options set that would add to the price if selected, I would like to be able to update the price on the page live when these options have been added. I have managed to iterate through every element with a "data-price-effect" attribute attached to them, HOWEVER, when it comes to a select element, I would need to check if the item is selected as an option, each option has their respective price change attribute of course, but the value would only update to the actual select element.
Here is my code upto now:
function updatePrice(){
$('[data-price-effect]').each(function( index ) {
// do something
});
}
Basic HTML set-up to explain further:
<form>
<input type="text" name="foo" onchange="updatePrice();" data-price-effect="10.00" />
<select name="bar" onchange="updatePrice();">
<option selected value="Item1" data-price-effect="5.00">Item 1</option>
<option selected value="Item2" data-price-effect="8.00">Item 2</option>
<option selected value="Item3" data-price-effect="10.00">Item 3</option>
</select>
</form>
I have NO idea how to even logically do this, not even with some huge messy code. Any pointers here from someone more experienced with Javascript?
Instead of having "updatePrice()" on each element, you could have a listener for all form elements for the function:
var EffectElements = $('form input[data-price-effect], form select');
EffectElements.on('change', function() {
var PriceEffect = 0;
EffectElements.each(function() { // Loop through elements
if ($(this).is('select')) { //if this element is a select
$(this).children().each(function() { //Loop through the child elements (options)
if ($(this).is(':selected')) { //if this option is selected
PriceEffect += parseFloat($(this).attr('data-price-effect'));
}
});
} else {
PriceEffect += parseFloat($(this).attr('data-price-effect'));
}
});
});
You could then use the PriceEffect variable to update your price on the website.
Ultimately it's the IS function doing the dirty work you needed ~_o
Working Example
http://jsfiddle.net/j3oh6s3a/
For some reason appending options to a select tag doesn't select the selected='selected' attribute option, instead selects the next option in the list.
Please see the above jfiddle.
<select id="category">
<option value='1'>Categroy 1</option>
<option value='2'>Categroy 2</option>
<option value='3'>Categroy 3</option>
</select>
<select id="sub-category">
<option value='1' data-parentid='1'>Car1</option>
<option value='2' data-parentid='1'>Car2</option>
<option selected='selected' value='3' data-parentid='1'>Car3</option>
<option value='4' data-parentid='1'>Car4</option>
<option value='5' data-parentid='1'>Car5</option>
<option value='6' data-parentid='2'>Car6</option>
<option value='7' data-parentid='2'>Car7</option>
<option value='8' data-parentid='2'>Car8</option>
<option value='9' data-parentid='3'>Car9</option>
<option value='10' data-parentid='3'>Car10</option>
<option value='11' data-parentid='3'>Car11</option>
<option value='12' data-parentid='3'>Car12</option>
</select>
$(document).ready(function(){
var allsuboptions = $('#sub-category option').remove();
var selectedOptions = allsuboptions.filter(function () {
return $(this).data('parentid').toString() === $('#category').val().toString();
});
selectedOptions.appendTo('#sub-category');
});
In the above example Car3 should be selected, but Car4 is selected after appending options to the select.
This is a tricky (and interesting) question.
If you test the fiddle on different browsers you'll see that the selected value changes: Chrome (Car4), IE (Car3), Firefox (Car5). So I have made a slight change to your fiddle to "prove a theory". You can see the changes on this link: http://jsfiddle.net/j3oh6s3a/1/. I only added a log to the filter loop so I can see the selected element in each iteration:
if ($(this).is(":selected")) { console.log("Selected value = " + $(this).val()) };
Now this is what happens (or at least my theory): Once the selected element is removed from the list each browser will proceed however thinks adequate to determine the selected option. And in this case each browser will proceed in a different way:
As the selected option has been removed, Chrome will select automatically (by default) the first option of the remaining in the list (Car4). When this option is sent to the new list, it is automatically selected as it is newer than the previous selected option. The log is: 3, 4.
Internet Explorer does nothing, and copies each element the same way they are without caring about if they are selected or not. The original selected value will be the final selected value (Car3). The log is: 3.
Firefox will proceed like Chrome, but every time that the selected element is removed from the list, the first option of the remaining ones will be selected. The log is: 3, 4, 5, 6, 7, 8, 9, 10, 11, 12; but as the last option inserted in the list is 5, it will be the selected one.
I will check later to see if I can find any information to source this, but it will have to be tomorrow as it's a bit late here.
jQuery .remove and .append internally uses .removeChild and .appendChild methods to remove/insert the child elements.
Theory: removeChild/appendChild maintains the attributes but doesn't maintain the element's property (maintaining the selection state)
When you use removeChild and then appendChild to add the options back, the attributes are maintained, but the property of the element are not maintained. You can read more about .prop() vs .attr() here.
In summary, attributes are initial values defined in the HTML that are parsed to set as properties to the Element by the browser, however setting the attributes doesn't guarantee setting the property.
$(function() {
var categoryDD = document.getElementById('category');
var removedOptions = remove.call(categoryDD.options);
add.call(categoryDD, removedOptions);
});
function add(options) { //add all options
for (var i = 0; i < options.length; i++) {
this.appendChild(options[i]);
}
}
function remove() { //removes all options
var el, returnOpt = [];
while (this.length) {
el = this[0];
returnOpt.push(el.parentNode.removeChild(el));
}
return returnOpt;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<select id="category">
<option value='1'>Categroy 1</option>
<option value='2' selected="selected">Categroy 2</option>
<option value='3'>Categroy 3</option>
<option value='4'>Categroy 4</option>
<option value='5'>Categroy 5</option>
<option value='6'>Categroy 6</option>
</select>
Testing Results:
On testing the above snippet, IE 10 and FF yielded me the same result which is selecting the last option from the drop down, however chrome seems to be bugged as it always selected the next option from the original selection. The results from IE 10 and FF made a little sense as to "Not maintaining the state", however Chrome behavior seems like a bug.
Above is my theory based on my test case, however I couldn't find a legit reference that states the same.
Anyways, tryout below solutions for a consistent output.
Solution 1: Remove only options that are NOT equal to parentId.
$('#sub-category option').filter(function () {
return $(this).data('parentid').toString() !== $('#category').val().toString();
}).remove();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<select id="category">
<option value='1'>Categroy 1</option>
<option value='2'>Categroy 2</option>
<option value='3'>Categroy 3</option>
</select>
<select id="sub-category">
<option value='1' data-parentid='1'>Car1</option>
<option value='2' data-parentid='1'>Car2</option>
<option selected='selected' value='3' data-parentid='1'>Car3</option>
<option value='4' data-parentid='1'>Car4</option>
<option value='5' data-parentid='1'>Car5</option>
<option value='6' data-parentid='2'>Car6</option>
<option value='7' data-parentid='2'>Car7</option>
<option value='8' data-parentid='2'>Car8</option>
<option value='9' data-parentid='3'>Car9</option>
<option value='10' data-parentid='3'>Car10</option>
<option value='11' data-parentid='3'>Car11</option>
<option value='12' data-parentid='3'>Car12</option>
</select>
Solution 2: [based on your original answer] The solution is simple, just get the select value before removing the options and set the selection after using .append.
var $subcategory = $('#sub-category');
var selectedOption = $subcategory.val();
var allsuboptions = $subcategory.find('option').remove();
var selectedOptions = allsuboptions.filter(function() {
return $(this).data('parentid').toString() === $('#category').val().toString();
});
selectedOptions.appendTo('#sub-category');
$subcategory.val(selectedOption);
<select id="category">
<option value='1'>Categroy 1</option>
<option value='2'>Categroy 2</option>
<option value='3'>Categroy 3</option>
</select>
<select id="sub-category">
<option value='1' data-parentid='1'>Car1</option>
<option value='2' data-parentid='1'>Car2</option>
<option selected='selected' value='3' data-parentid='1'>Car3</option>
<option value='4' data-parentid='1'>Car4</option>
<option value='5' data-parentid='1'>Car5</option>
<option value='6' data-parentid='2'>Car6</option>
<option value='7' data-parentid='2'>Car7</option>
<option value='8' data-parentid='2'>Car8</option>
<option value='9' data-parentid='3'>Car9</option>
<option value='10' data-parentid='3'>Car10</option>
<option value='11' data-parentid='3'>Car11</option>
<option value='12' data-parentid='3'>Car12</option>
</select>
What you're not seeing is the difference between the "selected" property and the "selected" attribute.
If you put this at the end of your code you can see it:
// Attribute
console.log( $("#sub-category").find("[selected]").val() );
// Property
console.log( $("#sub-category").find(":selected").val() );
Your option with value "3" has the selected attribute, but not the property, it's the opposite for the option with value "4".
Whenever you add options inside a select, you must re-select the desired one:
$("#sub-category").find("[selected]").prop("selected", true);
Firefox selects the last element. This behavior is probably correct and explainable. In order to understand, please keep the following in mind:
jQuery append and remove methods process the elements one by one behind the scene.
The current state of an input element should be retrieved or set using the corresponding property, not attribute.
Expected Behavior (Firefox)
Removing all options from a select element as demonstrated in your example works as follows:
Car1 is removed (Car3 remains selected)
Car2 is removed (Car3 remains selected)
Car3 is removed. Since this is the selected element, removing it will cause the next element to become selected
Car4 is removed. Since this is the selected element, removing it will cause the next element to become selected
This will repeat until all options are moved from DOM to the memory. At this point the options will have the following properties:
// allsuboptions.each(function() { console.log(this.value, this.selected); });
value: 1, selected: false
value: 2, selected: false
value: 3, selected: true
value: 4, selected: true
...
value: 12, selected: true
There are 3 options with selected = true. When you add the options back to the select element, the browser sets the last selected element as the selected one.
Internet Explorer and Chrome
While the options are removed one by one, these two browsers do not update the selected element immediately (they possibly wait for JavaScript execution to finish). This causes the following discrepancies:
Internet Explorer does not immediately make the next option selected when currently selected option is removed. Therefore the removed options contain only one selected element.
Chrome seems to immediately make the next option selected when currently selected option is removed, but it does that only once. Therefore the removed options contain two selected elements.
In order to prove the point about immediate and deferred updates, here is a Fiddle containing:
The original code
A variation that forces the browser to update the select element each time an option is removed
http://jsfiddle.net/salman/j3oh6s3a/9/
Solution
As suggested in other answers, the correct workaround is to use a variable that references the currently selected option before removing the options. Something like:
var $selectedOption = $("#sub-category option").filter(function() {
return this.selected;
});
Once the options are re-inserted, you can check if that element was added back and select it again:
if ($selectedOption.parent().length) {
$selectedOption.prop("selected", true);
}
// or
$("#sub-category option").filter($selectedOption).prop("selected", true);
Your script is doing what you created it to do.
The reason car4 is selected, is because
<option selected='selected' value='3' data-parentid='1'>Car3</option>
is initially selected, then you are appending parentid='1' to the value which causes car4 to be the new selection.
What is the purpose of this script?
This is a little more complicated than the title makes it out to be, but here are the essential business rules:
There are three select menus on the
page, each filled with the same
options and values.
There will always be three select
menus.
There will always be the same number
of options/values in each select
menu.
Selecting a question in any of the
menus will remove that question as an option from
the other two menus.
Re-selecting a different question
from any of the menus will bring
back the question that was
previously removed from the other
two menus at the index it was at previously.
I've tried this a few different ways, and the thing that is killing me is number 5. I know that it wouldn't be inserted at the exact index because some questions may have already been removed, which would reorder the index. It basically needs an insertBefore or insertAfter that puts it in the same "slot".
Even if you don't post any code, some thoughts on how you might approach this would be extremely helpful. The select menus and jQuery look something like this, but I've had numerous tries at it in different variations:
jQuery:
$(function() {
$(".questions").change(function() {
var t = this;
var s = $(t).find(":selected");
// Remove, but no "insert previously selected" yet...
$(".questions").each(function(i) {
if (t != this) {
$(this).find("option[value=" + s.val() + "]").remove();
}
});
});
});
HTML:
<select name="select1" class="questions">
<option value="1">Please select an option...</option>
<option value="2">What is your favorite color?</option>
<option value="3">What is your pet's name?</option>
<option value="4">How old are you?</option>
</select>
<select name="select2" class="questions">
<option value="1">Please select an option...</option>
<option value="2">What is your favorite color?</option>
<option value="3">What is your pet's name?</option>
<option value="4">How old are you?</option>
</select>
<select name="select3" class="questions">
<option value="1">Please select an option...</option>
<option value="2">What is your favorite color?</option>
<option value="3">What is your pet's name?</option>
<option value="4">How old are you?</option>
</select>
Don't remove the elements, hide them. With removing, you are causing you a lot more problems than necessary. This works for me:
$(function() {
$('select.questions').change(function() {
var hidden = [];
// Get the values that should be hidden
$('select.questions').each(function() {
var val = $(this).find('option:selected').val();
if(val > 0) {
hidden.push($(this).find('option:selected').val());
}
});
// Show all options...
$('select.questions option').show().removeAttr('disabled');
// ...and hide those that should be invisible
for(var i in hidden) {
// Note the not(':selected'); we don't want to hide the option from where
// it's active. The hidden option should also be disabled to prevent it
// from submitting accidentally (just in case).
$('select.questions option[value='+hidden[i]+']')
.not(':selected')
.hide()
.attr('disabled', 'disabled');
}
});
});
I made a small change to your HTML also, I denoted an option that should always be visible with a value of 0. So the valid options go from 1 to 3.
Here's a working example, tell me if I misunderstood you:
http://www.ulmanen.fi/stuff/selecthide.php
I was working on a solution of this recently and modified this code to remove rather than disable/hide. For my solution it was required (I'm also using UI to style the select elements). Here's how I did it:
// Copy an existing select element
var cloned = $('select[name="select1"]').clone();
// Each time someone changes a select
$('select.questions').live('change',function() {
// Get the current values, then reset the selects to their original state
var hidden[];
$('select.questions').each(function() {
hidden.push($(this).val());
$(this).html(cloned.html());
});
// Look through the selects
for (var i in hidden) {
$('select.questions').each(function() {
// If this is not the current select
if ((parseInt(i)) != $(this).parent().index()) {
// Remove the ones that were selected elsewhere
$(this).find('option[value="'+hidden[i]+'"]').not('option[value="0"]').remove();
} else {
// Otherwise, just select the right one
$(this).find('option[value="'+hidden[i]+'"]').not('option[value="0"]').attr('selected','selected');
}
});
}
});