ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] # 事件 &emsp;&emsp;事件是可以被控件识别的操作,如按下确定按钮,选择某个单选按钮或者复选框。每一种控件有自己可以识别的事件,如窗体的加载、单击、双击等事件,编辑框(文本框)的文本改变事件,等等。 &emsp;&emsp;事件是基于 触发-响应 机制实现的,当用户对控件做某些**操作**时,如 点击,移入鼠标,输入文字等,控件会识别到该操作,并作出对应的**响应**。 ## 事件三要素(掌握) 1. **事件源**: 触发事件的元素(被动) ​ 2. **事件名称**: click 点击事件 ​ 3. **事件处理函数**: 事件触发后要执行的代码(函数形式) ## 事件的基本使用 注册事件,必须使用**事件三要素**: **1、事件源 ​ 2、事件名称 ​ 3、事件处理函数** html和css代码 ``` <input id="hitme" type="button" value="点我!"/> ``` JavaScript代码 ``` //1、获取按钮对象 var hitme = document.getElementById('hitme'); console.log(hitme); //2、注册(绑定)点击事件 hitme.onclick = function () { alert('别点我,疼!') } ``` <br> # **注册事件的三种方式** &emsp;&emsp;这节课开始,我们来深入学习事件。首先,回顾以前注册事件的做法,在团队开发中,当一个按钮要被多个用户使用时,多个用户可能会对一个按钮进行事件注册,此时每个用户都的代码都写在了 js 文件中,当引入多个js文件时,后面引入的js会覆盖掉前面引入的js的对按钮的操作。即前面引入的js中的注册事件会被后面引入的js的注册事件覆盖掉。 &emsp;&emsp;下面对事件相关的方法进行详细学习,以解决以上存在的事件覆盖的问题。 **addEventListener**,将指定的监听器注册到 EventTarget(事件目标)上,当该对象触发指定的事件时,指定的回调函数就会被执行。说白了,就是addEventListener可以将时间监听器注册到btn按钮上,当事件发生时,调用事件处理函数。 1、以前的方式:无法给一个对象注册多个同种事件 2、addEventListener方式:有浏览器兼容性问题 &emsp;&emsp;html和css代码 ``` <input id="btn" type="button" value="按钮"/> ``` &emsp;&emsp;JavaScript代码 ``` var oBtn =document.getElementById('btn'); // 后写的事件会覆盖先写的事件 // 方式一 /*oBtn.onclick=function() { alert('注册事件1'); //弹出框 }; oBtn.onclick=function() { alert('注册事件2'); };*/ //方式二 /*oBtn['onclick']=function () { alert('注册事件01'); }; */ //方式三 oBtn.addEventListener('click',function () { alert('注册事件001'); },false) //在 addEventListener('click',function () {alert('注册事件001');},false) 中吗, // 第一个参数 'click' 是事件类型,不加 on ; //第二个参数 function () {alert('注册事件001');} 是事件处理函数; //第三个参数 false 是布尔类型,默认值为 false ; true捕获阶段调用事件处理方法;false冒泡阶段调用事件处理方法。 ``` ### 注册事件的兼容性问题 &emsp;&emsp;下面来看,注册事件的时候,浏览器的兼容性问题,我们学习过以下三种种注册事件存在的问题: 1、以前:无法给同一个对象的同一个事件注册多个事件处理函数 2、addEventListener:有浏览器兼容性问题,是DOM中的标准方法。 3、attachEvent:是IE中特有的方法 &emsp;&emsp;要解决这个问题,我们就得利用自定义函数,自己写函数去实现。   html和css代码 ``` <button id="btn">按钮</button> ```  JavaScript代码 ``` var oBtn =document.getElementById('btn'); addEventListener1(oBtn,'click',function () { alert('已处理兼容性问题'); },false); //这里 false 可写可不写,默认值是 false ; // 创建一个addEventListener的方法,参数: element--事件源 type--事件类型 fn--执行函数 function addEventListener(element,type,fn){ if(element.addEventListener){ //判断浏览器是否支持 addEventListener ,若符合,则执行下一行代码。 element.addEventListener(type,fn); }else if (element.attachEvent){ //判断该浏览器是否支持 attachEvent ,若符合,则执行下一行代码。 element.attachEvent('on'+type,fn); }else{ element['on'+type]=fn; //如果都不支持,就直接使用element['onclick']的方法 } } // attachEvent——兼容:IE7、IE8;不兼容firefox、chrome、IE9、IE10、IE11、safari、opera // addEventListener——兼容:firefox、chrome、IE、safari、opera;不兼容IE7、IE8 ``` &emsp;&emsp;以上方式一般既可以解决这几个方法的浏览器兼容性问题,但是在极端情况下还是有可能存在不支持的浏览器,庆幸的是,当前主流浏览器都支持了addEventListener,即使是老版本的IE浏览器也能用attachEvent解决,最后一种判断的写法element\["on"+eventName\] ,是作为一种极端情况下的解决方案。 <br> ## 移除事件的三种方式 &emsp;&emsp;在开发中,如果想让按钮的事件处理函数只能执行一次,怎么办?如何移除元素的事件? &emsp;&emsp;html和css代码 ``` <button id="btn">按钮</button> ``` &emsp;&emsp;JavaScript代码 ``` //如何移除元素的事件? var oBtn = document.getElementById('btn'); ``` ``` // 第一种移除事件的方法 oBtn.onclick = function(){ alert(1111); oBtn.onclick = null; } ``` ``` // 第二种 oBtn.addEventListener('click', fn) function fn(){ alert(111); oBtn.removeEventListener('click', fn); // removeEventListener第二个参数只能接收非匿名函数 } ``` ``` // 第三种,兼容ie9-11 oBtn.attachEvent('onclick', fn); function fn(){ alert(1111); oBtn.detachEvent('onclick', fn); ``` >[info] 小结 > 移除事件的三种方式小结如下: > 1、onclick:让按钮的事件处理函数只能执行一次,onclick=null ; > 2、removeEventListener:如果想要移除事件,注册事件的时候就不能使用匿名函数; > 3、detachEvent:谷歌中不支持,IE9-IE11中支持,了解即可; ### 移除事件的兼容性问题 解决移除事件的兼容性问题 跟 解决注册事件兼容性问题的函数相似。 注意,我们的代码知识作为一个演示,在这里作为了解即可,以后会使用使用框架,框架中已经帮我们做了很好的兼容性处理。 ``` var btn = document.getElementById('btn'); addEventListener(btn, 'click', fn); //事件处理函数 function fn() { alert('已经兼容主流浏览器的事件移除'); removeEventListener(btn, 'click', fn); } //解决移除事件的浏览器兼容性问题 //创建一个removeEventListener方法,用来兼容移除事件 //element 元素, type 事件类型 fn 事件处理函数 function removeEventListener(element, type, fn) { if (element.removeEventListener) { element.removeEventListener(type, fn); }else if (element.detachEvent) { //处理IE6-IE11之前的版本 element.detachEvent('on'+type, fn); }else { // 相当于element.onclick = null element['on'+type]=null; } } ``` ## 事件的三个阶段 捕获阶段 目标阶段 冒泡阶段 下面来学习addEventListener的第三个参数的作用,通过查文档可以知,addEventListener的第三个参数是一个布尔类型。 1、第三个参数是false时,事件从里到外执行,这种效果叫事件冒泡; 2、第三个参数是true时,事件从外到里执行,执行顺序颠倒过来了,这种效果叫做事件捕获; html和css代码 ``` <style> #box1 { width: 300px; height: 300px; background-color: blue; } #box2 { width: 200px; height: 200px; background-color: yellow; } #box3 { width: 100px; height: 100px; background-color: red; } </style> <script src="common.js"></script> <div id="box1"> <div id="box2"> <div id="box3"> </div> </div> </div> ``` JavaScript代码 ``` //addEventListener的第三个参数的作用 var box1 = my$('box1'); var box2 = my$('box2'); var box3 = my$('box3'); var array = [box1, box2, box3]; for (var i = 0; i < array.length; i++) { array[i].addEventListener('click', function () { //输出事件源的id console.log(this.id); }, true); } //以上代码,在点击box3的时候,除了box3本身的事件会触发,还会触发box2和box1的事件,而且这种触发顺序是从里到外,这种效果叫做 事件冒泡 for (var i = 0; i < array.length; i++) { array[i].addEventListener('click', function () { //输出事件源的id console.log(this.id); }, false); } //以上代码,在点击box3的时候,除了box3本身的事件会触发,还会触发box2和box1的事件,而且这种触发顺序是从外到里,这种效果叫做 事件捕获 ``` >[info]事件发生的时候,要经过事件的三个阶段,我们常常使用的是事件冒泡阶段,而其他两个阶段不能人为干预。 在JavaScript中,事件有以下三个阶段 第一阶段:捕获阶段 第二阶段:目标阶段(执行当前点击的元素) 第三阶段:冒泡阶段 第一阶段:捕获阶段 ![](https://img.kancloud.cn/c9/52/c9520a5d38353e44c93840b884bff7ea_452x288.png) 第二阶段:目标阶段(执行当前点击的元素) 第三阶段:冒泡阶段 ![](https://img.kancloud.cn/6b/22/6b22c476e554e66b12178da390f4431f_449x283.png) 注意,注册事件有三种,其中onclick、attachEvent没有第三个参数,实际上我们无法通过onclick、attachEvent来干预事件的第一阶段和第二阶段, 因为onclick、attachEvent都只有两个参数, 而且onclick、attachEvent注册的事件默认是冒泡阶段。很多时候我们只关心事件的第三阶段,即冒泡阶段。 //事件的阶段 // 第一阶段:事件捕获 // 第二阶段:目标阶段(执行当前点击的元素) // 第三阶段:事件冒泡 // onclick、attachEvent只有事件冒泡阶段 /*for (var i = 0; i < array.length; i++) { array[i].attachEvent('onclick',function () { console.log(this.id); }) }*/ for (var i = 0; i < array.length; i++) { array[i].onclick=function () { console.log(this.id); } } document.body.onclick=function () { console.log(this); } document.onclick=function () { console.log(this); } ### 事件委托(掌握) 案例 选择美女:当点击li时,选中li 在事件冒泡中,怎么获取被点击的元素?我们可以利用事件冒泡的特点:当子元素的事件发生时,父元素的同名事件也会发生。 利用事件冒泡的特点:当点击子元素时,父元素的同名事件也会发生。 知识点: 1、事件委托:原理就是事件冒泡。此处的事件委托是,当li被点击时,li并没有做事件处理,而是委托ul做了事件处理,ul事件中可以使用事件对象e获取target属性,完成事件处理。 2、事件对象(事件处理函数的参数e) html和css代码 ~~~ <ul id="ul">    <li>西施</li>    <li>貂蝉</li>    <li>昭君</li>    <li>凤姐</li>    <li>芙蓉姐姐</li> </ul> ~~~ JavaScript代码 ~~~ //事件冒泡的应用(事件委托) my$('ul').onclick = function (e) {    //e是事件对象,通过事件对象可以获取到触发事件的真正的元素相关的信息    // console.log(e);    // console.log(this);    //e.target:获取真正触发事件的那个元素    // console.log(e.target);    var target = e.target;    target.style.backgroundColor='lightblue'; } ~~~ **小结** ~~~ 知识点:事件委托,事件对象; 事件处理函数在事件发生时,由系统去调用,系统在调用事件处理函数时,会传入事件对象,所以我们可以直接使用事件对象。 ​ ~~~ ### 事件对象的属性(掌握) 通过事件对象,我们可以获取到事件发生的时候,和事件相关的一些数据。事件对象e的IE浏览器中存在兼容性问题,可以通过window.event获取事件对象以解决兼容性问题。下面演示事件对象的属性的使用和兼容性问题。 事件对象的三个属性 eventPhase:获取事件的阶段,数值表示 target:真正触发事件的元素,IE兼容性问题用 e.srcElement currentTarget:相当于this html和css代码 ~~~ <style>    #box1 {        width: 300px;        height: 300px;        background-color: blue;   } ​    #box2 {        width: 200px;        height: 200px;        background-color: yellow;   } ​    #box3 {        width: 100px;        height: 100px;        background-color: red;   } </style> <script src="common.js"></script> ​ <input type="button" id="btn" value="事件按钮"/> <div id="box1">    <div id="box2">        <div id="box3">        </div>    </div> </div> ​ ~~~ JavaScript代码 ~~~ //事件对象的属性的使用和兼容性问题 var box1 = my$('box1'); var box2 = my$('box2'); var box3 = my$('box3'); var array = [box1, box2, box3]; my$('btn').onclick = function (e) {    //当按钮被点击时,事件处理函数被系统调用,系统调用事件处理函数的时候,会传入事件对象。    //我们可以通过事件对象获取到事件的各种信息    //老版本的IE中,获取事件对象有兼容性问题    e = e || window.event;    //获取事件的阶段,数值表示 捕获阶段 1   目标阶段 2 冒泡阶段 3    console.log(e.eventPhase);    //获取真正触发事件的对象    // 获取触发事件的元素,老版本的IE中,使用srcElement获取    var target = e.target || e.srcElement;    console.log(target);    //当前执行事件的元素,相当于this    console.log(e.currentTarget); } ~~~ 事件冒泡中的三个属性代表的对象不一样 eventPhase:目标阶段、冒泡阶段 target:IE兼容性问题用 e.srcElement currentTarget:相当于this ~~~ for (var i = 0; i < array.length; i++) {    array[i].onclick = function (e) {        e = e || window.event;        //获取事件的阶段,数值表示 捕获阶段 1   目标阶段 2 冒泡阶段 3        console.log(e.eventPhase);        //获取真正触发事件的对象        // 老版本的IE中,使用srcElement获取        var target = e.target || e.srcElement;        console.log(target);        //相当于this        console.log(e.currentTarget);        console.log(this);   } } ~~~ event.type:获取事件名称。 应用场景:给多个事件指定同一个函数 好处:多个事件只使用了一个函数,减少内存的消耗 html和css代码 ~~~ <style>    body {        margin: 0;   } ​    #box {        margin: 100px;        width: 200px;        height: 200px;        background-color: blue;   } </style> <script src="common.js"></script> ​ <div id="box"></div> ​ ~~~ JavaScript代码 ~~~ //event.type:获取事件名称。 my$('box').onmouseover = function (e) {    e = e || window.event;    console.log(e.type); } ​ my$('box').onclick = fn; my$('box').onmouseover = fn; my$('box').onmouseout = fn; ​ //当多个(种)事件使用同一个事件处理函数时,可以使用e.type获取到当前发生的事件的名称 //多个事件只使用了一个函数,减少内存的消耗 ​ function fn(e) {    e = e || window.event;    //根据事件名称,执行不同的业务代码    switch (e.type) {        case 'click':            console.log('click事件');            break;        case 'mouseover':            console.log('鼠标移入');            break;        case 'mouseout':            console.log('鼠标移出');            break;   } } ​ ~~~ 当事件发生时,通过事件对象可以获取鼠标的位置坐标 ,如下: 1、e.clientX 和 e.clientY,获取相对于可视区域鼠标位置坐标,所有浏览器都支持; ​ 2、e.pageX 和 e.pageY,获取相对于整个文档的的位置,IE9以后开始支持; HTML和css代码 ~~~ <style>    body {        margin: 0;        height: 1000px;   } ​ </style> ~~~ JavaScript代码 ~~~ document.onclick=function (e) {    //获取可视区域内的鼠标的坐标,    // console.log(e.clientX);    // console.log(e.clientY); ​    //获取相对于整个文档的的位置。    console.log(e.pageX);    console.log(e.pageY); ​ } ~~~ ~~~ 1、当事件发生时,通过事件对象可以获取鼠标相对于可视区域的位置坐标 e.clientX 和 e.clientY     ,所有浏览器都支持,窗口位置;设置滚动条进行演示。 2、当事件发生时,通过事件对象可以获取鼠标相对于整个文档的的位置坐标 e.pageX 和 e.pageY ~~~ #### 需求 1、跟着鼠标飞的天使:当鼠在浏览器页面移动时,让天使(一张图片)跟着鼠标移动。 需求分析:当鼠标在文档中移动时,让图片的坐标跟着鼠标的坐标的变化而变化,即鼠标的位置和图片的位置保持相对静止。 html和css代码 ~~~ <style>    body {        height: 1000px;   }    #ts {        position: absolute;   } </style> <script src="common.js"></script> ​ <img src="images/tianshi.gif" id="ts" alt=""> ​ ~~~ JavaScript代码 ~~~ //1、鼠标移动事件 document.onmousemove=function (e) {    // console.log(e.clientX, e.clientY);    //在可视区域内移动    // my$('ts').style.left = e.clientX + 'px';    // my$('ts').style.top = e.clientY+ 'px';    //在整个文档中移动    my$('ts').style.left = e.pageX - 20 + 'px';    my$('ts').style.top = e.pageY - 20 + 'px'; } ~~~ 2、获取鼠标在文档中距离顶部的距离 (1)e.clientX 和 e.clientY,获取相对于可视区域鼠标位置坐标,所有浏览器都支持; ​ (2)e.pageX 和 e.pageY,获取相对于整个文档的的位置,IE9以后开始支持; 解决pageX和pageY的兼容性问题,思路:pageY = clientY+页面滚动出去的距离; 获取页面滚动出去的距离,document.body是文档的body元素: document.body.scrollLeft document.body.scrollTop 有些浏览器是使用document.documentElement(文档根元素 html)获取滚动出去的距离: document.documentElement.scrollLeft document.documentElement.scrollTop 为了解决浏览器兼容性问题,我们需要使用到document.body和document.documentElement两种对象。 html和css代码 ~~~ <style>    body {        height: 1000px;   } ​    #ts {        position: absolute;   } </style> ~~~ JavaScript代码 ~~~ //获取页面滚出去的距离 // 需求:获取鼠标在文档中距离顶部的距离 //   思路:鼠标距离文档顶部的距离 = clientY + 页面被滚动处理的长度 document.onclick=function (e) {    //处理事件对象的兼容性问题    e = e || window.event; ​    //获取被滚动出去的页面的长度    console.log(document.body.scrollLeft);    console.log(document.body.scrollTop); ​    //文档元素 html元素    console.log(document.documentElement);    console.log(document.documentElement.scrollLeft);    console.log(document.documentElement.scrollTop); } ​ //获取页面滚出去的距离 function getScroll(){    var scrolLeft = document.body.scrollLeft || document.documentElement.scrollLeft;    var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; ​    return{        scrolLeft:scrolLeft,        scrollTop:scrolLeft   } } ​ ~~~ 解决pageX和pageY的兼容性问题,e.pageX || e.clientX+getscroll().scrollLeft ~~~ // 需求:获取鼠标在文档中距离顶部的距离 //   思路:鼠标距离文档顶部的距离 = clientY + 页面被滚动处理的长度 document.onclick = function (e) {    console.log(getPage(e).pageX, getPage(e).pageY); } ​ // 鼠标距离文档顶部的距离 = clientY + 页面被滚动处理的长度 function getPage(e) {    var pageX = e.pageX || e.clientX + getScroll().scrolLeft;    var pageY = e.pageY || e.clientY + getScroll().scrollTop;    return{        pageX:pageX,        pageY:pageY   } } // 注意: IE中document.onclick事件无效, 需使用document.body.onclick进行测试 ~~~ 需求:获取鼠标在div中的坐标; 需求分析:鼠标在div中的位置 = 鼠标在文档中的坐标 - div在文档中的坐标(偏移量); div的偏移量:offsetLeft、offsetTop; html和css代码 ~~~ <style>    body {        margin: 0;   } ​    #box {        width: 300px;        height: 300px;        border: 1px solid red;        margin: 103px 10px 10px 103px;   } </style> <div id="box"> ​ </div> ~~~ JavaScript代码 ~~~ my$('box').onclick=function (e) {    console.log(getPage(e).pageX);    console.log(getPage(e).pageY);    //元素在文档中的坐标(偏移量)    console.log(this.offsetLeft);    console.log(this.offsetTop);    //获取鼠标在div中的坐标    var divLeft = getPage(e).pageX - this.offsetLeft;    var divTop = getPage(e).pageY - this.offsetTop;    console.log(divLeft, divTop); } ~~~ ### 阻止事件传播(掌握) 事件传播的经典行为是事件冒泡,下面介绍如何阻止事件冒泡。 我们曾使用return false取消a标签的默认跳转行为,除了这种写法,DOM中也提供了阻止a标签默认行为的标准方法 e.preventDefault(),而在IE老版本中则使用 e.returnValue = false; html和css代码 ~~~ <a id="link" href="http://www.baidu.com">我寻你千百度</a> ​ <style>    #box1 {        width: 300px;        height: 300px;        background-color: blue;   } ​    #box2 {        width: 200px;        height: 200px;        background-color: yellow;   } ​    #box3 {        width: 100px;        height: 100px;        background-color: green;   } </style> <script src="common.js"></script> ​ <div id="box1">    <div id="box2">        <div id="box3">        </div>    </div> </div> ​ ​ ~~~ JavaScript代码 ~~~ my$('link').onclick = function (e) {    alert('跳转吧');    // return false;    //DOM中的标准方法    // e.preventDefault();    //IE的老版本中使用e.returnValue    e.returnValue = true; } ​ ~~~ 停止事件传播->阻止事件冒泡: 标准方法 event.stopPropagation(); IE低版本 event.cancelBubble = true; 标准中已废弃 ~~~ for (var i = 0; i < array.length; i++) {    array[i].onclick = function (e) {        console.log(this.id);        //阻止冒泡        // Propagation:传播        // - 标准方式 event.stopPropagation();        // e.stopPropagation();        // - IE低版本 event.cancelBubble = true; 标准中已废弃        e.cancelBubble = true;   } } ~~~ ### 常用的鼠标和键盘事件(掌握) 需求: 控制输入年龄的文本框,使其只能输入数字和删除输入的数字 。 在开发中,经常会看到这样的需求 “ 控制输入年龄的文本框,使其只能输入数字和删除输入的数字 ” 。用户在输入数字时,需要按下键盘进行输入,怎么知道键盘是否按下呢?要完成这种需求,我们需要学习键盘事件! onkeyup事件:键盘按键抬起触发 ; ​ onkeydown事件:键盘按键按下触发; 键盘码 e.keyCode的值在 48-57 之间对应着键盘上的数字,回退(删除)按键的键盘码是 8,取消键盘按下时的默认行为(所谓默认行为是指:按键的值落到文本框内)。 html和css代码 ~~~ 年龄:<input type="text" name="userAge" id="tx"/> ~~~ JavaScript代码 ~~~ my$('tx').onkeydown = function (e) {    //处理事件对象的浏览器兼容性问题    e = e || window.event;    // e.keyCode: 键盘码, 在[48, 57]区间内的键盘码,对应着十个数值    // console.log(e.keyCode);    if ((e.keyCode < 48 || e.keyCode > 57) && e.keyCode !== 8) {        //取消键盘输出的默认行为的执行, 按键的值将不落到文本框中        // return false;        e.preventDefault();   } } ​ ​ ~~~ **小结** ~~~ 通过前面的学习,我们学习了一下这些事件,小结如下。 ​ onkeyup事件:键盘按键抬起触发 ; onkeydown事件:键盘按键按下触发; ​ onclick事件:元素被点击时触发; onmouseup事件:鼠标按键放开时触发; onmousedown事件:鼠标按键按下触发; onmousemove事件:鼠标移动触发; ~~~