4
var submenus = document.getElementsByClassName("submenu");
for (var i = 0; i < submenus.length; i++) {
    submenus[i].onclick = function() {
        toggle(submenus[i].nextSibling);
        return false;
    }
}

function toggle(el) {
    if (el.style.display == 'block') {
        el.style.display = 'none';
    } else {
        el.style.display = 'block';
    }
}

Causes error: TypeError: submenus[i] is undefined

I assume submenus[i] is not in the scope of the function. How do I get the element clicked so I can toggle it's next sibling?

Computer User
  • 546
  • 9
  • 15

4 Answers4

6
var submenus = document.getElementsByClassName("submenu");
for (var i = 0; i < submenus.length; i++) {
    submenus[i].onclick = function() {
        toggle( this.nextSibling);
        return false;
    }
}

Inside such an event handler the this keyword is bound to the element, that triggered the event. So in your example you can use this to refer to you submenu item and thereby to its sibling.

For more information see the link @FelixKling provided: http://quirksmode.org/js/this.html

Sirko
  • 72,589
  • 19
  • 149
  • 183
0

You can do this :

var submenus = document.getElementsByClassName("submenu");
for (var i = 0; i < submenus.length; i++) {
    (function(e){
      submenus[i].onclick = function() {
        toggle(e);
        return false;
      }
    })(submenus[i].nextSibling);
}

The immediately called function creates a scope in which submenus[i].nextSibling is kept.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
0

It has to look like this:

  var submenus = document.getElementsByClassName("submenu");
    for (var i = 0; i < submenus.length; i++) {
        (function(i){
            submenus[i].onclick = function() {
                toggle(submenus[i].nextSibling);
                return false;
            };
        })(i);
    }

    function toggle(el) {
        if (el.style.display == 'block') {
            el.style.display = 'none';
        } else {
            el.style.display = 'block';
        }
    }
sjkm
  • 3,887
  • 2
  • 25
  • 43
0
var submenus = document.getElementsByClassName("submenu");
for (var i = 0; i < submenus.length; i++) {
    submenus[i].onclick = (function(j) {
        return function () {
            // alert(j);
            toggle(submenus[j].nextSibling);
            return false;
        };
    })(i);
}

Because onclick function will fire after your for loop, so whenever the user click on desired element, the i will be the length of your elements:

<div class="submenu">Hello</div>
<div class="submenu">Hello</div>
<div class="submenu">Hello</div>
<div class="submenu">Hello</div>
<div class="submenu">Hello</div>
<div class="submenu">Hello</div>

And with your code:

var submenus = document.getElementsByClassName("submenu");
for (var i = 0; i < submenus.length; i++) {
    submenus[i].onclick = function() {
        alert(i);
    }
}

You'll get 6 for i on every element (check the jsFiddle).

You must keep i inside the onclick function, closure will help you:

for (var i = 0; i < submenus.length; i++) {
    submenus[i].onclick = (function(j) {
        return function () {
            alert(j);
        };
    })(i);
}

As you see, I've created a function and execute it immediately (with a parameter I've called it j, and the value of it, for each element, is the i). The return of it, is another function. Check this jsFiddle.