七、事件处理函数、冒泡捕获、阻止冒泡默认事件

七、事件处理函数、冒泡捕获、阻止冒泡默认事件

一、事件处理函数

事件是元素天生就具备的能力,比如可以被点击。

绑定事件——>绑定事件的处理函数。

事件 + 事件的反馈 == 前端交互,交互体验

1.1 如何绑定事件(绑定事件处理函数)

1.1.1 elem.onxxx = function(){} 句柄绑定形式

兼容性好;

一个元素同一种事件只能绑定一个事件处理函数,后写覆盖先写。

位置:

  • js中,一个元素同一种事件只能绑定一个事件处理函数,后写覆盖先写。
  • 内联事件监听器(行内事件监听器),每一个元素都有onxxx属性,属性值可以写代码或者函数。不推荐使用:结构和逻辑应该分开。
<button onclick="console.log(1)">button>
<button onclick="test">button>
<script>
	function test(){
        
    }
script>

注:若同时使用这两种句柄的形式绑定事件,js中的会覆盖行内写的。

1.1.2 elem.addEventListener(事件类型,事件处理函数,false)

注册(绑定)事件监听器。

W3C规范,IE9以下不兼容;

一个元素同一种事件可以绑定多个事件处理函数;

函数引用相同,事件处理函数只执行一次;

一个元素同一个事件绑定多个事件处理函数会有两种结果:

  • 事件处理函数为匿名函数:使用不同的函数引用,属于不同的事件处理函数,都会执行
  • 外部的普通函数:相同的函数引用,属于相同的事件处理函数,相当于一个元素的一个事件绑定了一个事件处理函数,只执行一次。
var oBtn = document.getElementsByTagName('button')[0];
// 注册事件监听器,事件处理函数为匿名函数
// 绑定的多个事件处理函数都会执行
oBtn.addEventListener('click',function(){
    console.log(1);
}, false);
oBtn.addEventListener('click',function(){
    console.log(1);
}, false);

// 注册事件监听器,事件处理函数为外部普通函数
// 绑定的多个事件处理函数只执行一次,相当于只绑定了一个事件处理函数
oBtn.addEventListener('click',test,false);
oBtn.addEventListener('click',test,false);
function test(){
    console.log(2);
}

七、事件处理函数、冒泡捕获、阻止冒泡默认事件_第1张图片

1.1.3 elem.attachEvent(事件类型、事件处理函数)

IE8及以下绑定方法

IE8及以下(谷歌中没有);

一个元素同一种事件可以绑定多个事件处理函数;

不看函数引用是否相同,事件处理函数全部执行;

其中this指向window,使用call/apply方法改变this指向。

var oBtn = document.getElementsByTagName('button')[0];
// IE8以下绑定方法(谷歌中没有)
oBtn.attachEvent('onclick',function(){
    console.log(1);
});
oBtn.attachEvent('onclick',function(){
    console.log(1);
});

oBtn.attachEvent('onclick',test);
oBtn.attachEvent('onclick',test);
function test(){
    console.log(2);
}

七、事件处理函数、冒泡捕获、阻止冒泡默认事件_第2张图片

1.2 绑定事件函数的兼容性写法

// 事件绑定函数的兼容性写法:
// 参数依次代表 事件源,事件类型,事件处理函数
function addEvent(el, type, fn){
    // 先寻找注册监听器 IE9及以上兼容
    if(el.addEventListener){
        el.addEventListener(type, fn, false);
    }else if(el.attachEvent){
        // 再兼容IE8及以下
        el.attachEvent('on' + type, fn);
    }else{
        // 还不行的话使用兼容最好的句柄形式
        el['on'+ type] = fn;
    }
}

验证通过!

1.3 笔试题

点击列表,输出列表的序号。

var oLi = document.getElementsByTagName('li'),
    len = oLi.length;
for(var i = 0; i < len; i++){
    // 产生了闭包现象
    // 使用如下方法,点击时的i已经变成5,每次点击都是5
    // oLi[i].onclick = function(){
    //   console.log(i);
    // }

    // 怎么解决这个问题 IIFE+闭包
    (function(j){
        oLi[j].addEventListener('click',function(){
            console.log(j);
        },false);
    })(i)
}

浅了解一下使用IIFE的目的:

这里使用IIFE的目的在于使内部的函数变成一个闭包函数,可以访问外界的作用域;每次循环时,都可以获得一个新的立即执行函数,新的立即执行函数会拿到新的参数,闭包函数就拿到了新的参数j,每次循环以后j值就不一样了。

1.4 解除事件处理函数

  1. element.onclick = null/false

    • element.removeEventListener(事件名,函数名,xx),函数为外部函数。

    • element.removeEventListener(事件名,arguments.callee,xx)非严格模式下使用arguments.callee拿到此函数的引用。

      //绑定事件是什么样子,解除就什么样
      element.addEventListener('click'、arguments.callee,false);
      element.removeEventListener('click'、arguments.callee,false);
      
  2. element.detachEvent(事件名,事件处理函数)

1.4.1 解除事件函数的兼容性写法(与绑定事件对应)

// 事件解除函数的兼容性写法
// 参数依次为:事件源,事件类型,事件处理函数
function removeEvent(el, type, fn){
    if(el.addEventListener){
        el.removeEventListener(type, fn);
    }else if(el.attachEvent){
        el.detachEvent('on' + type, fn);
    }else{
        el['on' + type] = null;
    }
}

注意:一般执行一次或多次后再解除事件处理函数,那么解除事件就应该写到事件绑定内部,或者根据一些条件成立后再解除事件。

