事件

事件


事件流冒泡捕获事件委托

一.事件概念:js与html的交互是通过事件完成的,即文档或浏览器窗口发生特定交互的瞬间。

二.事件流

1.起源:描述的是从页面接收事件的顺序,IE为冒泡流,netspace为捕获流。

2.捕获:在DOM2.0里,从外向内执行所有有事件的元素,会先执行最外面(父元素),然后依次向内执行直到执行的那个元素,这就是捕获过程。在这个过程中,事件相应的监听函数是不会被触发的。当某个元素触发事件,顶层对象(document)就会发起一个事件流,沿着DOM节点向着真正事件发生的元素流去,直到捕获它。例如:假设页面只有一个div元素,在该元素上点击,捕获顺序document->html->body->div。

注:i>实际在ie9、sarify、chrome、firefox和opera9.5+都会在捕获阶段触发事件。ii>ie9、sarify、chrome、firefox和opera都市从window对象开始捕捉事件的。

目标:到达事件目标后,若它上面绑定了监听函数,就执行相应函数。若未绑定,则不执行。

冒泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被触发。(事件冒泡是默认存在的),例如:div->body->html->document.

3.事件流顺序:捕获->目标->冒泡。

5

三.事件的绑定与解除

1.在JavaScript中,有三种常用的绑定事件的方法:

  • 在DOM元素中直接绑定;
<btn onclick="show()">点我</button> 

我们可以在DOM元素上绑定onclick、onmouseover、onmouseout、onmousedown、onmouseup、ondblclick、onkeydown、onkeypress、onkeyup等。(详见W3C)

  • 在JavaScript代码中绑定;
document.getElementById("btn").onclick=show();

在JavaScript代码中(即script标签内)绑定事件可以使JavaScript代码与HTML标签分离,文档结构清晰,便于管理和开发。但是只能实现一个函数的绑定。

  • DOM2 绑定事件监听函数

ie下:DOM对象.attachEvent(事件名称(字符串),事件函数);
标准:DOM对象.addEventListener(事件名称,事件函数,usecapture) //默认是false,false:冒泡 true:捕获

浏览器事件是否捕获事件名执行顺序this
IEattachEvent有on倒序Window
火狐addEventListener没有on顺序执行该事件的对象

注:通过attach添加的事件处理程序,都会被添加到冒泡阶段

2.事件解除

(1)解除绑定

document.onclick = null; //通用
ie:obj.detachEvent(事件名称,事件函数);//ie<=8
标准:obj.removeEventListener(事件名称,事件函数,是否捕获);

(2)通过addEL添加的事件处理程序只能使用removeEL来删除,通过attachE添加的事件只能通过detach来删除,他俩的条件是必须提供相同的参数,也就是说,添加的匿名事件将不能被移除。

3.(跨浏览器)绑定与解除

//封装绑定监听事件
function addEventHandler(target,type,fn){
  if(target.addEventListener){
    target.addEventListener(type,fn);
  }else{
    target.attachEvent("on"+type,fn);
  }
}
//封装移除监听事件
function removeEventHandler(target,type,fn){
  if(target.removeEventListener){
    target.removeEventListener(type,fn);
  }else{
    target.detachEvent("on"+type,fn);
  }
}

四。事件对象

(1)DOM中的事件对象

可用属性/方法

cancelbubble 是否可以取消事件的默认行为
currentTarget当前在处理的那个元素
preventDefault 阻止默认事件
detail 事件相关的细节
stopPropagation 取消事件的进一步冒泡或捕获
target 事件的目标
type 被触发的事件类型

(2)IE中事件对象

可用属性/方法

cancelbulle
returnValue (false)阻止默认事件
srcElement 事件的目标
type

注:阻止默认事件

addEventListener //DOM:因为return false只能阻止on事件,故用ev.preventDefault()阻止

attachEvent //ie:用return false

(3)(跨浏览器)阻止默认事件

function fn(ev){
  var ev=ev||event;
  if(ev.preventDefault){
    ev.preventDefault();
  }else{
    ev.returnValue=false;
  }
}

五。事件类型

(1)UI事件–交互时发生

load:加载完触发
reseize:窗口或框架大小变化时,在window或框架上面触发。
scroll:页面滚动时触发

(2)焦点事件

blue:失去焦点时触发
focus:获得焦点时触发

(3)鼠标与滚轮事件

鼠标事件:

mousedown:鼠标的键钮被按下。
mouseup:鼠标的键钮被释放弹起。
click:单击鼠标的键钮。
dblclick:鼠标的键钮被按下。
contextmenu :弹出右键菜单。
mouseover:鼠标移到目标的上方。
mouseout:鼠标移出目标的上方。
mousemove:鼠标在目标的上方移动。

mouseover/out支持冒泡,mouseteener/leave不支持冒泡
(1)不论鼠标指针,穿过被选元素或其子元素,都会触发out/over.
(2)只有在鼠标穿过被选元素时,才会触发teener/leave.


ev.clientX //鼠标指针在客户区的横坐标
ev.pageX //事件在页面的横坐标
ev.screenX //鼠标指针相对于屏幕的横坐标

滚轮事件

类型火狐IE/chrome
绑定事件DOMMouseScroll (addEL)onmousewheel
判定方向ev.detail (上负,下正)上正下负

步骤:

1判断浏览器
2选择不同滚轮事件
3根据返回值正负,判断方向

代码封装:

