事件总结1:事件流和事件处理程序

作者: 我不是大熊 | 来源:发表于2018-03-15 16:05 被阅读0次

    javascript和html之间的交互是通过事件实现的。事件就是指文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码。

    事件流

    事件流描述的是从页面中接收事件的顺序。IE和Netscape两个不同的事件流概念:IE中的事件流是事件冒泡流,Netscape中的事件流是事件捕获流。

    1.事件冒泡流

    事件冒泡:即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。

    <!DOCTYPE html>
        <html>
        <head>
            <title>Event Bubbling Example</title>
        </head>
        <body>
            <div id="myDiv">Click Me</div>
        </body>
        </html>
    

    如果单击页面中的<div>元素,那么这个 click 事件会按照如下顺序传播:①<div>,②<body>,③<html>,④document。就是从单击的元素沿 DOM 树向上传播,在每一级节点上都会发生,直至传播到document 对象。

    所有现代浏览器都支持事件冒泡,但在具体实现上还是有一些差别。IE5.5 及更早版本中的事件冒泡会跳过<html>元素(从<body>直接跳到 document)。IE9、Firefox、Chrome 和 Safari 则将事件一直冒泡到 window 对象

    2.事件捕获流

    Netscape的事件流思想是事件捕获流,即是指不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件,用意在于在事件到达预定目标之前捕获它。
    依然是前面的例子点击<div>元素就会以下列顺序触发 click 事件:①document,②<html>,③<body>,④<div>。虽然事件捕获是 Netscape唯一支持的事件流模型,但 IE9、Safari、Chrome、Opera 和 Firefox 目前也都支持这种事件流模型。尽管“DOM2 级事件”规范要求事件应该从 document 对象开始传播,但这些浏览器都是从 window 对象开始捕获事件的。

    由于老版本的浏览器不支持,所以常使用事件冒泡,在有特殊需要时再使用事件捕获。

    3.DOM事件流

    “DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。依然是前面的例子,单击<div>元素:触发事件的顺序:①Document,②<html>,③<body>,④<div>,⑤<body>,⑥<html>,⑦Document。

    在 DOM 事件流中,实际的目标(<div>元素)在捕获阶段不会接收到事件。这意味着在捕获阶段, 12 事件从 document 到<html>再到<body>后就停止了。下一个阶段是“处于目标”阶段,于是事件在<div> 上发生,并在事件处理中被看成冒泡阶段的一部分。然后,冒泡阶段发生, 事件又传播回文档。

    即使“DOM2 级事件”规范明确要求捕获阶段不会涉及事件目标,但 IE9、Safari、Chrome、Firefox 和 Opera 9.5 及更高版本都会在捕获阶段触发事件对象上的事件。结果,就是有两个机会在目标对象上面操作事件。

    IE9、Opera、Firefox、Chrome 和 Safari 都支持 DOM 事件流;IE8 及更早版本不支持 DOM 事件流。

    事件处理程序

    事件就是用户或浏览器自身执行的某种动作,而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。为事件指定处理 程序的方式有好几种。

    1.HTML事件处理程序

    某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的 HTML 特性来指定。可以是一段执行的javascript代码,也可以调用在页面其他地方定义的脚本。
    事件处理程序中的代码在执行时,有权访问全局作用域中的任何代码。如果当前元素是一个表单输入元素,则作用域中还会包含访问表单元素(父元素)的入口。

    <!--会弹出Click Me-->
    <input type="button" value="Click Me" onclick="alert(this.value)">
    <form method="post">
        <input type="text" name="username" value="Jackie">
        <!--会弹出Jackie-->
        <input type="button" value="Echo Username" onclick="alert(username.value)">
    </form>
    

    在HTML中指定事件处理程序的3个缺点:

    • 时差问题:用户可能会在 HTML 元素一出现在页面上就触发相应的事件,但当时的事件处理程序有可能尚不具备执行条件。为此,很多 HTML 事件处理程序都会被封装在一个 try-catch 块中,以便错误不会浮出水面。
    <p onclick="try{showMessage();}catch (ex){console.log(ex);}">错误测试</p>
    
    • 这样扩展事件处理程序的作用域链在不同浏览器中会导致不同结果。不同 JavaScript 引擎遵循的标识符解析规则略有差异,很可能会在访问非限定对象成员时出错。
    • HTML 与 JavaScript 代码紧密耦合。如果要更换事件处理程序,就要改动两个地方:HTML 代码和 JavaScript 代码。
    2. DOM0 级事件处理程序

    通过 JavaScript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。但也要注意,如果指定事件处理程序的代码在页面中位于按钮后面,就有可能在一段时间内怎么单击都没有反应

    使用 DOM0 级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在元素的作用域中运行,也就是程序中的 this 引用当前元素,可以在事件处理程序中通过 this 访问元素的任何属性和方法。

    html:
    <button id="myButton">按钮</button>
    js:
    var btn = document.getElementById("myButton");
    btn.onclick = function (e) {
        console.log(this.id);//打印myButton
        console.log(e);
    }
    

    以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。
    也可以删除通过 DOM0 级方法指定的事件处理程序:

    btn.onclick = null;//设为null即可
    

    而且,如果你使用 HTML 指定事件处理程序,那么 onclick 属性的值就是一个包含着在同名 HTML 特性中指定的代码的函数。而将相应的属性设置为 null,也可以删除以这种方式指定的事件处理程序。

    3.DOM2 级事件处理程序

    DOM2 级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener() 和 removeEventListener()。所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。布尔值 true表示在捕获阶段调用事件处理程序; false表示在冒泡阶段调用事件处理程序。

    与 DOM0 级方法一样,这里添加的事件处理程序也是在其依附的元素的作用域中运行。使用 DOM2 级方法添加事件处理程序的主要好处是可以添加多个事件处理程序

    var btn = document.getElementById("myButton");
    btn.addEventListener('click',function () {
        console.log(this.id);
    },false);
    btn.addEventListener('click',function () {
        console.log('hello world');
    },false);
    

    示例中的事件处理程序会按照添加它们的顺序触发,因此首先会显示元素的 ID,其次会显示"hello world"消息。

    回顾DOM事件流,3个阶段顺序是:事件捕获阶段>处于目标阶段>事件冒泡阶段,捕获或者冒泡到某个元素,或者处于目标的时候,就按照添加事件处理程序的顺序来进行处理:

    var btn = document.getElementById("myButton");
    document.body.addEventListener('click',function () {
        console.log('冒泡打印body1');
    },false);
    document.body.addEventListener('click',function () {
        console.log('冒泡打印body');
    },false);
    document.documentElement.addEventListener('click', function () {
        console.log('冒泡打印html');
    }, false);
    btn.addEventListener('click', function () {
        console.log('冒泡打印元素');
    }, false);
    btn.addEventListener('click', function () {
        console.log('捕获打印元素');
    }, true);
    document.documentElement.addEventListener('click', function () {
        console.log('捕获打印html');
    }, true);
    document.body.addEventListener('click',function () {
        console.log('捕获打印body');
    },true);
    打印顺序是:
    捕获打印html》捕获打印body》冒泡打印元素》
    捕获打印元素》冒泡打印body1》冒泡打印body》
    冒泡打印html
    

    通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()来移除,移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过 addEventListener()添加的匿名函数将无法移除

    var btn = document.getElementById("myButton");
    var clickHandler = function () {
        console.log('这是按钮点击事件');
    };
    btn.addEventListener('click',clickHandler,false);
    setTimeout(function () {
        //移除事件处理程序
        btn.removeEventListener('click',clickHandler,false);
    },5000);
    

    大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。

    IE9、Firefox、Safari、Chrome 和 Opera 支持 DOM2 级事件处理程序。

    4.IE事件处理程序

    IE 实现了与 DOM 中类似的两个方法:attachEvent()和 detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。由于 IE8 及更早版本只支持事件冒泡,所以通过 attachEvent()添加的事件处理程序都会被添加到冒泡阶段

    var btn = document.getElementById("myButton");
    //注意第一个参数是onclick,不是click
    btn.attachEvent("onclick", function(){
        console.log('IE添加事件')
    });
    

    在 IE 中使用 attachEvent()与使用 DOM0 级方法的主要区别在于事件处理程序的作用域,使用 attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此 this 等于 window。

    attachEvent()方法也可以用来为一个元素添加多个事件处理程序:

    btn.attachEvent("onclick", function(){
        alert("Clicked");
    });
    btn.attachEvent("onclick", function(){
        alert("Hello world!");
    });
    

    与 DOM 方法不同的是,这些事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发

    使用 attachEvent()添加的事件可以通过 detachEvent()来移除,条件是必须提供相同的参数。 与 DOM 方法一样,这也意味着添加的匿名函数将不能被移除。

    var handler = function(){
        alert("Clicked");
    };
    btn.attachEvent("onclick", handler);
    
    //这里省略了其他代码..
    
    btn.detachEvent("onclick", handler);
    

    支持 IE 事件处理程序的浏览器有 IE 和 Opera

    5.跨浏览器的事件处理程序

    跨浏览器的事件处理:

    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;
    } }
    };
    使用:
    var btn = document.getElementById("myBtn");
    var handler = function(){
        alert("Clicked");
    };
    EventUtil.addHandler(btn, "click", handler); 
    
    //这里省略了其他代码....
    
    EventUtil.removeHandler(btn, "click", handler);
    

    addHandler()和 removeHandler()没有考虑到所有的浏览器问题,例如在 IE 中的作用域问题。 不过,使用它们添加和移除事件处理程序已足够了。虽然DOM0 级对每个事件只支持一个事件处理程序,但只支持 DOM0 级的浏览器很少很少了。

    相关文章

      网友评论

        本文标题:事件总结1:事件流和事件处理程序

        本文链接:https://www.haomeiwen.com/subject/tlcqqftx.html