1. 程式人生 > 其它 >關於多元素繫結事件的問題和三個解決方法

關於多元素繫結事件的問題和三個解決方法

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			html,body,h1,h2,h3,h4,h5,h6,p,ul,ol,li,dl,dt,dd,form,fieldset,legend,table,tr,td,input{padding: 0;margin: 0;}
			ol,ul,li{
			    list-style: none;
			}
			a{
			    color: inherit;
			    text-decoration: none;
			}
			.wrap{
			    width: 600px;
			    margin: 50px auto;
			}
			.wrap .nav{
			    display: flex;
			    line-height: 40px;
			    text-align: center;
			}
			.wrap .nav li{
			    flex: 1;
			    border: 1px solid #333;
			}
			.wrap .nav li + li{
			    border-left: 0;
			}
			
			.wrap .nav .active{
			    background-color: #00AEEC;
			    color: #fff;
			}
		</style>
	</head>
	<body>
		<div class="wrap">
		    <div class="nav">
		        <li class="active"><a href="javascript:;">link_01</a></li>
		<!-- javascript:是一個偽協議,可以讓我們通過超連結去呼叫javascript函式,但是這個函式為空,所以我們呼叫的是一個空函式,相當於"javascript:void(0)",並不會發生實質性的改變,同時也可以實現a標籤的點選執行,一般在這種情況下,會給繫結一個事件回撥,來執行業務-->
		        <li><a href="javascript:;">link_02</a></li>
		        <li><a href="javascript:;">link_03</a></li>
		        <li><a href="javascript:;">link_04</a></li>
		        <li><a href="javascript:;">link_05</a></li>
		        <li><a href="javascript:;">link_06</a></li>
		    </div>
		</div>
	</body>
	<script type="text/javascript">
		var liList = document.querySelectorAll(".nav li");
		console.log(liList);//NodeList(6) [li.active, li, li, li, li, li]
		
		
		//如果要給每一個li都以分別一個個的繫結事件太麻煩,所以對於重複事件可以選擇用for迴圈來實現
		// 多元素繫結事件的問題:
		// 1. 頁面載入時 => 迴圈繫結事件(載入時不觸發,點選時才觸發) => 等待迴圈結束,直至全域性程式碼執行完畢 (li:最後一個  i:6)
		// 2. 點選li => 執行函式中的上下文  => 自己沒有li和i 向外跳一層,找全域性執行棧 (li:最後一個  i:6)
		
		
		for(var i = 0;i<liList.length;i++){ //0-5
			var li = liList[i];
			li.onclick = function(){
				console.log(li,i);
			}
		}
		console.log("迴圈結束:",li,i);
		//問題:無論點選哪一個li觸發後得到的li都是最後一個且i=6
		//原因:在頁面載入完、li點選觸發之前for迴圈就已經是執行完畢並且li = liList[5](也就是最後一個)的情況,當點選觸發後直接輸出這個結果
		
		
		// 解決方法一:尋找替換方案
		// 思路:li和i已經無法使用,尋找替換方法
		// 方法:li => 替換為this(指向觸發事件的元素)
		for(var i = 0;i<liList.length;i++){
			var li = liList[i];
	
			li.setAttribute("date-index",i);//方法一:先傳入屬性
			// li.index = i;//方法二:先找到每個li的下標並賦值(在for中遍歷元素時給每個元素繫結自定義屬性date-index和對應的標識下標)
			li.onclick = function(){
				var index = this.getAttribute("date-index");//方法一對應:再獲取屬性的值
				// var index = this.index;//方法二對應:通過index找到this指向所點選的那個li
				console.log(this,index);  
			}
		}
		
		
		// 解決方法二:利用作用域
		// 思路:不成功的原因在於li和i是全域性的 => 找到一個辦法讓li和i找迴圈當前的資料
		// 方法:在事件外巢狀函式作用域 => 變相的將li和i轉化為區域性變數
		for(var i = 0;i<liList.length;i++){
			//for是塊級作用域,所以巢狀一個函式作用域
			function fn(i){ //i 形參 => 區域性變數
				var li = liList[i];//li也是區域性的,於全域性無關
				li.onclick = function(){
					console.log(li,i);
				}
			}
			//這裡的fn函式在頁面載入時就已經是全部遍歷執行並且儲存到對應的記憶體空間的,可以理解為已經對應的放到了每個li中,當點選時會觸發相應的li(即li在每一次迴圈的時候已經繫結好了)
			fn(i);//迴圈呼叫函式 實參i=>0 1 2 3 4 5
			console.log(liList[i],i);
		}
		
		
		// 解決方法三:利用let變數宣告的區域性特性
		// 思路:不成功的原因在於li和i是全域性的 => 找到一個辦法讓li和i找迴圈當前的資料
		// 方法:在事件外巢狀塊級作用域 => 塊級作用域中let宣告的變數是區域性的 => 變相的將li和i轉化為區域性變數
		// 原理:let在程式碼塊中都有自己的作用域,所以在for迴圈中的表示式中使用let它的每一個值都會單獨存在一個獨立的作用域中不會被覆蓋掉,可以理解為是聲明瞭五個let塊級作用域
		for(let i=0;i<liList.length;i++){
			let li = liList[i];//依次將獲取到的5個li賦值給在5個塊級作用域中不同的li變數
			li.onclick = function(){
				console.log(li,i);
			}
			console.log(liList[i],i);
		}
	</script>
</html>