JS中的事件基础知识

本文首发于个人博客:www.wyb.plus

JS作为一门事件驱动型的语言,了解与事件有关的知识是十分必要的。

JS中与事件有关的概念非常多,本文尽量整理完善。

  • 作者:王雨波
  • qq:760478684
  • 博客:www.wyb.plus

1. 事件的三要素

事件就是由用户或浏览器本身执行的操作。

事件的三要素包括:事件源事件触发的动作事件处理程序

1.1 事件源

触发事件的元素叫做事件源。比如有一个button按钮绑定了一个点击事件,点击即会弹出一个弹窗,那么这个button按钮就是事件源

1.2 事件的触发动作

事件的触发动作也叫事件的类型,上面的例子中,点击按钮的点击动作就是事件的触发动作。DOM3事件规定了以下几类事件:UI事件、焦点事件,鼠标事件、滚轮事件、文本事件、键盘事件、合成事件、变动事件

1.3 事件处理程序

响应某个事件的函数叫做事件处理程序。事件处理程序的名字以“on”开头,因此click事件的事件处理程序就是onclick。事件处理程序在下一节详细总结。

1.4 执行事件的步骤
  1. 获取元素
  2. 绑定事件
  3. 编写事件处理程序

2. 事件处理程序

事件处理程序大概有三种(排除IE),分别是HTML事件处理程序、DOM0级事件处理程序和DOM2级事件处理程序(注:没有DOM1级这个概念)

2.1 HTML事件处理程序

在HTML代码中调用函数执行就是HTML事件处理程序。在HTML中,某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。

比如:

<input type="button" onclick="alert('clicked!')">

这种方式不推荐使用,因为他会有两个问题:

  1. 触发事件时事件还没加载的顺序问题
  2. HTML与JS高度耦合的问题
2.2 DOM0级事件处理程序

DOM0级事件处理程序是JavaScript指定事件处理程序的传统方式。这种方式是将一个函数赋值给一个事件处理程序属性。

简单例子:

<body>
 <input type="button" name="btn" id="myBtn" value="点击">

 <script>
     var mybtn = document.getElementById("myBtn");
     mybtn.onclick = function() {
         alert(this.name)
     }
 </script>
</body>

在这里插入图片描述

this指向的是目标元素,并且事件在冒泡阶段(冒泡阶段下节总结)被捕获。

删除该事件处理程序的方法

mybtn.onclick = null
2.3 DOM2级事件处理程序

DOM2级事件定义了两个方法,用于添加和删除事件处理程序:addEventListener()removeEventListener()。所有DOM节点都包含这两个方法,并且他们都接收三个参数:要处理的事件名事件处理函数,一个布尔值。布尔值为false(默认)时表示在冒泡阶段调用事件处理函数,为true时表示在捕获阶段调用事件处理程序。

一个简单例子:

<body>
 <input type="button" name="btn" id="myBtn" value="点击">
 <script>
     var mybtn = document.getElementById("myBtn");
     mybtn.addEventListener('click', function dd() {
         alert(this.type)
     }, false)
 </script>
</body>

在这里插入图片描述

可以对同一事件源添加多个DOM2级事件处理程序

<script>
     var mybtn = document.getElementById("myBtn");
     mybtn.addEventListener('click', function dd() {
         console.log(this.type);
     }, false)
     mybtn.addEventListener('click', function dd() {
         console.log(this.name);
     }, false)
 </script>

在这里插入图片描述

移除DOM2级事件处理程序只能使用removeEventListener(),并且传入的事件处理函数必须和传入相同,所以,我们应该在添加事件时将事件处理函数先赋值给一个变量,这样才能保证移除时的函数相同

<script>
     var mybtn = document.getElementById("myBtn");
     function dd() {
         console.log(this.type);
     }
     mybtn.addEventListener('click', dd, false)
     mybtn.removeEventListener("click", dd, false)
 </script>
//这样点击事件就不会被触发了,结果是什么也不会输出

最后,IE事件处理程序略略略过~~!

3. 批量绑定事件

3.1 批量绑定

补充批量绑定事件的知识,主要是复习一下闭包,因为对闭包的理解还谈不上深刻。

首先来写一个批量绑定事件

<body>
<ul>
  <li>文字按钮</li>
  <li>文字按钮</li>
  <li>文字按钮</li>
  <li>文字按钮</li>
</ul>
<script>
  var ps = document.getElementsByTagName("li")
  for (var i = 1; i < ps.length; i++) {
      ps[i].onclick = function() {
          console.log(i);
      }
  }
</script>
</body>

点击这四个li,出现4个4,并非1,2,3,4

在这里插入图片描述

