Skip to content

Fix for #13514.Set selectedIdx to 0 if a 'wrong' value is set as dropdown-list value #1191

Closed
wants to merge 12 commits into from

4 participants

@ruado1987

This commit fixes the issue described in the bug ticket when a dropdown-list is set to a value which doesnt match with any of its options' value.

@staabm staabm and 1 other commented on an outdated diff Mar 3, 2013
src/attributes.js
@@ -280,6 +280,12 @@ jQuery.extend({
if ( !values.length ) {
elem.selectedIndex = -1;
+ } else {
+ // IE 9 doesn't select a dropdown-list's first option
@staabm
staabm added a note Mar 3, 2013

needs to be Support: IE9. is IE7/8/10 also affected?

@ruado1987
ruado1987 added a note Mar 4, 2013

Well, I haven't had a chance to check the issue on these ie versions. Anyway, I will try to check it out today.

@ruado1987
ruado1987 added a note Mar 4, 2013

BTW, you also meant the comment needs to be changed to Suport: IE9 ..., right?

@staabm
staabm added a note Mar 4, 2013

Yes

@ruado1987
ruado1987 added a note Mar 4, 2013

@staabm: this issue doesnt affect IE7/8, I havent had a chance to test it on IE10 though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ruado1987

@dmethvin @gibson042 : Please review this fix and give away your advice. Thanks.

@gibson042 gibson042 and 1 other commented on an outdated diff Mar 6, 2013
src/attributes.js
@@ -280,6 +280,12 @@ jQuery.extend({
if ( !values.length ) {
elem.selectedIndex = -1;
+ } else {
+ // IE 9 doesn't select a dropdown-list's first option
+ // when value doesnt match with one of its options' value
+ if ( elem.selectedIndex < 0 ) {
+ elem.selectedIndex = 0;
@gibson042
jQuery Foundation member
gibson042 added a note Mar 6, 2013

This will not work correctly when elem is a select-multiple. Also, I'm concerned about this edge case fix requiring too much additional code, but that could be mitigated somewhat by templating off the get method. I'd try starting with

var optionSet,
    values = jQuery.makeArray( value ),
    options = elem.options,
    index = elem.selectedIndex,
    one = elem.type === "select-one" || index < 0;

and proceed from there, making sure to add thorough unit tests for both select-one and select-multiple.

@ruado1987
ruado1987 added a note Mar 7, 2013

This issue is not occurring to select-multiple. Do you mean that the code has to be changed so that calling val() with a 'wrong' option value(s) on select-multiple will also select the first option? Currently no option will be selected when passing a 'wrong' value to val() in case of select-multiple.

@gibson042
jQuery Foundation member
gibson042 added a note Mar 7, 2013

Observe the change in behavior from introducing this code: http://jsfiddle.net/wuM8f/

@ruado1987
ruado1987 added a note Mar 8, 2013

My latest changes: http://jsfiddle.net/aVjyH/1/ to preserve the current behavior when elem is a select-multiple. The new code changes elem.selectedIndex only if elem.type is "select-multiple" and elem.selectedIndex is less than 0. The approach used in get doesn't work well when applying to set because one = elem.type === "select-one" || index < 0 can be true even when elem is a select-multiple and none of its options is selected.

@gibson042
jQuery Foundation member
gibson042 added a note Mar 8, 2013

That's right of course; we slip it in there because a select-multiple with nothing selected can follow the single-get null case. Regardless, http://jsfiddle.net/aVjyH/1/ is too much code, and will compress poorly. Seriously, follow along these lines:

var optionSet,
    values = jQuery.makeArray( value ),
    options = elem.options,
    index = elem.selectedIndex,
    // Keep as a variable only if overall gzip size is smaller
    // than using elem.type !== "select-one" directly in an if condition
    one = elem.type === "select-one";

and try using the loop body instead of selectedIndex to track if post-iteration fixup is necessary.

@ruado1987
ruado1987 added a note Mar 9, 2013

Following your advice, I've come up with this change. However, I don't use the index variable because the variable is not useful in determining if post-iteration fixup is necessary. I also use elem.type === "select-one" directly in the if condition since it results in a bit smaller file than introducing the one variable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@gibson042 gibson042 and 1 other commented on an outdated diff Mar 11, 2013
src/attributes.js
});
if ( !values.length ) {
elem.selectedIndex = -1;
+ } else {
+ // fix IE9 bug when non-matching value is set
+ if ( elem.type === "select-one" && !optionSet ) {
@gibson042
jQuery Foundation member
gibson042 added a note Mar 11, 2013

What file are you using to verify size changes? Our standard metric is jquery.min.js.gz, and you'll be able to see it directly from grunt if you rebase onto 91d5764. I'm certain, for instance, that you'll achieve better compression by declaring options third rather than first and skipping the initial assignment to optionSet. There are even greater savings to be had by abandoning jQuery.each in favor of native iteration.

Also, looking at this with fresh eyes, I'm pretty sure you can combine the two tests here and just set elem.selectedIndex = -1 whenever optionSet is falsy.

@ruado1987
ruado1987 added a note Mar 12, 2013

I had verified size changes using the generated minified file before you pushed the latest changes regarding to the grunt-compare-size. Just pulled the latest changes and started using this plugin for my verification. You are also right when saying native iteration will result in better performance than jQuery.each. Actually the former was what I wanted to use, but I thought it would be better to follow the old code to use jQuery's own utilities. Anyway, I will change the code to use native iteration. As for the last comment, I don't really get what you mean. Are you saying that it is better to remove elem.type === "select-one" and keep only !optionSet in the condition tests?

@gibson042
jQuery Foundation member
gibson042 added a note Mar 12, 2013

Yes; assuming the following works (and if it doesn't then $selectMultiple.val([]) etc. are likely broken), I would consider it better than the if/else:

if ( !optionSet ) {
    elem.selectedIndex = -1;
}

The remainder is then mostly optimizing for compression; e.g.:

var optionSet, option,
    options = elem.options,
    i = options.length,
    values = jQuery.makeArray( value );

while ( i-- ) {
    option = options[ i ];
    if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {
        optionSet = true;
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ruado1987

@gibson042: Moving the assignment of i to the for loop results in a smaller gz file (29476 vs. 29480).

@ruado1987

@gibson042: This issue wont be fixed if the code is just elem.selectedIndex = -1. It is still required to set elem.selectedIndex to 0 if the element is a select one.

@gibson042
jQuery Foundation member

Really? I observe consistent behavior on all browsers: http://jsbin.com/ikovev/1/

At this point, it's a question of what consistency is most appropriate. Nonexistent values are not documented as valid input, so this is an opportunity to define behavior. Implementing "value not found" as selectedIndex = -1 (and subsequent null .val()) seems to me like an improvement over selectedIndex = 0 (and subsequent non-null .val()), but this is now sufficiently vague that I'll put out the @dmethvin​-shaped signal light and we'll try to establish a consensus.

@gibson042
jQuery Foundation member

HTML5 language:

On setting, the selectedIndex attribute must set the selectedness of all the option elements in the list of options to false, and then the option element in the list of options whose index is the given new value, if any, must have its selectedness set to true.
This can result in no element having a selectedness set to true even in the case of the select element having no multiple attribute and a display size of 1.

@ruado1987

@gibson042 By "won't be fixed", I mean IE9's behavior still remains the same as described in ticket #13514. According to the HTML5 spec on this regard, I think IE9 behavior is the most appropriate though. Anyway, let's wait for @dmethvin decision.

@ruado1987

@gibson042 Is there any update on this issue? It has been idle for a week.

@gibson042
jQuery Foundation member

@dmethvin ping.

I strongly prefer the first, but as stated above, this is undefined behavior for jQuery right now:

  1. "Value not found" sets selectedIndex = -1. Subsequent .val() returns null.
  2. "Value not found" sets selectedIndex = 0 (more technically, to the lowest selectedIndex identifying a non-disabled <option>). Subsequent .val() returns the corresponding value.

(HTML5 link)

@dmethvin
jQuery Foundation member

I remember looking at this and my head started hurting. Same effect this week. Us defining behavior is always dangerous.

If a select has no initial option selected, that's undefined behavior according to HTML4 but nearly all browsers handle that case by choosing the first option I think. I couldn't see the similar language in the HTML5 case.

So option 1 provides functionality that isn't possible through the normal declarative markup. How cross-browser is it? In HTML5 if no option has its selectness, does that imply the spec is preventing the browser from displaying one as a default? If so this seems to contradict the HTML parsing scenario.

@gibson042
jQuery Foundation member

You're right that HTML4 leaves it undefined, but HTML5 specifies option 1, at least programatically:

Note: [setting selectedIndex] can result in no element having a selectedness set to true even in the case of the select element having no multiple attribute and a display size of 1.

It's clear that setting selectedIndex to -1 should result in all options having false selectedness, and that getting selectedIndex in such a state should return -1.

The display is unspecified, but all browsers that I tested blank out the select— including Android, iOS, and IE6+. We've previously taken a firm stance that rendering issues are not our problem, and I don't see any reason to break that now.

@dmethvin
jQuery Foundation member

Okay, I'm good with option 1 then. Let's do it.

@ruado1987

@gibson042 @dmethvin : Done. Please review the latest changes.

@gibson042 gibson042 added a commit that closed this pull request Mar 22, 2013
@ruado1987 ruado1987 Fix #13514: Set selectedIndex to -1 when non-matching value is set on…
… a select. Close gh-1191.

(cherry picked from commit 48d71d0)
c9ca9bf
@gibson042 gibson042 closed this in c9ca9bf Mar 22, 2013
@gibson042
jQuery Foundation member

Thanks very much!

@mescoda mescoda pushed a commit to mescoda/jquery that referenced this pull request Nov 4, 2014
@ruado1987 ruado1987 Fix #13514: Set selectedIndex to -1 when non-matching value is set on…
… a select. Close gh-1191.
014aa50
@bperel bperel pushed a commit that referenced this pull request Jul 21, 2015
@ruado1987 ruado1987 Fix #13514: Set selectedIndex to -1 when non-matching value is set on…
… a select. Close gh-1191.

(cherry picked from commit 48d71d0)
29514f1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.