资讯专栏INFORMATION COLUMN

javascript事件基础知识

ad6623 / 2147人阅读

摘要:可选,布尔值,指定事件是否在捕获或冒泡阶段执行,默认冒泡。适用范围参数介绍必须,字符串,事件名称。必须,指定事件触发时执行的函数。事件冒泡事件冒泡与事件捕获恰恰相反,事件冒泡顺序是由内到外进行事件传播,直到根节点。

什么是事件

javascript与HTML之间交互就是通过事件实现的,事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。事件在浏览器中是以对象的形式存在的,即event,触发一个事件,就会产生一个事件对象event,该对象包含着所有与事件有关的信息,包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。

DOM元素添加事件 1.DOM元素事件属性
2.javascript脚本添加事件

a. element.onclick=function(e){}
b. element.addEventListener(event,function(e),useCapture)

适用范围:现代浏览器(IE9+, Firefox , chorme, safari, opera)

参数介绍:event必须,事件名称。(注:不含“on”,比如“click”、“mouseover”等)。function必须,指定事件触发时执行的函数。useCapture可选,布尔值,指定事件是否在捕获或冒泡阶段执行,默认false(冒泡)。

c. element.attachEvent(event,function(e))

适用范围:IE 6、7、8

参数介绍:event 必须,字符串,事件名称。(注:含“on”,比如“onclick”、“onmouseover”等)。function必须,指定事件触发时执行的函数。


扩展:原生Javascript写法,跨浏览器添加事件

 var EventUtil = {
     // 添加事件监听
      add: function(element, type, callback){
          if(element.addEventListener){
             element.addEventListener(type, callback, false);
          } else if(element.attachEvent){
              element.attachEvent("on" + type, callback);
         } else {
             element["on" + type] = callback;
         }
     }
     // 移除事件监听
    remove: function(element, type, callback){
         if(element.removeEventListener){
             element.removeEventListener(type, callback, false);
         } else if(element.detachEvent){
             element.detachEvent("on" + type, callback);
         } else {
             element["on" + type] = null;
         }
     }
 }
 
 

事件冒泡

事件冒泡(dubbed bubbling):与事件捕获恰恰相反,事件冒泡顺序是由内到外进行事件传播,直到根节点。

注:无论是事件捕获还是事件冒泡,它们都有一个共同的行为,就是事件传播

阻止事件捕获/事件冒泡
 event.stopPropagation()   
 event.stopImmediatePropagation()

event.stopPropagation()
会阻止事件继续分发到其他document节点,但是当前节点绑定的多个事件会继续按注册的顺序执行

event.stopImmediatePropagation()
不仅阻止事件继续分发到其他document,还会将事件分发就地停止,在当前事件之后注册的其他事件,都不会执行

举个栗子:此栗子是阻止事件捕获,阻止事件冒泡原理一样

//执行结果 alert("div1第一次执行"); alert("div1第二次执行");
//执行结果 alert("div1第一次执行");
事件代理 什么是事件代理

事件代理也可以称之为事件委托,事件代理就是在祖先级DOM元素绑定一个事件,当触发子孙级DOM元素的事件时,利用事件冒泡的原理来触发绑定在祖先级DOM的事件。

那这是什么意思呢?网上的各位大牛们讲事件代理基本上都用了同一个例子,就是取快递来解释这个现象,借花献佛,大家认真领会一下事件代理到底是一个什么原理:
有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工,前台MM也会在收到寄给新员工的快递后核实并代为签收。

这里其实有2层意思的:
第一,现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;
第二,新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的。

为什么要用事件代理

一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?

在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件代理,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;

每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了(内存不够用,是硬伤),比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能呵呵了,如果用事件代理,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,是不是省了很多,自然性能就会更好。

事件代理的实现

在介绍事件代理的方法之前,先来看一段一般方法的栗子:

  • 111
  • 222
  • 333
  • 444

上面的代码很简单,我们看看有多少次的dom操作,首先要找到ul,然后遍历li,然后点击li的时候,又要找一次目标的li的位置,才能执行最后的操作,每次点击都要找一次li。

用事件代理的方式来实现,上栗子:

window.onload = function(){
    var ulDelegate= document.getElementById("ulDelegate");
    ulDelegate.onclick = function(){
         alert("Delegate");
    }
}

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