这就是异步的影响(关于异步可以看我的的另一篇文章http://www.wyb.plus/index.php/archives/1317/),由于事件是异步的,所以for循环在每次执行后并不会立即把i传给ps,只有等到for循环结束了,才会把i传给ps。由于for循环结束时i=4,所以事件中的i每次点击都为4。

利用IIFE形成的闭包来解决异步造成的影响 >>>

var ps = document.getElementsByTagName("li")
     for (var i = 0; i < ps.length; i++) {
         (function(m) {
             ps[m].onclick = function() {
                 console.log(m);
             }
         })(i)
     };

在这里插入图片描述

除了IIFE之外还有一种方法,那就是强制添加属性的方法:

var ps = document.getElementsByTagName("li")
     for (var i = 0; i < ps.length; i++) {
         ps[i].id = i; //先编号
         ps[i].onclick = function() {
             console.log("先编号");
             console.log(this.id);
         }
     };

在这里插入图片描述

这种方法会在异步事件之前就将i赋值给ps,在异步之前就将他们区分开了。

3.2 JS事件中的对应与排他

事件的对应 >>>

首先设计一个这样的结构:

在这里插入图片描述

我们要实现这样一个功能:当我点击上面一排的li时,下面一排对应的li背景颜色被改变

<script>
     var u1 = document.querySelectorAll(".u1>li")
   var u2 = document.querySelectorAll(".u2>li")
   var index = 0;
     for (var i = 0; i < u1.length; i++) {
         u1[i].index = i;
         u1[i].onclick = function() {
             u2[this.index].style.backgroundColor = "pink"
         }
     }
</script>

在这里插入图片描述

这个功能的实现是因为这两个ul里面的结构相同,li的索引相同,所以我们通过一个信号量去关联他们的索引,从而达到给A添加事件去改变B。

排他 >>>

首先设计一个这样的结构:

在这里插入图片描述

要实现这样一个功能:当我点击一个li,它自己不变,其余的所有li的背景颜色被改变

<script>
        var u1 = document.querySelectorAll(".u1>li") 
        var index = 0;
        for (var i = 0; i < u1.length; i++) {
            u1[i].index = i;
         u1[i].onclick = function() {
            this.style.backgroundColor = "cadetblue"
             for (var j = 0; j < u1.length; j++) {
                if (this.index !== j) {
                     u1[j].style.backgroundColor = "pink"
                }
             }
          }
        }
    </script>

在这里插入图片描述

这就是一个最简单的排他的应用

4. 事件流

首先要明白:事件流,事件传播顺序,事件模型都是指的同一个东西 >>> 从页面中接收事件的顺序,也可以理解为事件在页面中传播的顺序。

IE和Netscape开发团队居然提出了两个截然相反的事件流概念。

  • IE的事件流是 – 事件冒泡流
  • 标准浏览器事件流是 – 事件捕获
  • 当事件流在事件源身上时又叫目标阶段

所以事件流有三个阶段:捕获阶段–目标阶段–冒泡阶段

4.1 事件冒泡

事件冒泡:从事件源开始逐级向上传播到祖先节点。

4.2 事件捕获

事件捕获:从祖先节点逐级传播到事件源身上。

4.3 自定义监听阶段

通过DOM2级事件处理程序的addEventListener的第三个参数可以控制监听的阶段:false(默认)为冒泡阶段,true为捕获阶段

4.4 阻止默认事件和事件传播

JS中的默认行为是浏览器给我们提供的一些功能,例如:

  • a标签,默认click事件,点击跳转
  • contextmenu 右击弹出菜单
  • submit提交表单等等
<a href="http://www.wyb.plus/" target="_blank">博客</a>

DOM中提供preventDefault()方法来取消事件默认行为,需要通过Event对象调用此方法。

 var prevent = document.querySelector("#aa");
    prevent.onclick = function(event) {
        event.preventDefault();
    }

IE中需要使用 return false…(略略略)

阻止事件传播 >>>

DOM中提供stopPropagation()方法,但IE不支持。仍然使用Event对象在事件函数中调用就行

  event.stopPropagation()

IE中提供的是,cancelBubble属性,默认为false,当它设置为true时,就是阻止事件冒泡,也是用Event对象在事件函数中调用

 event.cancelBubble = true;

5. Event对象

5.1 Event是什么

Event对象代表事件的状态。这个状态里面包含了这个事件触发时的所有细节。

Event是JS的一个系统内置对象,平时无法使用,当有事件被触发后才会生成这个对象。

查看一下这个对象上的一些属性:

var prevent = document.querySelector("#aa");
 prevent.onclick = function(event) {
     console.log(event);
 }

除了一些位置类的属性外,比较重要的属性有:

在这里插入图片描述

type:事件类型

target:事件的目标节点

button:0,1,2 >>> 分别对应左键,滚轮,右键

bubbles:表示事件是否冒泡

cancelable:true表示停止冒泡,这就是IE中停止冒泡的方法

currentTarget:表示事件处理程序当前正在处理事件的那个元素,与this指向一致

eventPhase:调用事件处理程序的阶段 >>> 0表示捕获,1表示处于目标,2表示冒泡。这也是事件流的传播顺序。

5.2 怎么用

要注意只有在事件发生的过程中, Event对象才生效。

在调用事件处理函数的时候,标准浏览器是传给这个事件处理函数一个实际参数Event对象。但是在IE6,7,8是给window对象绑定一个当前的Event属性。

具体点说:

在IE中,Event是一个全局变量,不存在作用域问题。谁触发了事件,那再事件绑定的函数中,你可以直接使用Event的属性做任何操作。

在标准浏览器中,每个事件绑定的处理函数中都会默认传入一个形参event,而且是在第一个形参的位置,并且我们不需要去写这个形参

举个例子:比如我将上面的例子中的事件处理函数的第一个参数由event改成hehe,没有传入event

var prevent = document.querySelector("#aa");
 prevent.onclick = function(hehe) {
     console.log(event);
 }

直接打印event,结果仍然是原来的那个对象,所以我们不需要去写这个形参。

在这里插入图片描述

6. 事件代理

6.1 概述

事件代理也叫事件委托。

JavaScript高级程序设计上讲:事件委托就是利用事件事件冒泡,只指定一个事件处理程序,就可以管理一类型的所有事件。

仔细揣摩这个取快递的例子:一个寝室的同学都卖了东西,现在要在某一天取快递。现在由两种方案,一种是每个人都去菜鸟驿站等着,然后自己取自己的快递;另一种是同学们让寝室的室长代取,室长取了之后一一分发快递。为了提高效率,当然是选择第二种方案。第二种方案有一个优势:那就是不管寝室里有多少人,也不管后面还会不会增加人,室长都能帮忙取快递。

这里面还有两层意思 >>>

第一层:不管寝室的室长自己有没有快递,他都要取快递,即室长对应的DOM节点是有事件的。

第二层:寝室来了新室友,室长也是要帮新室友取快递的,即程序中新添加的DOM节点也是有事件的。

6.2 为什么要用委托

通过上面的例子我们也能够想到,很多人一起去快递会造成菜鸟驿站的拥堵,取快递的过程就会变慢,如果我们只派一个同学去取,那么就能提高很多的效率,节约很多的性能 。(关于js操作DOM浪费性能这点,可以参考这篇《JS当中DOM操作成本到底高在哪儿?》https://blog.csdn.net/qq_44607694/article/details/105520967)

6.3 事件委托的原理

事件委托是利用事件冒泡的原理来实现的。事件冒泡上文已经讲了,再举个例子:页面上有这样的一个节点树 div>ul>li,当我们给最里面的li绑定一个点击事件的时候,同时ul也被点击了,然后是div也被点击了。所以我们可以通过给ul添加一个事件来代理里面的所有li的事件

6.4 实现方法

实现一个功能:点击li输出对应的文本内容

一般方法:不使用代理,批量绑定事件

<body>
 <ul class="uull">
     <li>111</li>
     <li>222</li>
     <li>333</li>
 </ul>
</body>
<script>
 var oUl = document.querySelector(".uull")
 var aLi = oUl.querySelectorAll("li")
 for (var i = 0; i < aLi.length; i++) {
     aLi[i].onclick = function() {
         console.log(this.innerText);
     }
 } 
</script>

使用事件委托:

<script>
 var oUl = document.querySelector(".uull")
 var aLi = oUl.querySelectorAll("li")
 oUl.onclick = function() {
     console.log(event.target.innerText);
 }
</script>

在这里插入图片描述

这样写有一点点缺陷,那就是如果我只想事件代理的效果就像直接给节点绑定事件的效果一样怎么办,比如说这有点击li才会触发。那么就需要用到上面讲的Event对象的target属性了。

由与Event对象是有兼容性问题的,所以这里兼容一下

 var oUl = document.querySelector(".uull")
 var aLi = oUl.querySelectorAll("li")
   
 oUl.onclick = function(e) {
         var e = e || window.event
         var target = e.target || e.srcElement
         if (target.nodeName.toLowerCase() == "li") {
             console.log(target.innerText);
         }
     }
6.5新增节点的代理

我们使用了事件代理了,现在新增一个子节点,看看是否有事件

<ul class="uull">
     <li>111</li>
     <li>222</li>
     <li>333</li>
     <li>444</li>
 </ul>

仍然使用上面事件代理的方法

var oUl = document.querySelector(".uull")
var aLi = oUl.querySelectorAll("li")

oUl.onclick = function(e) {
      var e = e || window.event
      var target = e.target || e.srcElement
      if (target.nodeName.toLowerCase() == "li") {
          console.log(target.innerText);
      }
  }

在这里插入图片描述

嗯,完美 !

7. 参考资料

  • –《JavaScript事件(事件类型、事件目标、事件处理程序、事件对象、事件流)》

  • – 作者:@77

  • –来源:CSDN

  • – 《JavaScript中的Event事件对象详解》

  • – 作者:沐枫自然

  • – 来源:CSDN

  • – 《JS中事件的核心概念》

  • – 作者:Lin_Dan_Dan

  • – 来源:CSDN

  • – 《js中的事件委托或是事件代理详解

  • – 作者:凌云之翼

  • – 来源:博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你是时光 轻轻呵唱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值