事件处理程序

事件处理程序就是响应某个事件的函数,简单地来说,就是函数。我们又把事件处理程序称为事件侦听器。事件处理程序是以“on“开头的,比如点击事件的处理程序是”onclick“,事件处理程序大概由以下5种。

  •  1 HTML事件处理程序
  •  2 DOM0级事件处理程序
  •  3 DOM2级事件处理程序
  •  4 IE事件处理程序
  •  5.跨浏览器的事件处理程序

一、HTML事件处理程序

       HTML事件处理程序,它是写在html里面的,是全局作用域:

例子代码--点击事件触发alert函数
<button οnclick="alert('hello')"></button>

       当我们需要使用一个复杂的函数时,将js代码写在这里面,显然很不合适,所以有了下面这种写法:

例子代码--点击事件触发doSomething()函数,这个函数写在单独的js文件或<script>之中。
<button οnclick="doSomething()"></button>

这样会出现一个时差问题,当用户在HTML元素出现一开始就进行点击,有可能js还没有加载好,这时候就会报错。但我们可以将函数封装在try-catch块中来处理:

<button οnclick="try{doSomething();}catch(err){}"></button>

同时,一个函数的改变,同时可能会涉及html和js的修改,这样是很不方便的,综上,我们有了DOM0级事件处理程序。

二、DOM0级事件处理程序

之所以有DOM0级事件处理程序,和我们之前提到的IE以及Netscape对应事件传播方向不同处理而产生的事件处理程序。

<button id="btn">点击</button>
 
<script>
  var btn=document.getElementById("btn");
  btn.onclick=function(){
    alert("hello");
  }
</script>

可以看到button.onclick这种形式,这里事件程序作为了btn对象的方法,是局部作用域。

所以我们可以用

btn.onclick = null;来删除指定的事件处理程序。

如果我们尝试给事件添加两个事件,如:

<button id="btn">点击</button>
 
<script>
  var btn=document.getElementById("btn");
  btn.onclick=function(){
    alert("hello");
  }
  btn.onclick=function(){
    alert("hello again");
  }
</script>

输出,hello again,很明显,第一个事件函数被第二个事件函数给覆盖掉了,所以,DOM0级事件处理程序不能添加多个,也不能控制事件流到底是捕获还是冒泡。

三、DOM2级事件处理程序(不支持IE)

进一步规范之后,有了DOM2级事件处理程序,其中定义了两个方法:

addEventListener()  添加事件侦听器

removeEventListener() 删除事件侦听器

函数具有三个参数:

第一个参数是要处理的事件名(不带on前缀的才是事件名)

第二个参数是作为事件处理程序的函数

第三个参数是一个boolean值,默认false表示使用冒泡机制,true表示捕获机制。

<button id="btn">点击</button>
 
<script>
  var btn=document.getElementById("btn");
  btn.addEventListener('click',hello,false);
  btn.addEventListener('click',helloagain,false);
  function hello(){
    alert("hello");
  }
  function helloagain(){
    alert("hello again");
  }
</script>

这时候两个事件处理程序都能够成功触发,说明可以绑定多个事件处理程序,但是注意,如果是定义了一模一样时监听方法,是会发生覆盖的,即同样的事情和事件流机制下相同的方法只会触发一次。

removeEventLister()方法几乎和添加时用法一样:

<button id="btn">点击</button>
 
<script>
  var btn=document.getElementById("btn");
  btn.addEventListener('click',hello,false);
  btn.removeEventListener('click',hello,false);
  function hello(){
    alert("hello");
  }
</script>

这样的话,事件处理程序只会执行一次。

但是要注意,如果同一个监听事件分别为”事件捕获“和”事件冒泡“注册了一次,一共两次,者两次事件需要分别移除。两者不会互相干扰。

这时候的this指向该元素的引用。

这里事件触发的顺序时添加的顺序。

四、IE事件处理程序

对于Internet Explorer来说,在IE9之前,你必须使用attachEvent而不是使用标准方法addEventListener。

IE事件处理程序中有类似与DOM2级事件处理程序的两个方法:

1.attachEvent()
2.detachEvent()

它们都接收两个参数:

1.事件处理程序名称。如onclick、onmouseover,注意:这里不是事件,而是事件处理程序名称,所以有on。

2.事件处理程序函数。

之所以没有和DOM2级事件处理程序中类似的第三个参数,是因为IE8及更早版本只支持冒泡事件流

<button id="btn">点击</button>
 
<script>
  var btn=document.getElementById("btn");
  btn.attachEvent('onclick',hello);
  btn.detachEvent('onclick',hello);
  function hello(){
    alert("hello");
  }
</script>

这里事件触发的顺序不是添加的顺序而是添加顺序的相反顺序。

使用attachEvent方法有个缺点,this的只会变成window对象的引用而不是触发事件的元素。

五、跨浏览器的事件处理程序

为了兼容IE浏览器和标准的浏览器,我们需要编写通用的方法来处理:
var EventUtil = {
    addHandler: function (element, type, handler) {
        if (element.addEventListener) {
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + type, handler);
        } else {
            element["on" + type] = handler;
        }
    },
    removeHandler: function (element, type, handler) {
        if (element.removeEventListener()) {
            element.removeEventListener(type, handler, false);
        } else if (element.detachEvent) {
            element.detachEvent("on" + type, handler);
        } else {
            element["on" + type] = null;
        }
    }
};

这一部分需要创建两个方法:

addHandle()--这个方法职责是视情况来使用DOM0级、DOM2级、IE事件处理程序来添加事件。

removeHandler()--这个方法就是移除使用addHandlerT添加的事件。

