Jama A.
Jama A.

Reputation: 16079

Dropdown's change handler is working a bit weird

Here is what I have done:

<html><head><script type="text/javascript"> 
function loaded(){
    var selectElems=document.getElementsByName("select");
    for(i=0;i<selectElems.length;i++){
      var elem=selectElems[i];
      elem.onchange=function(){
          alert(elem.selectedIndex);
       }
     }
} </script></head>
<body onload="loaded()">
<select name="select">
      <option>0</option>
      <option>1</option>
      <option>2</option>
      <option>3</option>
      <option>4</option> </select>
 <select name="select">
      <option>0</option>
      <option>1</option>
      <option>2</option>
      <option>3</option>
      <option>4</option></select>
 <select name="select">
      <option>0</option>
      <option>1</option>
      <option>2</option>
      <option>3</option>
      <option>4</option></select>
</body></html>

I need to get selected index of element with JavaScript. But I had a problem with that. Here is my code: http://jsfiddle.net/aRMpt/139/

Upvotes: 0

Views: 109

Answers (1)

Ruan Mendes
Ruan Mendes

Reputation: 92274

The problem is that your elem always points to the last select element. The simplest solution is to use this in the handler instead of the elem variable http://jsfiddle.net/mendesjuan/aRMpt/140/

function loaded(){
    var selectElems=document.getElementsByName("select");
    for(i=0;i<selectElems.length;i++){
      var elem=selectElems[i];
      elem.onchange=function(){
          // This points to the select element that was changed
          alert(this.selectedIndex);
       }
     }
}

This doesn't really tell you what your problem is though. It's a workaround (though a good one). The reason your code doesn't work is because your closure function onchange uses the same elem from the closure. By the time your onchange is called, elem points to the last select element. A way to avoid this is to introduce another closure that freezes the elem for your onchange handler http://jsfiddle.net/mendesjuan/aRMpt/142/

function loaded(){
    var selectElems=document.getElementsByName("select");
    for(i=0;i<selectElems.length;i++){
      var elem=selectElems[i];
      // Freeze the elem variable by creating a function that passes
      // the elem and creates a separate closure for each handler
      elem.onchange= (function(el) {
          // This is the function that will actually be the handler
          return function(){
             // This points to the select element that was changed
             alert(el.selectedIndex);
          }
     }) (elem) 
}

Here's another example that may help you understand how the above example works.

// This is a function that returns another function
// Its only reason is so that we don't use the shared closure 
// variables from the outer scope, it freezes the el
// variable at the moment its called and so that it's available
// when the handler is called
function createOnChangeHandler(el) {
  return function() {
     alert(el.selectedIndex);
  }
}

function loaded(){
    var selectElems=document.getElementsByName("select");
    for(i=0;i<selectElems.length;i++){
      var elem=selectElems[i];
      // If we just use elem here, its value will be what it was assigned last
      // in the loop. That is because the handler won't be called until
      // this loop has finished. However, by calling another function, 
      // a new scope is created with the value that we're passing into it
      elem.onchange = createOnChangeHandler(elem);
}

This freezing of variables could also be accomplished by using Function.bind, but this only works in newer browsers; however, the above link shows you how to make it work for browsers that don't support it. http://jsfiddle.net/mendesjuan/aRMpt/143/

function loaded(){
    var selectElems=document.getElementsByName("select");
    for(i=0;i<selectElems.length;i++){
      var elem = selectElems[i];
      elem.onchange = (function (el) {
        alert("Select index: " + el.selectedIndex)
      }).bind(null, el);
}

Upvotes: 3

Related Questions