【JS】关于多元素绑定事件的问题和三个解决方法

<!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>
posted @ 2022-03-31 10:58  嘻嘻不是菜鸟了  阅读(646)  评论(0)    收藏  举报