How to Change Select Values Correctly with JQuery

For the most part, while using Jquery you can change most dom attributes using $.attr() function. Which is great for altering id, classes, names and many other values. But what happens when you try to change input values that need to be submitted.

The Path to Discovery

On a recent project, we had to create a custom dropdown menu that was populated from a traditional html select form element. Using Jquery, we grabbed all of the option values and labels to use as a data driver for our new custom dropdown. When the user clicks on one of our values, we then in-turn simulate a user select using $.attr() and submit the form.

The expected result is that the form submits and you see the get get values in the url, which does happen, but something odd occurs when you re-submit the form a second time. The value is submitted twice, one with the default value and the other with the selected.

Creating a Simple Custom Dropdown Menu

To start, lets create a html form drop down menu and pre-fill it with data. We’re going to be using the option values and text to create our custom clickable tag. Feel free to setup your html however you like, I’ve chosen to use twitter bootstrap.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div class="container">
<div class="row">
<div class="span8">
<h2>Colors</h2>
<form action="" id="color-form" method="get">
<select name="colors" id="color-list">
<option value="">Choose A Color</option>
<option value="#ff0000">Red</option>
<option value="#0000ff">Blue</option>
<option value="#00800">Green</option>
</select>
</form>
</div>
<div class="span4">
<h3>Our Form value</h3>
<div id="color-converter">
</div>
</div>
</div>
</div>

To mimic our custom drop down menu, we are going to use jquery’s $.each() function to iterate through all the select options and create p tags. In our loop we will also save the option value to each item so we can create a 1-to-1 relationship.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$(function(){
$("#color-list option ").each(function(i,k){
var cItem = $(this);
var label = cItem.text();
var value = cItem.attr("value");
var item = $('<p class="color-click">' + label +'</p>');
item.attr("data-value", value);
$("#color-converter").append(item);
})
$(".color-click").on("click",function(){
var value = $(this).data(“value”);
});
});

We start off on line 2 by selecting all of the option tags and run JQuery’s $.each function to cycle through the items. To make it more manageable, in lines 3-5, we extract the label and the value associated and assign them to variables, which in line 6 we will use to create <p> tags. As you can see, on line 8 we use the $.attr(); function to add the data attribute value to our p tag. And finally we use the $.append() function to add our new p tag to the div with the id=”color-converter”.

Now the real fun begins on line 13. After our loop finishes and adds the new p tags to the dom, we add a click event listener to every p tag with the class=”color-click”. When the user clicks on any of the items, we will use the the $.data() function to grab the value stored in our data-value attribute that was created earlier.

Changing The select base on User Click

To understand our next step you have to realize what happens when you actually select an item from the drop down menu. Using the Chrome web browser inspector window, we can inspect the dom and see what the default value is and how do we set it. In the image below there is one option tag with the attribute selected=”selected”. If we put that on any other element it will make that value the default. So technically, by removing the selected attribute from one option tag and putting it on the other it should update our select box.

how_select_works

Lets give it a try, our goal is to deselect the current value in our form select box, and replace it with the one the user clicked on in our custom list. To do that, we will run through another $.each() loop and if the value is selected we will remove the attribute selected=”selected”. Now in our option loop we also want to select the item that the user clicked on. So in the same loop we will also check to see if the value is equal to our user click data-value. If so give it the attribute selected=”selected”.

1
2
3
4
5
6
7
8
9
10
11
$(".color-click").on("click",function(){
var userValue = $(this).data("value");
$("#color-list option ").each(function(){
var t = $(this);
var cValue = t.attr("value");
if(t.attr("selected") =="selected"){
console.log("current item is: " + t.text());
}
})
});

In the example above we run through our new each loop and grab the current option element and check to see if it has a attribute selected with the value selected. Right now we’re just using console.log() to get the label of the current value to debug our code and make sure it works. In the next example we are going to take it a step further by removing the selected attribute and then selecting the users value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$(".color-click").on("click",function(){
var userValue = $(this).data("value");
$("#color-list option ").each(function(){
var t = $(this);
var cValue = t.attr("value");
if(t.attr("selected") =="selected"){
t.removeAttr("selected");
}
//new code
if(t.attr("value") == userValue ){
t.attr("selected","selected");
}
});
});

If we open our inspector window and look at the dom you will see that using the code above we are deselecting the current value and selecting the new value. The last thing we need to do is submit the form and that we can do by adding one line of code. $(“#color-form”).submit(); Which references our form id and submits it.

Now here is where we run into an issue. As of right now Using the Chrome web browser version 26.0.1410.43, there are a few instances where you get the double submission. One is if the user double clicks after a form has previously been submitted. The other is random and can be due to many factors including server side handling.

Digging For a Solution

So what is actually causing the problem. After much digging I found a very interesting fact. Throughout this whole article, the word attribute is mentioned a number of times. Logically, if you visit the w3.org you’ll see them mention the term attribute frequeently. But that is kind of misleading. In the case of the inputs check and selected. Those attributes are attributes, but they are attributes that represent the default state of the the property. What actually represents the current selected state is the property selected and not the attribute.

HUH… let me explain, in our select example the dom element attribute “selected” only is a reference to the default selected value when the page is loaded. What really controls the current selection option is the property attached to the option object. Prior to JQuery 1.6 the $.att() function would handle both for you, so there was no need to worry.

So in order to avoid those random occurrences of this bug, you have to change how we modify our selected option. And to do so we simply have to change our $.attr() to $.prop(). Checkout the full code bellow.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$(function(){
$("#color-list option ").each(function(i,k){
var cItem = $(this);
var label = cItem.text();
var value = cItem.attr("value");
var item = $('<p class="color-click">' + label +'</p>');
item.attr("data-value", value);
$("#color-converter").append(item);
})
$(".color-click").on("click",function(){
var userValue = $(this).data("value");
$("#color-list option ").each(function(){
var t = $(this);
var cValue = t.attr("value");
if(t.attr("selected") =="selected"){
t.removeAttr("selected");
}
if(t.attr("value") == userValue ){
t.prop("selected","selected");
}
});
$("#color-form").submit();
});
});

Conclusion

This bug has always been around in jquery, but having it resolved automatically allowed it to fly under the radar. JQuery is a great javascript library and has done wonders for the revival of Javascript, but like any tool if used incorrectly it causes more harm than good.