- 1. js代码书写位置&事件初识&文档加载
- 2. 再会js中的事件
- 3. 深入理解事件
- 3.1 事件触发和事件对象
- 3.2 事件的绑定(事件监控)
- 3.3 事件的冒泡
- 3.4 事件的委派
- 3.5 事件的传播
- 3.6 事件的取消
- 3.6.1 取消事件传播
- 3.6.2 取消默认行为
-
js书写位置共四种,下面先来看前三种:
- 第一种方式:head中使用script标签,令type属性为"text/javascript",不写type默认也是"text/javascript"
- 第二种方式:将js代码写到标签的属性中,如onlick属性,当我们点击按钮时,js代码才会执行
- 第三种方式:head中使用script标签,指明src引入一个外部的js文件
-
在讲解第四种书写位置时,先来看事件和文档加载:
-
事件:
- 定义:就是用户和浏览器之间的交互行为,比如:点击按钮,鼠标移动、关闭窗口
- 我们可以在事件对应的属性中设置一些js代码,这样当事件被触发时,这些代码将会执行。即js的第二种书写位置,如在onclik属性中添加js代码,当点击时,执行js。种写法我们称为结构和行为耦合,不方便维护,不推荐使用
DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Documenttitle> head> <body> <button id="btn" onclick="alert('提交成功')"> 提交 button> body> html>
-
- ps:第一种和第三种方式也可以给js绑定事件,还是上例,如果通过在script标签中使用js代码绑定事件,首先要找到button按钮,然后为他绑定onclick事件,如下代码(下例中,onclik事件后面为function函数,称为回调函数,不由我们调用,当触发onclik事件时,自动调用),最终页面显示效果是:点击提交按钮后,没有反应(未d出提交成功的d窗),具体原因文档加载会讲
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script>
// 1. 首先找到button按钮:通过id号查找
// getElementByID:通过id号找到元素,后续会详细介绍,这样我们就找到了button按钮
var btn = document.getElementById("btn");
// 2. 为button按钮绑定onclick事件:一旦点击按钮,就相当于调用了该函数
btn.onclick = function(){
alert("提交成功");
};
script>
head>
<body>
<button id="btn">
提交
button>
body>
html>
-
文档加载:
- 浏览器在加载一个页面时,是按照自上向下的顺序加载的,读取到一行就运行一行。
- 如果将script标签写到页面的上边,无论是直接在script中书写js(第一种书写位置)还是在script中通过src引入外部js文件(第三种书写位置),都会先执行script中的js代码,此时页面还没有加载,即DOM对象还没有加载,因此无法获取DOM对象(上例中的button对象就无法获取)。
- 上例中button按钮的onclick事件会在加载完button后点击触发,由于js代码写到script标签中,先执行script(页面还没有加载,DOM对象还没有加载),js代码无法获取到button元素,最终导致onload函数不起作用。
-
既然我们不推荐第二种方式,第一种和第三种方式又存在弊端,故引出第四种js书写位置:将script标签及里面的js代码写到body中对应元素的下方
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<button id="btn">
提交
button>
<script>
var btn = document.getElementById("btn");
btn.onclick = function(){
alert("提交成功");
};
script>
body>
html>
2. 再会js中的事件
-
js语言本身不会产生事件,产生事件的是网页文档和网页文档中的html元素。事件就是网页文档或网页元素对外发出的通知。
-
html事件发生在html元素上,如上例中button按钮的onclick。html事件可以是浏览器行为,也可以是用户行为,如下html事件的实例:
- html页面完成加载(浏览器行为)
- html input字段改变时(浏览器行为)
- html按钮被点击(用户行为)
-
通常,当事件发生时,可以触发js代码。如果js需要对DOM对象触发的事件进行处理,就需要监听事件,浏览器会自动调用监听事件的js函数,js监听事件的方式有三种:
- 内联属性监听:
(已接触)
- DOM属性绑定监听:即上例
btn.onclick=function(){xxx}
(已接触) - 使用事件监听函数:对HTML元素对象调用
addEventListener(event,function,useCapture)
方法或attachEvent()
方法,绑定事件的处理函数(后续讲解)
- 内联属性监听:
-
常见的html事件如下:(各种事件可以在后续学习中慢慢体会,下表仅是部分事件)
事件 | 描述 |
---|---|
onchange | html元素改变 |
onclick | 用户点击html元素 |
onmousemove | 鼠标在元素中移动时发生 |
onmouseover | 鼠标指针移动到指定的元素上时发生 |
onmousedown | 当鼠标在元素上按下时发生 |
onmouseup | 当鼠标按键在元素上松开时发生 |
onmouseout | 用户从一个html元素上移开鼠标时发生 |
onkeydown | 用户按下键盘按键 |
onload | 浏览器已完成页面的加载 |
- onload是浏览器完成页面加载后自动触发,因此上例中的问题,除了使用js第四种书写位置,还可以通过在head的script标签中为元素绑定onload事件解决
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script>
// onload事件会在整个页面加载完成之后才触发,为window绑定一个onload事件
// 该事件对应的响应函数将会在页面加载完成之后执行,这样可以确保我们的代码执行时所有的DOM对象已经加载完毕了
window.onload = function(){
//获取id为btn的按钮
var btn = document.getElementById("btn");
//为按钮绑定一个单击响应函数
btn.onclick = function(){
alert("hello");
};
};
script>
head>
<body>
<button id="btn">提交button>
body>
html>
3. 深入理解事件
- 关于事件,在前面章节我们已经有了初步的接触,事件指的就是用户与浏览器交互的一瞬间。我们通过为指定事件绑定回调函数的形式来处理事件,当指定事件触发以后,我们的回调函数就会被调用,这样我们的页面就可以完成和用户的交互了。
- 事件触发:
- 事件的发生(触发)主要是由用户 *** 作引起的。
- 如mousemove这个事件就是由于用户移动鼠标引起的,在鼠标指针移动的过程中该事件会持续发生
- 当指定事件被触发时,浏览器就会调用对应的函数(代码)去响应事件。一般情况下,事件每触发一次,函数就会执行一次。因此,设置鼠标移动的事件可能会影响到鼠标的移动速度,所以设置该类事件时一定要谨慎。
- 事件对象:
- 当事件被触发时,就会调用响应函数,在DOM标准的浏览器中,浏览器每次都会将一个事件对象作为实参传递进响应函数。在事件对象中封装了当前事件相关的一切信息,比如鼠标的坐标,键盘哪个按键被按下,鼠标滚轮滚动的方向等等。
- IE中的事件对象:
- IE8以上的浏览器和标准浏览器一样
- 在IE8及以下的浏览器中,响应函数被触发时,浏览器不会传递事件对象,而是将事件对象作为window对象的属性保存。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<style type="text/css">
#areaDiv {
border: 1px solid black;
width: 300px;
height: 50px;
margin-bottom: 10px;
}
#showMsg {
border: 1px solid black;
width: 300px;
height: 20px;
}
style>
<script>
window.onload = function(){
/*
* 当鼠标在areaDiv中移动时,在showMsg中来显示鼠标的坐标
*/
//获取两个div
var areaDiv = document.getElementById("areaDiv");
var showMsg = document.getElementById("showMsg");
// 为areaDiv绑定onmousemove事件
areaDiv.onmousemove = function(event){ // 参数event写不写都行,详情见js语法-函数
/*
* 在IE8中,响应函数被处罚时,浏览器不会传递事件对象,
* 在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的
*/
/*if(!event){
event = window.event;
}*/
//解决事件对象的兼容性问题
event = event || window.event;
/*
* clientX可以获取鼠标指针的水平坐标
* cilentY可以获取鼠标指针的垂直坐标
*/
var x = event.clientX;
var y = event.clientY;
//在showMsg中显示鼠标的坐标
showMsg.innerHTML = "x = "+x + " , y = "+y;
};
};
script>
head>
<body>
<div id="areaDiv">div>
<div id="showMsg">div>
body>
html>
- 事件对象event包含与创建它的特定事件有关的属性和方法,触发的事件类型不一样,可用的属性和方法也不一样。下图第一张图片为标准浏览器一些公共方法或属性,第二张图片为IE8及以下浏览器一些公共方法或属性。
-
下述案例为让id为box1的div跟随鼠标移动而移动:
- 设置body的width和height为一个相对较大的值,可以出现水平和垂直方向滚动条
- 设置box1开启绝对定位
- 对于浏览器滚动条,chrome认为浏览器滚动条是body元素的,可以通过
document.body.scrollTop
来获取;火狐等浏览器认为滚动条是html的,通过document.documentElement.srollLeft
获取 clientX
和clientY
用于获取onmousemove事件中鼠标在当前的可见窗口的坐标pageX
和pageY
用于获取onmousemove事件中鼠标在当前页面的坐标,但是IE8及以下浏览器不兼容。- box1绝对定位的偏移量为可见窗口坐标加上滚动条偏移量
DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>title> <style type="text/css"> #box1{ width: 100px; height: 100px; background-color: red; /* 为box1开启绝对定位 */ position: absolute; } style> <script type="text/javascript"> window.onload = function(){ /* * 使div(box1)可以跟随鼠标移动 */ // 获取box1 var box1 = document.getElementById("box1"); // 为document绑定鼠标移动事件 document.onmousemove = function(event){ // 获取滚动条滚动的距离:解决chrome和其他浏览器的兼容问题 var st = document.body.scrollTop || document.documentElement.scrollTop; var sl = document.body.scrollLeft || document.documentElement.scrollLeft; //var st = document.documentElement.scrollTop; // 获取event:解决兼容问题 event = event || window.event; // 获取到鼠标在可见窗口的坐标 var left = event.clientX; var top = event.clientY; //设置div的偏移量 box1.style.left = left + sl + "px"; box1.style.top = top + st + "px"; }; }; script> head> <body style="height: 1000px;width: 2000px;"> <div id="box1">div> body> html>
-
在第三章中,我们已经提到了事件绑定的三种方式:
- 方式一:内联属性监听,也可以称为通过HTML元素指定事件属性来绑定
- 方式二:DOM属性绑定监听:通过DOM对象指定的属性来绑定
- 方式三:设置事件监听器(事件监听函数),使用
元素对象.addEventListener()
或者元素对象.attachEvent()
方法。
-
前两种事件绑定方式的比较
-
方式一:举例
,这种方式,当我们点击按钮以后,onclik属性中的js代码将会依次执行,也就是点击按钮后,页面会d出两个提示框。除了直接将代码编写到onclik属性中,也可以事先在外部定义好函数。
- 优点:设定步骤非常简单,并且能够确保事件处理程序在载入时被设定。
- 缺点:将js和html代码编写到一起,并不推荐;只能同时为一个元素的一个事件绑定一个响应函数,不能绑定多个,如果绑定了多个,则后边会覆盖掉前边的(在方式二讲解)
-
方式二:举例如下,效果同方式一。
var btn = document.getElementById("btn"); btn.onclik = function(){ alert("hello"); alert("world"); }
- 优点:html代码和js代码写在不同位置,维护起来更加容易。
- 缺点:只能同时为一个元素的一个事件绑定一个响应函数,不能绑定多个,如果绑定了多个,则后边会覆盖掉前边的。如下例,同一个btn元素的onclick事件绑定了两个响应函数,则第二个会覆盖第一个,即结果只有一个提示框,内容为"hello javascript"("hello"和"world"提示框内容不会生效)
var btn = document.getElementById("btn"); btn.onclik = function(){ alert("hello"); alert("world"); } btn.onclick = function(){ alert("hello javascript"); }
-
方式一和方式二我们在前述章节都有所接触,这两种方式都是日常用的比较多的,但更推荐使用第二种方式。两种方式都存在一个缺点:只能同时为一个元素的一个事件绑定一个响应函数,不能绑定多个,可以使用方式三来解决这个问题。
-
-
方式三:设置事件监听器详解:
- IE8以上浏览器及其他非IE浏览器:使用
元素.addEventListner(参数1, 参数2, 参数3)
可以同时为一个元素的相同事件同时绑定多个响应函数,当事件被触发时,响应函数将会按照函数的绑定顺序执行。该方法不支持IE8及以下的浏览器- 参数1:事件的字符串,去掉on,如onclik事件需要传"click"
- 参数2:回调函数,当事件触发时调用该函数
- 参数3:boolean,是否在捕获阶段触发事件,一般传false。事件捕获阶段含义在事件的传播一章中详细解释,暂且认为就是加载时。
- IE8及以下浏览器:使用
元素.attachEvent(参数1, 参数2)
,也可以为一个元素的相同事件绑定多个响应函数,但是执行顺序与addEventListener
相反,即先绑定的后执行,后绑定的先执行。- 参数1:事件的字符串,要on,同
addEventListener
- 参数2:回调函数,当事件触发时调用该函数
- 参数1:事件的字符串,要on,同
- this补充:我们知道函数都有一个隐含参数this,函数的不同使用方式this的含义不同,在事件监听函数(事件处理程序)中
- IE8及以下浏览器,this是window
- 其他浏览器的this就是设置了该事件处理程序的元素(从另一个角度想,
元素.事件监听函数()
本质是以方法的形式调用,故this是调用方法的那个对象,也即前面的元素)
DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Documenttitle> <script> window.onload = function(){ // 首先获取btn01、btn02、btn03 var btn01 = document.getElementById("btn01"); var btn02 = document.getElementById("btn02"); var btn03 = document.getElementById("btn03"); /** * 分别为btn01、btn02、btn03绑定事件 * - btn01:适用于IE8及以下浏览器 * - btn02:适用于其他浏览器 * - btn03:适用于兼容场景 */ // btn01:attachEvent,先绑定后执行,在IE8及以下浏览器验证请自行取消注释 // btn01.attachEvent("onclick", function(){ // alert(3); // }) // btn01.attachEvent("onclick", function(){ // alert(2); // }) // btn01.attachEvent("onclick", function(){ // alert(1); // alert(this); // }) // btn02:addEventListener,先绑定先执行,如果浏览器为IE8及以下浏览器,请将下方代码注释 // btn02.addEventListener("click", function(){ // alert(1); // alert(this); // }, false) // btn02.addEventListener("click", function(){ // alert(2); // }, false) // btn02.addEventListener("click", function(){ // alert(3); // }, false) // btn03:兼容模式 // 定义一个函数,用来为指定元素绑定响应函数 var bind = function(obj, eventStr, callback){ if(obj.addEventListener){ // 大部分浏览器 obj.addEventListener(eventStr, callback, false); } else{ // IE8及以下浏览器 obj.attachEvent("on" + eventStr, callback); } } // 调用函数 bind(btn03, "click", function(){ alert(this); }) } script> head> <body> <button id="btn01">IE8及以下浏览器button> <br><br> <button id="btn02">其他浏览器button> <br><br> <button id="btn03">兼容模式button> body> html>
- IE8以上浏览器及其他非IE浏览器:使用
- 事件的冒泡(Bubble):
- 指事件的向上传导,当后代元素上的事件被触发时,其祖先元素相同事件也会被触发。其本质是向上一层(上一代)传导,如下例中id分别为box1、box2、box3的div元素存在爷爷–爸爸–儿子的关系,此外,body是box1的爸爸,四者都绑定了单击响应函数,当触发box3的onclik事件,向上传导至box2,即触发box2的单击响应函数,box2又向上传导至box1,以此类推。
- 以onclik事件为例,由于子元素是父元素(祖先元素)的一部分,故单击子元素,其本质也是单击父元素(祖先元素),从这个角度来看,父元素(祖先元素)的onclik事件必然会触发,当然,我们正好单击了子元素,子元素本身的onclik事件也会触发。
- 在开发中,大部分情况冒泡都是有用的,如果不希望发生事件冒泡(单击子元素就是单击子元素本身,不认为单击了父元素),可以通过事件对象来取消冒泡:设置
event.cancelBubble=true
取消。还是下例当我们设置box3的event.cancelBubble=true
时,单击box3只会触发box3的onclik事件,由于取消冒泡,不会传导至box2,box2也因此无法继续传导下去;但是,直接单击box2,依然会触发box1、body的onclik事件。
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>title>
<style type="text/css">
#box1{
background-color: yellow;
width: 300px;
height: 300px;
}
#box2{
background-color: #bfa;
width: 200px;
height: 200px;
}
#box3{
background-color: gray;
width: 100px;
height: 100px;
}
style>
<script type="text/javascript">
window.onload = function(){
// 获取box1、box2、box3
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
// 为box3绑定单击响应函数函数
box3.onclick = function(event){
alert("我是box3的单击响应函数");
// 取消冒泡:可以将事件对象的cancelBubble设置为true,即可取消冒泡
// event = event || window.event;
// event.cancelBubble = true;
};
//为box2绑定一个单击响应函数
box2.onclick = function(event){
alert("我是box2的单击响应函数");
// 取消冒泡
// event = event || window.event;
// event.cancelBubble = true;
};
// 为box1绑定单击响应函数
box1.onclick = function(event){
alert("我是box1的单击响应函数");
// 取消冒泡
// event = event || window.event;
// event.cancelBubble = true;
}
//为body绑定一个单击响应函数
document.body.onclick = function(){
alert("我是body的单击响应函数");
};
};
script>
head>
<body>
<div id="box1">
<div id="box2">
<div id="box3">div>
div>
div>
body>
html>
- 下述案例为让id为box1的div跟随鼠标移动而移动,但是不会作用于box2(基于上一小节的案例)
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>title>
<style type="text/css">
#box1{
width: 100px;
height: 100px;
background-color: red;
/* 开启box1的绝对定位 */
position: absolute;
}
style>
<script type="text/javascript">
window.onload = function(){
/*
* 使div(box1)可以跟随鼠标移动
*/
// 获取box2
var box2 = document.getElementById("box2");
box2.onmousemove = function(event){
event = event || window.event;
// 取消事件冒泡:当box1移动到box2时,触发box2的onmousemove事件,取消冒泡,
// document不会触发onmousemove事件,因此box1不会跟着动
event.cancelBubble = true;
};
// 获取box1
var box1 = document.getElementById("box1");
// 为document绑定鼠标移动事件
document.onmousemove = function(event){
// 获取滚动条滚动的距离:解决chrome和其他浏览器的兼容问题
var st = document.body.scrollTop || document.documentElement.scrollTop;
var sl = document.body.scrollLeft || document.documentElement.scrollLeft;
// 获取event:解决兼容问题
event = event || window.event;
// 获取到鼠标在可见窗口的坐标
var left = event.clientX;
var top = event.clientY;
//设置div的偏移量
box1.style.left = left + sl + "px";
box1.style.top = top + st + "px";
};
};
script>
head>
<body style="height: 1000px;width: 2000px;">
<div id="box2" style="width: 500px; height: 500px; background-color: #bfa;">div>
<div id="box1">div>
body>
html>
3.4 事件的委派
- 在讲解事件的委派前,先来看一个例子(场景):ul祖先元素下有四个li子元素,其中第2-4个li元素均有一个a子元素,分别称作超链接一、超链接二、超链接三,三个a元素的class均为link。我们想要为三个a元素均绑定单击响应函数,可以使用循环遍历每个a并绑定单击响应函数
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script>
window.onload = function(){
var links = document.getElementsByClassName("link");
for (var i=0; i < links.length; i++){
links[i].onclick = function(event){
alert("我是超链接的单击响应函数");
}
}
}
script>
head>
<body>
<ul id="ul" style="background-color: #bfa;">
<li>
<p>p元素p>
li>
<li>
<a href="javascript:;" class="link">超链接一a>
li>
<li>
<a href="javascript:;" class="link">超链接二a>
li>
<li>
<a href="javascript:;" class="link">超链接三a>
li>
ul>
body>
html>
- 上述代码通过for循环的方式,虽然也完成了我们的需求:为每个超链接绑定单击响应函数,但是仍存在以下问题:
- for循环的方式,进行了3次事件绑定 *** 作,会影响程序的性能
- 假如有一个button按钮,点击后会新增一个超链接,我们希望新增的超链接也绑定同样的单击响应事件,上述代码无法完成(只能完成已有超链接的单击响应事件)
- 解决思路:只绑定一次事件,即可应用到多个元素上,即使元素是后添加的
- 解决方案:事件的委派:
- 指将事件统一绑定给元素(暂且称为目的子元素)共同的祖先元素上,通过祖先元素的event判断是否为目的子元素,如果是,则执行相应的代码。
event.target
:表示触发事件的对象,以onclik为例,假如我们单击的是子元素,则返回子元素对象,假如我们单击的是子元素以外的父元素部分,则返回父元素对象。
- 事件的委派本质是为祖先元素绑定事件,但最终效果是为子元素绑定了事件
- 事件委派是利用了冒泡,当后代元素事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的相应函数来处理事件。
- 通过委派可以减少事件绑定次数,提高了程序性能;还能为新增元素绑定事件,解决了上述问题。
- 指将事件统一绑定给元素(暂且称为目的子元素)共同的祖先元素上,通过祖先元素的event判断是否为目的子元素,如果是,则执行相应的代码。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script>
window.onload = function(){
/**
* 点击button按钮,新增a标签
*/
// 获取button
var btn = document.getElementById("btn01");
// 为button绑定单击响应函数
btn.onclick = function(){
// 创建li标签
var liLable = document.createElement("li");
// 为创建的li标签添加a标签
liLable.innerHTML = "新增超链接"
// 将创建的li标签添加到ul标签下
var ulLable = document.getElementById("ul");
ulLable.appendChild(liLable);
}
/**
* 通过事件的委派,为所有的a标签,包括点击button按钮新增的,绑定单击响应事件
*/
// 获取ul标签
var ulLable = document.getElementById("ul");
// 为ul绑定单击响应事件
ulLable.onclick = function(event){
// 解决浏览器兼容问题
event = event || window.event;
// event.target:获取触发事件的元素对象
if (event.target.className == "link"){
alert("触发单击响应函数");
}
}
}
script>
head>
<body>
<button id="btn01">添加超链接button>
<ul id="ul" style="background-color: #bfa;">
<li>
<p>p元素p>
li>
<li>
<a href="javascript:;" class="link">超链接一a>
li>
<li>
<a href="javascript:;" class="link">超链接二a>
li>
<li>
<a href="javascript:;" class="link">超链接三a>
li>
ul>
body>
html>
3.5 事件的传播
- 在网页中标签与标签之间是有嵌套关系的,比如下图,如果这时用户点击了sample按钮,则会以该按钮作为事件目标触发一次点击事件。事件从触发到完成这个过程称为事件传播。
<html>
<body>
<div>
<button id="bar">samplebutton>
div>
body>
html>
- 关于事件传播微团公司和网景公司有不同的理解,W3C则综合了两家公司的方案:
- 微团公司:事件应该由外向内传播,也就是当事件触发时,应该先触发当前元素事件,然后再向当前元素的祖先元素上传播,也就是说事件应该在冒泡阶段执行
- 网景公司:事件应该是由外向内传播的,也就是当前事件触发时,应该先触发当前元素的最外层的祖先元素,然后再向内传播给后代元素
- W3C:综合了两家公司方案,将事件传播分成三个阶段:
- 捕获阶段:这一阶段会从window对象开始向下一直遍历到目标对象,如果发现有对象绑定了响应事件则做相应的处理(从最外层的祖先元素,向目标元素进行事件捕获,但默认此时不会触发事件)
- 目标阶段:这一阶段已经遍历结束,事件捕获到目标元素,执行目标对象上绑定的响应函数。
- 事件冒泡阶段:这一阶段,事件的传播方式和捕获正好相反,会从事件目标一直向上遍历,直到window对象结束,这时对象上绑定的响应函数也会执行。
- 在事件的绑定一节,函数
addEventListener()
第三个参数为boolean,如果为true则表示在捕获阶段触发事件,一般我们并不希望这样,因此多数情况为false,该参数默认值为false,因此也可以省略不写。实际上,将参数值设为true后,最终的表现结果和false是一样的,由于捕获阶段下一步就是目标阶段,这个过程在后台进行,从前端结果来看,并无区别。 - IE8及以下浏览器中没有捕获阶段
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>title>
<style type="text/css">
#box1{
width: 300px;
height: 300px;
background-color: yellowgreen;
}
#box2{
width: 200px;
height: 200px;
background-color: yellow;
}
#box3{
width: 150px;
height: 150px;
background-color: skyblue;
}
style>
<script type="text/javascript">
window.onload = function(){
/*
* 分别为三个div绑定单击响应函数
*/
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
bind(box1,"click",function(){
alert("我是box1的响应函数")
});
bind(box2,"click",function(){
alert("我是box2的响应函数")
});
bind(box3,"click",function(){
alert("我是box3的响应函数")
});
};
function bind(obj , eventStr , callback){
if(obj.addEventListener){
//大部分浏览器兼容的方式
obj.addEventListener(eventStr , callback , true);
}else{
/*
* this是谁由调用方式决定
* callback.call(obj)
*/
//IE8及以下
obj.attachEvent("on"+eventStr , function(){
//在匿名函数中调用回调函数
callback.call(obj);
});
}
}
script>
head>
<body>
<div id="box1">
<div id="box2">
<div id="box3">div>
div>
div>
body>
html>
3.6 事件的取消
- 在讲解事件取消前,我们先来看下例:
- 例子:
box1-->box2-->box3-->a
从左到右辈分逐步降低,其中a
是一个超链接,链接到百度
首页,同时,a绑定了三个单击响应函数:第一个单击响应函数效果是出现d窗,内容a1;第二个单击响应函数效果是出现d窗,内容a2;第三个单击响应函数效果是出现d窗,内容a3。box1、box2、box3也均绑定了单击响应函数,效果都是出现d窗,内容分别为"我是box1的单击响应函数"、“我是box2的单击响应函数”、“我是box3的单击响应函数”。 - 动作:浏览器打开网页,鼠标单击"百度超链接"
- 效果:
- 首先连续出现6个d窗,d窗内容为:a1、a2、a3、我是box3的单击响应函数、我是box2的单击响应函数、我是box1的单击响应函数
- 然后新打开一个网页,跳转至百度首页
- 解释:单击"百度超链接",也就是会触发标签a绑定的事件,依次d出d窗a1、a2、a3;在事件传播的冒泡阶段,依次触发box3、box2、box1的单击响应函数,故依次出现d窗"我是box3的单击响应函数"、“我是box2的单击响应函数”、“我是box1的单击响应函数”。超链接a标签本身有默认行为:链接到其他网页并打开,故浏览器打开一个新的网页跳转至百度首页。
- 例子:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script>
window.onload = function(){
// 分别获取box1/box2/box3/link
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
var link = document.getElementById("link");
/**
* 为以上四个绑定单击响应函数
* - link绑定三个单击响应函数
* - box1、box2、box3分别绑定一个单击响应函数
*/
// 为link绑定单击响应函数,单击id为link的a元素,依次d出a1、a2、a3三个窗口
// link的第一个单击响应事件
if (link.addEventListener){
// 一般浏览器
link.addEventListener("click", function(){
alert("a1");
})
}
else{
// IE8及以下浏览器
link.attachEvent("onclick", function(){
alert("a3");
})
}
// link的第二个单击响应事件
if (link.addEventListener){
// 一般浏览器
link.addEventListener("click", function(){
alert("a2");
})
}
else{
// IE8及以下浏览器
link.attachEvent("onclick", function(){
alert("a2");
})
}
// link的第三个单击响应事件
if (link.addEventListener){
// 一般浏览器
link.addEventListener("click", function(){
alert("a3");
})
}
else{
// IE8及以下浏览器
link.attachEvent("onclick", function(){
alert("a1");
})
}
// 为box1、box2、box3分别绑定单击响应函数
function boxFun(obj, content){
obj.onclick = function(){
alert(content);
}
}
boxFun(box3, "我是box3的单击响应函数");
boxFun(box2, "我是box2的单击响应函数");
boxFun(box1, "我是box1的单击响应函数")
}
script>
head>
<body>
<div id="box1">
box1
<div id="box2">
box2
<div id="box3">
box3
<br>
<a href="http://www.baidu.com" target="_blank" id="link">百度超链接a>
div>
div>
div>
body>
html>
- 在上例效果的解释中,其实包含了两大方面:一是事件传播;二是默认行为。因此事件取消也是从这两方面就行阐述。
-
我们已经知道事件传播包括三个阶段:捕获阶段、目标获取阶段、冒泡阶段。取消事件传播主要针对的是目标阶段和冒泡阶段:
-
在目标阶段取消传播:指一个元素本身具有多个相同事件,在其中一个事件设置取消事件传播,该事件以后的事件都不会触发,该事件元素的祖先元素事件也不会触发。如上例标签a具有三个onclik事件,假如我们在第二个onclik事件中设置取消事件传播,则标签a的第三个onclik事件不会触发,box3、box2、box1的onclik事件也不会触发。在标签a的第二个onclik事件中取消事件传播的方法:
事件2的event对象.stopImmediatePropagation()
,该函数的解释: 不仅阻止事件继续分发到其他document,还会将事件分发就地停止,在当前事件之后注册的其他事件,都不会执行 。DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Documenttitle> <script> window.onload = function(){ // 分别获取box1/box2/box3/link var box1 = document.getElementById("box1"); var box2 = document.getElementById("box2"); var box3 = document.getElementById("box3"); var link = document.getElementById("link"); /** * 为以上四个绑定单击响应函数 * - link绑定三个单击响应函数 * - box1、box2、box3分别绑定一个单击响应函数 */ // 为link绑定单击响应函数,单击id为link的a元素,依次d出a1、a2、a3三个窗口 // link的第一个单击响应事件 if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(){ alert("a1"); }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(){ alert("a3"); }) } // link的第二个单击响应事件:******在响应阶段阻止事件传播********* if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(event){ alert("a2"); event.stopImmediatePropagation(); }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(event){ alert("a2"); event.stopImmediatePropagation(); }) } // link的第三个单击响应事件 if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(){ alert("a3"); }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(){ alert("a1"); }) } // 为box1、box2、box3分别绑定单击响应函数 function boxFun(obj, content){ obj.onclick = function(){ alert(content); } } boxFun(box3, "我是box3的单击响应函数"); boxFun(box2, "我是box2的单击响应函数"); boxFun(box1, "我是box1的单击响应函数") } script> head> <body> <div id="box1"> box1 <div id="box2"> box2 <div id="box3"> box3 <br> <a href="http://www.baidu.com" target="_blank" id="link">百度超链接a> div> div> div> body> html>
-
在冒泡阶段阻止事件传播:即目标阶段已经执行结束(目标元素的全部事件都已经触发完成),但阻止下一步的冒泡,也就是说目标事件元素的所有祖先元素(包括父元素)事件都不会触发。
-
如上例,在a标签的三个onclick事件中任一个设置
event.stopPropagation()
都会阻止下一步的冒泡,最终结果为d窗会d出a1、a2、a3,但不会出现"我是box3的单击响应函数"、“我是box2的单击响应函数”、"我是box1的单击响应函数"三个d窗。stopPropagation()
函数含义:会阻止事件继续分发到其他document节点,但是当前节点绑定的多个事件会继续按注册的顺序执行 。下方代码,仅在link的第二个单击响应事件处修改了代码:添加event.stopPropagation()
DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Documenttitle> <script> window.onload = function(){ // 分别获取box1/box2/box3/link var box1 = document.getElementById("box1"); var box2 = document.getElementById("box2"); var box3 = document.getElementById("box3"); var link = document.getElementById("link"); /** * 为以上四个绑定单击响应函数 * - link绑定三个单击响应函数 * - box1、box2、box3分别绑定一个单击响应函数 */ // 为link绑定单击响应函数,单击id为link的a元素,依次d出a1、a2、a3三个窗口 // link的第一个单击响应事件 if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(){ alert("a1"); }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(){ alert("a3"); }) } // link的第二个单击响应事件:********在冒泡阶段阻止事件传播*********** if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(event){ alert("a2"); event.stopPropagation(); }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(event){ alert("a2"); event.stopPropagation(); }) } // link的第三个单击响应事件 if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(){ alert("a3"); }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(){ alert("a1"); }) } // 为box1、box2、box3分别绑定单击响应函数 function boxFun(obj, content){ obj.onclick = function(){ alert(content); } } boxFun(box3, "我是box3的单击响应函数"); boxFun(box2, "我是box2的单击响应函数"); boxFun(box1, "我是box1的单击响应函数") } script> head> <body> <div id="box1"> box1 <div id="box2"> box2 <div id="box3"> box3 <br> <a href="http://www.baidu.com" target="_blank" id="link">百度超链接a> div> div> div> body> html>
-
-
-
在事件的冒泡一章中,我们提到,可以用
event.cancelBubble=true
来取消冒泡,和event.stopPropagation()
有异曲同工之妙。DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Documenttitle> <script> window.onload = function(){ // 分别获取box1/box2/box3/link var box1 = document.getElementById("box1"); var box2 = document.getElementById("box2"); var box3 = document.getElementById("box3"); var link = document.getElementById("link"); /** * 为以上四个绑定单击响应函数 * - link绑定三个单击响应函数 * - box1、box2、box3分别绑定一个单击响应函数 */ // 为link绑定单击响应函数,单击id为link的a元素,依次d出a1、a2、a3三个窗口 // link的第一个单击响应事件 if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(){ alert("a1"); }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(){ alert("a3"); }) } // link的第二个单击响应事件:********设置event.cancelBubble = true取消冒泡*********** if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(event){ alert("a2"); event.cancelBubble = true; }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(event){ alert("a2"); event.cancelBubble = true; }) } // link的第三个单击响应事件 if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(){ alert("a3"); }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(){ alert("a1"); }) } // 为box1、box2、box3分别绑定单击响应函数 function boxFun(obj, content){ obj.onclick = function(){ alert(content); } } boxFun(box3, "我是box3的单击响应函数"); boxFun(box2, "我是box2的单击响应函数"); boxFun(box1, "我是box1的单击响应函数") } script> head> <body> <div id="box1"> box1 <div id="box2"> box2 <div id="box3"> box3 <br> <a href="http://www.baidu.com" target="_blank" id="link">百度超链接a> div> div> div> body> html>
-
默认行为:具体指浏览器对当前事件的默认行为,比如上例,浏览器对当前超链接a标签的onclik事件具有默认的网页跳转行为(触发完成超链接的onclick事件后,浏览器自动默认执行跳转 *** 作)。类似地,用户点击注册,浏览器自动跳转至注册成功页;用户点击提交按钮后提交成功等等
-
取消默认行为:对于超链接默认行为加以取消,如取消点击超链接后的跳转,注册信息填写有误时,点击注册不会跳转至注册成功页,提交信息有误时,无法提交成功。
-
取消默认行为有两种方法:
- 方法一:在事件响应函数中使用
event.preventDefault()
,还是本章开头的案例,在link的第二个onclik事件响应函数中,添加event.preventDefault()
代码,最终效果是出现6个d窗,但网页不会跳转。也就是说浏览器对事件的默认行为是发生在事件传播之后的,无论在link的第几个onclik事件响应函数中添加event.preventDefault()
,都不会阻止事件传播,但会取消默认的跳转行为。在IE8及以下浏览器通过event.returnValue=false
方式实现。
DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Documenttitle> <script> window.onload = function(){ // 分别获取box1/box2/box3/link var box1 = document.getElementById("box1"); var box2 = document.getElementById("box2"); var box3 = document.getElementById("box3"); var link = document.getElementById("link"); /** * 为以上四个绑定单击响应函数 * - link绑定三个单击响应函数 * - box1、box2、box3分别绑定一个单击响应函数 */ // 为link绑定单击响应函数,单击id为link的a元素,依次d出a1、a2、a3三个窗口 // link的第一个单击响应事件 if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(){ alert("a1"); }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(){ alert("a3"); }) } // link的第二个单击响应事件:***event.preventDefault()取消默认行为*** if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(event){ alert("a2"); event.preventDefault(); }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(event){ alert("a2"); event.returnValue=false; }) } // link的第三个单击响应事件 if (link.addEventListener){ // 一般浏览器 link.addEventListener("click", function(){ alert("a3"); }) } else{ // IE8及以下浏览器 link.attachEvent("onclick", function(){ alert("a1"); }) } // 为box1、box2、box3分别绑定单击响应函数 function boxFun(obj, content){ obj.onclick = function(){ alert(content); } } boxFun(box3, "我是box3的单击响应函数"); boxFun(box2, "我是box2的单击响应函数"); boxFun(box1, "我是box1的单击响应函数") } script> head> <body> <div id="box1"> box1 <div id="box2"> box2 <div id="box3"> box3 <br> <a href="http://www.baidu.com" target="_blank" id="link">百度超链接a> div> div> div> body> html>
-
方式二:在事件的响应函数中
return false
,这种方法只适用于通过属性注册的处理程序,也就是说只适用于第一种和第二种事件绑定方式,第三种方法不适用。其他未特别说明,则表示都适用。DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Documenttitle> <script> window.onload = function(){ // 分别获取box1/box2/box3/link var box1 = document.getElementById("box1"); var box2 = document.getElementById("box2"); var box3 = document.getElementById("box3"); var link = document.getElementById("link"); /** * 为以上四个绑定单击响应函数 * - link、box1、box2、box3分别绑定一个单击响应函数 */ // 为link绑定单击响应函数:****使用return false取消默认行为**** // 本代码直接在html中绑定了onclik事件,如果想验证此处效果,可删除html代码中的onclick,这里取消注释 // link.onclick = function(event){ // alert("a"); // return false; // } // 为box1、box2、box3分别绑定单击响应函数 function boxFun(obj, content){ obj.onclick = function(){ alert(content); } } boxFun(box3, "我是box3的单击响应函数"); boxFun(box2, "我是box2的单击响应函数"); boxFun(box1, "我是box1的单击响应函数") } script> head> <body> <div id="box1"> box1 <div id="box2"> box2 <div id="box3"> box3 <br> <a href="http://www.baidu.com" target="_blank" id="link" onclick="alert('a'); return false;">百度超链接a> div> div> div> body> html>
- 方法一:在事件响应函数中使用
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)