function dowheel( obj, fnUp, fnDown ){
    if(navigator.userAgent.indexOf("Firefox")>=0){
    obj.addEventListener("DOMMouseScroll",function(ev){
        var ev=ev||event;
        if(updown(ev)){
            fnUp();
        }else{
            //alert("下");
            fnDown();
        }
        ev.preventDefault();
    });
    }else{
        $("div1").onmousewheel=function(ev){
            var ev=ev||event;
            if(updown(ev)){
                fnUp();
            }else{
                fnDown();
            }
            return false;
        };
    }

    function updown(ev){
        var b = true;
        if(ev.wheelDelta){b=ev.wheelDelta>0?true:false;}
        else{b= ev.detail<0?true:false;}
        return b;
    }
}
//dowheel($("div1"),function fnUp(){alert("上");}, function fnDown(){alert("下");});

(4)键盘与文本事件

keydown:用户按下任意键触发,若连按住不放,则会重复触发此事件。
keypress:用户按下任意字符键触发,若连按住不放,则会重复触发此事件。
keyup:用户释放按键时触发

(5)设备事件

orientationchange事件:通过window.orientationchange获取,共有三个属性值。0:肖像模式(旋转0度),90:左旋转90°的横屏模式,-90:右旋转90°的横屏模式。

(6)触摸与手势事件

touchstart//当手指触摸屏幕时触发
touchmove//手指在屏幕滑动时,连续触发
touched//手指从屏幕移开时触发
touchcancel//系统跟踪时触发

(7)HTML5事件

contextmenu事件:该事件属于鼠标事件,故包含与位置有关的所有属性;该事件冒泡,所以可以为docuemnt指定一个事件处理程序,用以处理页面所有此类事件。注意要清除默认样式,ie用return false;兼容DOM浏览器中,使用ev.preventDefault()。通常利用该事件来显示自定义右键菜单。

六。内存与性能

(一)事件委托

冒泡例子(eg:在三层嵌套div中,从当下事件元素冒泡到父元素,分别弹出123)

HTML:

<div id="div1">
    <div id="div2">
        <div id="div3"></div>
    </div>
</div>

js:

   var aDiv = document.getElementsByTagName("div");
    for(var i=0;i<aDiv.length;i++){
        aDiv[i].onclick = function(){
            alert(this.id);
        }
    }

分析:虽然实现了从事件目标元素到父元素的冒泡,但是假想,若父元素里有大量子元素,难道要给每一个子元素添加事件函数吗?不然,这里引入了事件委托。

1.什么是事件委托?

有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。

这里其实还有2层意思的:

第一,现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;

第二,新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的。

2.优点:

  • 管理的函数变少了。不需要为每个元素都添加监听函数。对于同一个父节点下面类似的子元素,可以通过委托给父元素的监听函数来处理事件。

  • 可以方便地动态添加和修改元素,不需要因为元素的改动而修改事件绑定。

  • JavaScript和DOM节点之间的关联变少了,这样也就减少了因循环引用而带来的内存泄漏发生的概率。

3.适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。(所有用到按钮的事件,多数的鼠标事件和键盘事件)

值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。

不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

4.如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办?比如说只有点击li才会触发?

Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom。

标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,

通过操控target,可以只执行一次DOM操作。

5.现在给一个场景 ul > li > div>p,要求在ul该层点击无任何变化,若在li内点击,则让被点击的那一层变红,如何实现?(利用冒泡)

思路:核心代码是while循环部分,实际上就是一个递归调用,也可以写成一个函数,用递归的方法来调用,同时用到冒泡的原理,从里往外冒泡,知道currentTarget为止,当当前的target是li的时候,就可以执行对应的事件了,然后终止循环

var oLi=document.getElementsByTagName("li")[0];
window.onload=function() {
    oLi.onclick = function (ev) {
        var ev = ev || window.event;
        var target = ev.target || ev.srcElement;
        var oPa = target;//获取目标元素
        while (oPa != oLi) {
            oPa = oPa.parentNode;
            //向上冒泡到li
            if (oPa == document) {
                break;
            }
        }
        //判断属于li内元素
        if (oPa == oLi) {
            target.style.background = "red";
        }
    }
};

(二)移除事件处理程序

例子:设要求点击时,div出现对应内容

<div id="box"></div>
<input type="button" value="click me" id="btn">
<script>
  var oBtn=document.getElementById("btn");
  oBtn.onclick=function () {
      document.getElementById("box").innerHTML="麻烦了...";
  }
</script>

当内容展示后,按钮不再需要,此时若直接移除按钮,则按钮对应的事件处理程序极有可能无法被垃圾回收,即事件处理程序与按钮依旧保持着引用关系。当这种引用越多,页面执行就越慢,影响内存与性能。

改进1:移除事件处理程序,即断离引用

<div id="box"></div>
<input type="button" value="click me" id="btn">
<script>
    var oBtn=document.getElementById("btn");
    oBtn.onclick=function () {
        oBtn.onclick=null;
        document.getElementById("box").innerHTML="...";
    }
</script>

改进2:利用事件委托,通过把事件处理程序给较高层次的元素,可以减少连接数量。


为了内存与性能考虑,使用事件时需要注意:

(1)限制页面中事件处理程序的数量,数量太多会占用内存,页面不灵敏。
(2)使用事件委托,减少事件处理程序的数量
(3)建议在浏览器卸载页面前,移除该页面的所有事件处理程序。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值