在前端羣裏看見過很多人問過這個問題,今晚又有人問了這個問題,所以寫篇文章整理一下。首先看一下代碼,點擊li之後彈出當前li所對應的索引值。於是很多人刷刷刷寫出了下面的代碼。
1 2 3 4 5 6 | var aLi = document.getElementsByTagName( 'li' ); for ( var i = 0; i < aLi.length; i++){ aLi[i].onclick = function (){ alert(i); } } |
但是結果不盡人意,爲了簡單,我們約定一下頁面中有2個li。點擊li之後彈出的都是2。
我們首先來分析一下爲什麼結果是1.我們可以簡單的將循環分成兩部。
1 2 3 | i = 0時,aLi[0].onclick = function (){alert(i)} i = 1時,aLi[1].onclick = function (){alert(i)} i = 2時,不滿足條件跳出循環. |
在執行click的函數的時候,會有一個作用域鏈,這個作用域鏈是一個對象列表,這組對象定義了代碼作用域中的變量。( 關於變量對象的內容想更詳細瞭解的可以查看變量對象。)當我們alert(i)的時候,會去從內到外的去尋找變量i。這個時候這個函數的作用域鏈上有兩個對象,這時循環已經結束了,i此時的值爲2.所以點擊任何一個li,彈出的都是2,而不是我們想要的索引值。重點在於彈出的是變量i,變量i,變量i。重要的事情說三遍。
那麼問題來了,我們要如何解決這個問題呢。我們需要做的就是在每次給aLi[i]綁定事件的時候,將這個時候i的值保存在內部的作用域中。解決方案如下。
1 2 3 4 5 6 7 8 | var aLi = document.getElementsByTagName( 'li' ); for ( var i = 0; i < aLi.length; i++) { ( function (i){ aLi[i].onclick = function () { alert(i); }; })(i) } |
這裏涉及到一個塊級作用域的概念。在es6出來前,函數是作爲創建塊級作用域的主要手段。這裏我們通過在aLi[i].onclick外面套上一層函數,將i作爲參數,我們重新分析一下結果。
1 2 3 4 5 6 7 8 9 10 11 12 13 | i = 0時, ( function (i){ aLi[0].onclick = function (){ alert(i); } })(0) i = 1時, ( function (i){ aLi[1].onclick = function (){ alert(i); } })(1) i = 2時,不滿足條件跳出循環. |
由於多了一層自執行函數的包裹,當我們點擊li時,會有三層的作用域,從內帶外分別是:click函數內部的變量對象,自執行函數的變量對象和最外層的window對象。查找到第二層的時候,找到了i,自執行函數的i等於傳入的參數值,相對應的存下了當時i的值,所以就彈出了相應的索引值。
下面再給大家分享一個js常見的問題,實現點擊li能夠彈出當前li索引與innerHTML的函數
點擊其中一項需要alert出如下結果:
按照我們平常的想法,代碼應該是這樣寫的:
1 2 3 4 5 6 7 8 9 10 11 | var myul = document.getElementsByTagName( "ul" )[0]; var list = myul.getElementsByTagName( "li" ); function foo(){ for ( var i = 0, len = list.length; i < len; i++){ list[i].onclick = function (){ alert(i + "----" + this .innerHTML); } } }
|
但是不巧的是產生的結果是這樣的:
索引index爲什麼總是4呢,這是js中沒有塊級作用域導致的。這裏有三種解決思路
1. 使用閉包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <script type= "text/javascript" > var myul = document.getElementsByTagName( "ul" )[0]; var list = myul.getElementsByTagName( "li" ); function foo(){ for ( var i = 0, len = list.length; i < len; i++){ var that = list[i]; list[i].onclick = ( function (k){ var info = that.innerHTML; return function (){ alert(k + "----" + info); }; })(i); } } foo(); </script> |
2.使用ES6中的新特性let來聲明變量
用let來聲明的變量將具有塊級作用域,很明顯可以達到要求,不過需要注意的是得加個'use strict'(使用嚴格模式)纔會生效
1 2 3 4 5 6 7 8 9 10 11 12 | <script type= "text/javascript" > var myul = document.getElementsByTagName( "ul" )[0]; var list = myul.getElementsByTagName( "li" ); function foo(){ 'use strict' for (let i = 0, len = list.length; i < len; i++){ list[i].onclick = function (){ alert(i + "----" + this .innerHTML); } } } foo(); </script> |
3.引入jquery,使用其中的on或delegate進行事件綁定(它們都有事件代理的特性)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <script type= "text/javascript" src= "jquery-1.8.2.min.js" ></script> <script type= "text/javascript" > $( "ul" ).delegate( "li" , "click" , function (){ var index = $( this ).index(); var info = $( this ).html(); alert(index + "----" + info); }); </script> <script type= "text/javascript" > $( "ul" ).on( "click" , "li" , function (){ var index = $( this ).index(); var info = $( this ).html(); alert(index + "----" + info); });
|
參考:http://www.jb51.net/article/77490.htm