Javascript事件的深度解析与代码实战

Javascript事件的深度解析与代码实战,第1张

文章目录
  • 1. js代码书写位置&事件初识&文档加载
  • 2. 再会js中的事件
  • 3. 深入理解事件
    • 3.1 事件触发和事件对象
    • 3.2 事件的绑定(事件监控)
    • 3.3 事件的冒泡
    • 3.4 事件的委派
    • 3.5 事件的传播
    • 3.6 事件的取消
      • 3.6.1 取消事件传播
      • 3.6.2 取消默认行为

1. js代码书写位置&事件初识&文档加载
  • 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事件如下:(各种事件可以在后续学习中慢慢体会,下表仅是部分事件)

事件描述
onchangehtml元素改变
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. 深入理解事件
  • 关于事件,在前面章节我们已经有了初步的接触,事件指的就是用户与浏览器交互的一瞬间。我们通过为指定事件绑定回调函数的形式来处理事件,当指定事件触发以后,我们的回调函数就会被调用,这样我们的页面就可以完成和用户的交互了。
3.1 事件触发和事件对象
  • 事件触发:
    • 事件的发生(触发)主要是由用户 *** 作引起的。
    • 如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获取
    • clientXclientY用于获取onmousemove事件中鼠标在当前的可见窗口的坐标
    • pageXpageY用于获取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>
    

3.2 事件的绑定(事件监控)
  • 在第三章中,我们已经提到了事件绑定的三种方式:

    • 方式一:内联属性监听,也可以称为通过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:回调函数,当事件触发时调用该函数
    • 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>
    

3.3 事件的冒泡
  • 事件的冒泡(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为例,假如我们单击的是子元素,则返回子元素对象,假如我们单击的是子元素以外的父元素部分,则返回父元素对象。
    • 事件的委派本质是为祖先元素绑定事件,但最终效果是为子元素绑定了事件
    • 事件委派是利用了冒泡,当后代元素事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的相应函数来处理事件。
    • 通过委派可以减少事件绑定次数,提高了程序性能;还能为新增元素绑定事件,解决了上述问题。
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>

  • 在上例效果的解释中,其实包含了两大方面:一是事件传播;二是默认行为。因此事件取消也是从这两方面就行阐述。
3.6.1 取消事件传播
  • 我们已经知道事件传播包括三个阶段:捕获阶段、目标获取阶段、冒泡阶段。取消事件传播主要针对的是目标阶段和冒泡阶段:

    • 在目标阶段取消传播:指一个元素本身具有多个相同事件,在其中一个事件设置取消事件传播,该事件以后的事件都不会触发,该事件元素的祖先元素事件也不会触发。如上例标签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>
    

3.6.2 取消默认行为
  • 默认行为:具体指浏览器对当前事件的默认行为,比如上例,浏览器对当前超链接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>
      

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/887484.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-14
下一篇 2022-05-14

发表评论

登录后才能评论

评论列表(0条)

保存