// 事件绑定测试
addEvent(oBtn, 'click', function(){
    test();
    
    //...一些条件
    
    // 事件解除测试
    removeEvent(oBtn, 'click', arguments.callee);
});

二、冒泡捕获

标签是内联元素(行内元素inline),即使display变成块级元素,DOM结构解析出来也是非嵌套的。

原则上内联元素不应该嵌套内联元素

<style>
    /* 属性选择器 */
    [href="https://www.taobao.com"]{
      display: block;
      width: 100px;
      height: 100px;
      background-color: skyblue;
    }
style>
<a href="https://www.taobao.com">淘宝
    <a href="https://www.baidu.com">百度a>
    <p>11
    <p>22p>
    p>
a>

七、事件处理函数、冒泡捕获、阻止冒泡默认事件_第3张图片

2.1 事件冒泡

addEventListener(type, fn, false)这种形式是事件冒泡。

DOM结构中存在嵌套关系,有了从内层到外层的事件冒泡现象:先执行事件源元素的事件处理函数,再执行父元素的同一种事件所触发的事件处理函数,直到顶层。(子元素的事件会从子元素起向父元素冒泡,从而触发父元素与子元素传递的同一事件,直到顶层)

2.2 事件捕获

addEventListener(type, fn, true)这种形式是事件捕获。

DOM结构中存在嵌套关系,有了从外层到内层的事件捕获现象:先执行自身所在的顶层节点的相同事件,然后向下传递到自身

2.3 DOM事件流

七、事件处理函数、冒泡捕获、阻止冒泡默认事件_第4张图片

DOM事件流分为三个阶段:

  1. 捕获阶段
  2. 当前目标阶段
  3. 冒泡阶段

当一个元素又绑定了一个捕获事件,又绑定了一个冒泡事件,执行:由哪个元素触发事件,它就属于事件源,在DOM事件流中属于当前目标阶段。没有捕获和冒泡,就是单纯执行一个事件函数或多个事件函数,多个事件处理函数的具体执行顺序在不同浏览器,不同版本浏览器中不同;而其他元素的事件按照先捕获后冒泡执行

简单来说:当一个元素又绑定了一个捕获事件,又绑定了一个冒泡事件,执行:事件源的事件处理函数正常执行(起一个传递事件的作用),其他元素的事件先捕获后冒泡。

2.4 其他

有些事件没有捕获和冒泡现象:

focus blur change submit reset select

IE浏览器没有事件捕获

三、阻止冒泡默认事件(IE8中事件对象不在事件处理函数中,而在window上)

3.1 阻止冒泡事件

不想父级也执行同一个事件所引发的事件处理函数,所以要阻止冒泡默认事件。

W3C—>e.stopPropagation()存在于Event.prototype上

IE——>e.cancelBubble = true

这里的e为事件对象,每个事件都有一个e在事件处理函数中。

阻止冒泡现象的兼容性写法:

// 消除事件默认冒泡现象,需要事件对象
// 而 IE8 中事件对象不在事件处理函数中,而在window上
// 需要使用兼容性写法
function cancelBubble(e){
    var e = e || window.event;
    // 首选W3C规则
    if(e.stopPropagation){
        e.stopPropagation()
    }else{
        e.cancelBubble = true;
    }
}

使用位置:这个函数写到子元素的事件绑定函数中。

3.2 阻止默认事件

3.2.1 阻止右击浏览器默认出现菜单栏,兼容性写法

// 取消 右击浏览器默认出现菜单
document.oncontextmenu = function(e){
    var e = e || window.event;
    // 句柄形式中取消默认事件方法:
    // 兼容性最好,addEventListener中不能用这个
    // return false;
    cancelPrevent(e);
}
// 取消默认事件兼容性写法
function cancelPrevent(e){
    var e = e || window.event;
    // w3c方法;一般 W3C 标准,IE9及以下都不支持
    if(e.preventDefault){
        e.preventDefault();
    }else{
        // IE9以下方法
        e.returnValue = false;
    }
}

3.2.2 阻止a链接跳转的方法

<body>
  <a href="https://www.baidu.com" target="_blank">百度a>
  
  <a href="javascript:;">淘宝a>
  
  <a href="javascript:void(0);">京东a>
  
  <script>
    var a = document.getElementsByTagName('a')[0];
    // 方法3:
    a.onclick = function(e){
      var e = e || window.event;
      // 点击百度不会跳转
      e.preventDefault();
    }
  script>
body>

3.2.3 点击文字不跳转,点击文字外部跳转

<body>
  <a href="https://www.baidu.com" class="link" target="_blank">
    <div class="inner">点击div>
  a>
  <script>
    // 要求:点击文字不跳转,点击文字外部跳转
    var inner = document.getElementsByClassName('inner')[0];
    // 在div中清除 a 标签默认跳转事件
    inner.addEventListener('click',function(e){
      var e = e || window.event;
      e.preventDefault();
    }, false)
  script>
body>

3.2.4 阻止submit默认事件

// submit默认事件:刷新页面去提交数据(同步跳转提交)
// 通过id获取元素
var submit = document.getElementById('submit');

submit.onclick = function(e){
    var e = e || window.event;
	// 这里没有用兼容性写法
    e.preventDefault();
    console.log('提交了');
} 

DOM中的兼容性写法更多是一个知识储备和面试相关,所以重点肯定在于DOM原型链;DOM增删改查;所有事件,包括键盘;冒泡捕获,事件代理

在DOM中的兼容性写法大部分针对IE浏览器各版本,firefox有些也是会考虑的;但是现在大部分项目不会考虑,因为工程化可以对兼容性进行配置。

而chrome浏览器是迭代的,99%API都兼容。

你可能感兴趣的:(DOM基础学习笔记,javascript,dom,点击事件)