这两个方法接收相同的三个参数:

1. 要操作的元素--通过dom方法获取

2.事件名称--注意:没有on,如”click“、”mouseover“

3.事件处理程序函数--对应的函数

使用:

<button id="btn">点击</button>
 
<script>
  var btn=document.getElementById("btn");
  EventUtil.addHandler(btn,'click',hello);
  EventUtil.removeHandler(btn,'click',hello);
  function hello(){
    alert("hello");
  }
</script>

事件对象

事件对象是用来记录一些事件发生时的相关信息的对象,但事件对象只有发生时才会产生,并不只能是事件处理函数内部访问,在所有事件处理函数运行结束后,事件对象就被销毁。

 

 一、属性

<button id="btn">点击</button>
 
<script>
        var btn=document.getElementById("btn");
        btn.ddEventListener('click', doCurrent, true);
        // 判断事件的属性
        function doCurrent(event) {
            //获取当前事件触发的div
            var target = event.currentTarget;

            //通过判断事件的event.eventPhase属性返回事件传播的当前阶段
            //1:捕获阶段、2:正常事件派发和3:起泡阶段。
            //得到当前阶段和id值并输出
            var msg = (event.eventPhase == 1 ? '捕获阶段:' : '冒泡阶段:')+ target.attributes["id"].value;;
            console.log(msg);
        }
</script>

二、方法

Event对象主要有以下两个方法,用于处理事件的传播(冒泡、捕获)和事件的取消。

stopPropagation()----冒泡机制下,阻止事件的进一步往上冒泡

<body>
   <div id="btn1">btn1
       <div id="content">content</div>
   </div>
   <script>

    var btn1=document.getElementById("btn1");
    var content=document.getElementById("content");
    btn1.addEventListener("click",function(){
        console.log("btn1");
        
    },false);
    content.addEventListener("click",function(event){
        console.log("content");
        event.stopPropagation();
    },false);

   </script>
    
</body>
//这里会输出content,阻止了向content的冒泡

preventDefault()-----用于取消事件的默认操作,比如链接的跳转或者表单的提交,主要是用来阻止标签的默认行为

<a id="go" href="https://www.baidu.com/">禁止跳转</a>
var go = document.getElementById('go');
function goFn(event) {
 event.preventDefault();
// 不会跳转
}
go.addEventListener('click', goFn, false);

三、兼容性

     当然,事件对象也存在一定的兼容性问题,在IE8及以前版本之中,通过设置属性注册事件处理程序时,调用的时候并未传递事件对象,需要通过全局对象window.event来获取。解决方法如下:

function getEvent(event) {
 event = event || window.event;
}

在IE浏览器上面是event事件是没有preventDefault()这个属性的,所以在IE上,我们需要设置的属性是returnValue

window.event.returnValue=false

stopPropagation()也是,所以需要设置cancelBubble,cancelBubble是IE事件对象的一个属性,设置这个属性为true能阻止事件进一步传播。

event.cancelBubble=true

事件委托

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类的所有事件。

    <button id="btnAdd">添加</button>
    <ul id="ulList">
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <script>
        var btnAdd = document.getElementById('btnAdd');
        var ulList = document.getElementById('ulList');
        var list = document.getElementsByTagName('li');
        var num = 3;
        btnAdd.onclick = function () {
            num++;
            var li = document.createElement('li');
            li.innerHTML = num;
            ulList.appendChild(li)
        }
        for (i = 0; i < list.length; i++) {
list[i].onclick
= function(){ alert(this.innerHTML); } } </script>

 

结果是:新更新的HTML节点或者新添加HTML节点时,新添加的节点无法绑定事件,更新的节点也无法绑定事件,表现的行为是无法触发事件。

其中一种解决方法是,添加子节点的时候,再次为其添加监听事件

 

这也是问题所在:
 1.首先我们多次操作DOM获取元素,这样势必会降低浏览器处理性能
 2.事件不具有继承性,如果我们动态在页面中添加了一个元素,那么还需要重新走一遍上述程序为其添加监听事件

那么有没有更好的方法呢?根据事件的冒泡原理,我们还可以实现另外一个很重要的功能:事件委托

我们只监听最外层的元素,然后在事件函数中根据事件来源进行不同的事件处理。这样,我们添加事件监听时只需要操作一个元素,极大的降低了DOM访问,并且不用再给新增的元素添加监听事件了,因为元素的事件会冒泡到最外层,被我们截获。

得到

    <button id="btnAdd">添加</button>
    <ul id="ulList">
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <script>
        var btnAdd = document.getElementById('btnAdd');
        var ulList = document.getElementById('ulList');
        var num = 3;

        ulList.onclick = function(event){
            var event = event || window.event;
            var target = event.target || event.srcElement;
            if(target.nodeName.toLowerCase() == 'li'){
                alert(target.innerHTML);
            }
        }

        btnAdd.onclick = function () {
            num++;
            var li = document.createElement('li');
            li.innerHTML = num;
            ulList.appendChild(li);
            doclick();
        }
    </script>

这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会被触发,当然,这里当点击ul的时候也会触发的,所以要判断点击的事件是不是li标签元素。

Event对象提供了一个属性叫target,可以返回事件的目标节点,

这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的,所以要判断点击的对象到底是不是li标签元素。

Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,我们需要转成小写再做比较(习惯问题)。

这样,我们就实现了我们的事件委托,当然,不是所有的事件都是可以委托的。
适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。

当用事件委托的时候,根本就不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,新增加的节点也可以触发事件效果。

参考:https://segmentfault.com/a/1190000013265547#articleHeader6

转载于:https://www.cnblogs.com/psxiao/p/11537778.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值