for循環綁定事件解決方法


  首先我們來看一段代碼

<body>
    <ul id="ul">
        <li>1111</li>
        <li>2222</li>
        <li>3333</li>
        <li>4444</li>
        <li>5555</li>
    </ul>
    
    <script type="text/javascript">
        var oUl = document.getElementById("ul");
        var aLi = oUl.getElementsByTagName("li");
        for(var i = 0; i < aLi.length; i++){  
                    aLi[i].onclick = function (){  
                        alert(i);  
                    };
                }  
    </script>
</body>

  運行之后發現無論點哪個標簽,彈出的都是最后一個標簽的index

  這是因為 for 循環的里面 var 定義的變量 i 自動提升為全局變量,等同於下面的代碼

<script type="text/javascript">
    var oUl = document.getElementById("ul");
    var aLi = oUl.getElementsByTagName("li");
    var i;
    for(i = 0; i < aLi.length; i++){  
        aLi[i].onclick = function (){  
            alert(i);  
        };
    }  
</script>  

  這時候 alert(i) 里面的i還沒有值,當用戶調用 onclick 的匿名函數時,需要對i求值

  解析程序首先會在事件處理程序內部查找,但 i 沒有定義。然后,又到方法外部去查找,此時有定義,但此時的i已經循環完畢,因此,無論點哪個標簽,彈出的都是最后一個標簽的index。

  有以下幾種方法解決:

  立即調用的函數表達式(IIFE);不懂的可以看看我之前寫的  點擊這里

<body>
    <ul id="ul">
        <li>1111</li>
        <li>2222</li>
        <li>3333</li>
        <li>4444</li>
        <li>5555</li>
    </ul>
    
    <script type="text/javascript">
        var oul = document.getElementById("ul");
        var ali = oul.getElementsByTagName("li");
        for(var i = 0; i < ali.length; i++){
            (function(i){   //這里的i類似形參 
                ali[i].onclick = function (){  
                    alert(i);   
                } 
            })(i);   //這里的i類似實參
         
    }  
    </script>
</body>   

  將變量 i 保存給在每個段落對象(li)上

<body>
    <ul id="ul">
        <li>1111</li>
        <li>2222</li>
        <li>3333</li>
        <li>4444</li>
        <li>5555</li>
    </ul>
    
    <script type="text/javascript">
        var oul = document.getElementById("ul");
        var ali = oul.getElementsByTagName("li");
        for(var i = 0; i < ali.length; i++){
            ali[i].i = i;
                ali[i].onclick = function (){  
                    alert(this.i);   
                } 
        }  
    </script>
</body> 

  將變量 i 保存在匿名函數自身

<body>
    <ul id="ul">
        <li>1111</li>
        <li>2222</li>
        <li>3333</li>
        <li>4444</li>
        <li>5555</li>
    </ul>
    
    <script type="text/javascript">
        var oul = document.getElementById("ul");
        var ali = oul.getElementsByTagName("li");
        for(var i = 0; i < ali.length; i++){
            (ali[i].onclick = function (){  
                alert(arguments.callee.i);   
            }).i = i
        }
    </script>
</body> 

  還有一種使用ES6新語法 let 關鍵字 由於是新語法 各瀏覽器支持不同

<body>
    <ul id="ul">
        <li>1111</li>
        <li>2222</li>
        <li>3333</li>
        <li>4444</li>
        <li>5555</li>
    </ul>
    
    <script type="text/javascript">
        var oul = document.getElementById("ul");
        var ali = oul.getElementsByTagName("li");
        for(let i = 0; i < ali.length; i++){
            ali[i].onclick = function (){  
                alert(i);   
            }
        }
    </script>
</body>

  關於let,我一直似懂非懂,后來在阮一峰老師的《ECMAScript 6 入門》這本書上找到了答案

  使用let,聲明的變量僅在塊級作用域內有效,當前的 i 只在本輪循環有效,所以每次都是一個新的變量

  可能你會問,如果每一輪循環的變量 i 都是重新聲明的,那它怎么知道上一輪循環的值,從而計算出本輪循環的值

  這是因為Javascript引擎內部會記住上一輪循環的值,初始化本輪的變量 i 時,就在上一輪循環的基礎上進行計算。

  題外話

  for循環還有一個特別之處,就是循環語句部分是一個父作用域,而循環體內部則是一個單獨的子作用域

for (let i = 0; i < 3; i++) {
    let i = "abc" ;
    console.log(i);
}
 // abc
 // abc
 // abc

   上面的代碼輸出了3次 abc,這表明函數內部的變量 i 和外部的變量 i 是分離的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM