2013-10-06 31 views
1

为什么i在此代码中的callback function之外?当应用事件侦听器时,超出范围的变量

// All menu items collection 
var menuItems = document.getElementsByClassName('menu-item'); 

// Loop trough all menu items and attach 
// event listeners. 
for (var i = 0; i < menuItems.length; i++) { 
     // Check if element truely exsists 
     if (menuItems[i]) { 
      menuItems[i].addEventListener('click', function(e){ 
       //////////////////////////////// 
       // NOTE: i, is out of scope! // 
       ////////////////////////////// 
       var icon = menuItems[i].children[1], 
        submenu = menuItems[i].children[2]; 

       // Change icon color 
       if (icon.style.background !== "blue") { 
        icon.style.background = "blue"; 
       } else { 
        icon.style.background = "red";   
       } 

       // Show/hide submenu 
       if (submenu.style.display !== "block") { 
        submenu.style.display = "block"; 
       } else { 
        submenu.style.display = "none";   
       } 
      }); 
     } 
} 
+0

仅当设置了事件侦听器时触发事件才定义它? –

+0

这是一个答案的问题吗? – Johnny

回答

3

变量i未超出范围。只是事件处理程序将在循环完成后的某段时间被调用,因此变量i的值指向menuItems数组之外。

包裹在一个函数的代码,创建,你必须为每次迭代变量的副本的范围:

for (var i = 0; i < menuItems.length; i++) { 
    // Check if element truely exsists 
    if (menuItems[i]) { 

    (function(i){ 

     menuItems[i].addEventListener('click', function(e){ 
     var icon = menuItems[i].children[1], 
      submenu = menuItems[i].children[2]; 

     // Change icon color 
     if (icon.style.background !== "blue") { 
      icon.style.background = "blue"; 
     } else { 
      icon.style.background = "red";   
     } 

     // Show/hide submenu 
     if (submenu.style.display !== "block") { 
      submenu.style.display = "block"; 
     } else { 
      submenu.style.display = "none";   
     } 
     }); 

    })(i); 

    } 
} 

由于事件处理程序使用i变量,它会在一个封闭捕获该功能的对象。每个事件处理程序都有一个单独的闭包对象,但没有函数包装器为每个迭代创建一个单独的变量i,所有闭包都会捕获相同的变量。

函数的闭包含函数在外部作用域使用的所有局部变量。除非menuItems是一个全局变量,它也将在关闭中。

+0

Javascript并不是我的强项,但是您可以从事件参数中获得菜单项作为事件的目标吗?这不会消除保留我的需要吗? – Jesse

+0

很好的解释,但我有一个要求。你可以添加你的答案表示方式,如何Javascript将这两个'EventListeners'不同地作为'object'存储。我很想看看Javascript如何存储'i'变量,但我希望将它看作是一个'object',它对我有很大帮助! – Johnny

+0

@Jesse:是的,因为变量'i'只用于获取被点击的元素,在这种情况下,它也可以用来获取事件的目标。 – Guffa