那么问题就来了,如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办,比如说只有点击li才会触发,这时,Event对象就派上用场了,Event提供了一个属性叫target,可以返回事件的目标节点,我们称为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,我们可以转成小写再做比较,或者不转。

window.onload = function(){
  var ulDelegate= document.getElementById("ulDelegate");
  ulDelegate.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    if(target.nodeName.toLowerCase() == "li"){
       alert("Delegate");
      alert(target.innerHTML);
    }
  }
}

这样就只有点击li会触发事件了,且每次只执行一次dom操作,如果li数量很多的话,将大大减少dom的操作,优化的性能可想而知!

上面的栗子是说li操作的是同样的效果,如果每个li被点击的效果都不一样,上栗子:

  • 添加
  • 删除
  • 修改

再来最后一个事件代理的栗子,当我们新增一个li元素的时候,同样先看一下一般写法:

  • 111
  • 222
  • 333
  • 444

再来看一下事件代理的写法:

    window.onload = function(){
        var ulDelegate = document.getElementById("ulDelegate");
        var addLi = document.getElementById("addLi");
        
        ulDelegate.onclick = function(ev){
            var ev = ev || window.event;
            var target = ev.target || ev.srcElement;
            if(target.nodeName.toLowerCase() == "li"){
                alert("Delegate");
                alert(target.innerHTML);
            }
        };

        addLi.onclick = function(){
            var addLiElement = document.createElement("li");
            addLiElement .innerHTML = 10000*Math.random();
            ulDelegate.appendChild(addLiElement );
        }

    }

看,上面是用事件单例的方式,新添加的子元素是带有事件效果的,我们不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,其他的都是在js里面的执行,这样可以大大的减少dom操作,这才是事件代理的精髓所在。

总结

事件是将javascript与网页联系在一起的主要方式,在使用事件时,需要考虑如下一些内存与性能方面的问题。

有必要限制夜歌页面中事件处理程序的数量,数量太多会导致占用大量内存,而且也会让用户页面反应不够灵敏

建立在事件冒泡机制之上的时间委托技术,可以有效的减少时间处理程序的数量

建议在浏览器卸载页面之前移除页面中的所有事件处理程序

事件是javascript中最重要的主题之一,深入理解事件的工作机制以及它们对性能的影响是至关重要的

(总结摘自javascript高级程序设计第三版)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/109153.html

相关文章

  • 总结javascript基础概念(二):事件队列循环

    摘要:而事件循环是主线程中执行栈里的代码执行完毕之后,才开始执行的。由此产生的异步事件执行会作为任务队列挂在当前循环的末尾执行。在下,观察者基于监听事件的完成情况在下基于多线程创建。 主要问题: 1、JS引擎是单线程,如何完成事件循环的? 2、定时器函数为什么计时不准确? 3、回调与异步,有什么联系和不同? 4、ES6的事件循环有什么变化?Node中呢? 5、异步控制有什么难点?有什么解决方...

    zhkai 评论0 收藏0
  • 聊聊DOM中很重要的一个知识点:事件

    摘要:事件事件就是浏览器告知程序,用户的行为,用户点击了页面中的某个按钮或者用户输入用户名或密码等操作可以称为事件事件的类型事件根据使用的场景不同,可以分为如下几种依赖于设备的输入事件键盘事件和鼠标事件,这些事件都是直接和设备相关的独立于设备的输 事件 事件就是浏览器告知JavaScript程序,用户的行为,用户点击了HTML页面中的某个按钮或者用户输入用户名或密码等操作可以称为事件 事件的...

    church 评论0 收藏0
  • 【连载】前端个人文章整理-从基础到入门

    摘要:个人前端文章整理从最开始萌生写文章的想法,到着手开始写,再到现在已经一年的时间了,由于工作比较忙,更新缓慢,后面还是会继更新,现将已经写好的文章整理一个目录,方便更多的小伙伴去学习。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 个人前端文章整理 从最开始萌生写文章的想法,到着手...

    madthumb 评论0 收藏0
  • #yyds干货盘点# 前端基础知识面试集锦3

    摘要:当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体如何实现继承构造继承原型继承实例继承拷贝继承原型机制或和方法去实现较简单,建议使用构造函数与原型混合方式。它是基于的一个子集。 JavaScript介绍js的基本数据类型。Undefined、Null、Boolean、Number、Stri...

    番茄西红柿 评论0 收藏2637

发表评论

0条评论

最新活动
阅读需要支付1元查看
<