十九

作者: xpwei | 来源:发表于2018-04-18 18:48 被阅读8次

    跨浏览器的事件处理程序
    要保证处理事件的代码能在大多数浏览器下一致地运行,只需关注冒泡阶段。

    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;
            }
        }
    };
    

    这两个方法首先都会检测传入的元素中是否存在DOM2级方法。如果存在DOM2级方法,则使用该方法:传入事件类型、事件处理程序函数和第三个参数false(表示冒泡阶段)。如果存在的是IE的方法,则采用第二种方案。注意,为了在IE8及更早版本中运行,此时的事件类型必须加上“on”前缀。最后一种可能就是使用DOM0级方法(在现代浏览器中,应该不会执行这里的代码)。此时,我们使用的是方括号语法来将属性名指定为事件处理程序,或者将属性设置为null。
    可以像下面这样使用EventUtil对象:

    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级的浏览器已经没有那么多了,因此这对你而言应该不是什么问题。

    事件对象
    在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含这所有与事件有关的信息。

    DOM中的事件对象
    兼容DOM的浏览器会将一个event对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0或DOM2),都会传入event对象:

    var btn = document.getElementById("myBtn");
    btn.onclick = function(event) {
        alert(event.type); //"click"
    };
    btn.addEventListener("click", function(event) {
        alert(event.type); //"click"
    }, false);
    

    在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget和target包含相同的值:

    var btn = document.getElementById("myBtn");
    btn.onclick = function(event) {
        alert(event.currentTarget === this); //true
        alert(event.target === this); //true
    };
    
    document.body.onclick = function(event) {
        alert(event.currentTarget === document.body); //true
        alert(this === document.body); //true
        alert(event.target === document.getElementById("myBtn")); //true
    };
    

    当点击这个例子的按钮时,this和currentTarget都等于document.body,因为事件处理程序是注册到这个元素上的。然而。target元素却等于按钮元素,因为它是click事件真正的目标。由于按钮上并没有注册事件处理程序,结果click事件就,冒泡到了document.body,在那里事件才得到了处理。
    在需要通过一个函数处理多个事件时,可以使用type属性:

    var btn = document.getElementById("myBtn");
    var handler = function(event) {
        switch (event.type) {
            case "click":
                alert("Clicked");
                break;
            case "mouseover":
                event.target.style.backgroundColor = "red";
                break;
            case "mouseout":
                event.target.style.backgroundColor = "";
                break;
        }
    };
    btn.onclick = handler;
    btn.onmouseover = handler;
    btn.onmouseout = handler;
    

    要阻止特定事件的默认行为,可以使用preventDefault()方法。例如,链接的默认行为就是在被单击时会导航到其href特定指定的URL。如果你想阻止链接导航这一默认行为:

    var link = document.getElementById("myLink");
    link.onclick = function(event) {
        event.preventDefault();
    };
    

    只有cancellabel属性设置为true的事件,才可以使用preventDefault()来取消其默认行为。
    另外,stopPropagation()方法用于立即停止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。例如,直接添加到一个按钮的事件处理程序可以调用stopPropagation(),从而避免触发注册在document.body上面的事件处理程序:

    var btn = document.getElementById("link");
    btn.onclick = function(event) {
        alert("Clicked");
        event.stopPropagation();
    };
    document.body.onclick = function(event) {
        alert("Body clicked");
    };
    

    对于这个例子而言,如果不调用stopPropagation(),就会在单击按钮时出现两个警告框。可是,由于click事件根本不会传播到document.body,因此就不会触发注册在这个元素上的onclick事件处理程序。
    事件对象的eventPhase属性,可以用来确定事件当前正位于事件流的哪个阶段。如果是在捕获阶段调用的事件处理程序,那么eventPhase等于1;如果事件处理程序处于目标对象上,等于2;如果是在冒泡阶段调用的事件处理程序,等于3。这里要注意的是,尽管“处于目标”发生在冒泡阶段,但eventPhase仍然一直等于2。

    var btn = document.getElementById("myBtn");
    btn.onclick = function(event) {
        alert(event.eventPhase); //2
    };
    document.body.addEventListener("click", function(event) {
        alert(event.eventPhase); //1
    }, true);
    document.body.onclick = function(event) {
        alert(event.eventPhase); //3
    };
    

    只有在事件处理程序执行期间,event对象才会存在;一旦事件处理程序执行完成,event对象就会被销毁。

    IE中的事件对象
    与访问DOM中的event对象不同,要访问IE中的event对象有几种不同的方式,取决于指定事件处理程序的方法。在使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在:

    var btn = document.getElementById("myBtn");
    btn.onclick = function() {
        var event = window.event;
        alert(event.type); //"click"
    };
    

    如果事件处理程序是使用attachEvent()添加的,那么就会有一个event对象作为参数被传入事件处理程序函数中:

    var btn = document.getElementById("myBtn");
    btn.attachEvent("onclick", function(event) {
        alert(event.type); //"click"
    });
    

    在像这样使用attachEvent()的情况下,也可以通过window对象来访问event对象,就像使用DOM0级方法时。不过为了方便起见,同一个对象也会作为参数传递。
    因为事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为this会始终等于事件目标。故而,最好还是使用event.srcElement比较保险:

    var btn = document.getElementById("myBtn");
    btn.onclick = function() {
        alert(window.event.srcElement === this); //true
    };
    btn.attachEvent("onclick", function(event) {
        alert(event.srcElement === this); //false
    });
    

    returnValue属性相当于DOM中的preventDefault()方法,它们的作用都是取消给定事件的默认行为。只要将returnValue设置为false,就可以阻止默认行为:

    var link = document.getElementById("myLink");
    link.onclick = function() {
        window.event.returnValue = false;
    };
    

    与DOM不同的是,在此没有办法确定事件是否能被取消。
    相应地,cancelBubble属性与DOM中的stopPropagation()方法作用相同,都是用来停止事件冒泡的。由于IE不支持事件捕获,因而只能取消事件冒泡;但stopPropagatioin()可以同时取消事件捕获和冒泡:

    var btn = document.getElementById("myBtn");
    btn.onclick = function() {
        alert("Clicked");
        window.event.cancelBubble = true;
    };
    document.body.onclick = function() {
        alert("Body clicked");
    };
    

    跨浏览器的事件对象
    虽然DOM和IE中的event对象不同,但基于它们之间的相似性依旧可以拿出跨浏览器的方案来。IE中event对象的全部信息和方法DOM对象中都有,只不过实现方式不一样。不过,这种对应关系让实现两种事件模型之间的映射非常容易。可以对前面介绍的EventUtil对象加以增强:

    var EventUtil = {
        addHandler: function(element, type, handler) {
            //省略的代码
        },
        getEvent: function(event) {
            return event ? event : window.event;
        },
        getTarget: function(event) {
            return event.target || event.srcElement;
        },
        preventDefault: function(event) {
            if (event.preventDefault) {
                event.preventDefault();
            } else {
                event.returnValue = false;
            }
        },
        removeHandler: function(element, type, handler) {
            //省略的代码
        },
        stopPropagation: function(event) {
            if (event.stopPropagation) {
                event.stopPropagation();
            } else {
                event.cancelBubble = true;
            }
        }
    };
    

    以上代码显示,我们为EventUtil添加了4个新方法。第一个是getEvent(),它返回对event对象的引用。考虑到IE中事件对象的位置不同,可以使用这个方法来取得event对象,而不必担心指定事件处理程序的方式。在使用这个方法时,必须假设有一个事件对象传入到事件处理程序中,而且要把该变量传给这个方法:

    btn.onclick = function(event) {
        event = EventUtil.getEvent(event);
    };
    
    btn.onclick = function(event) {
        event = EventUtil.getEvent(event);
        var target = EventUtil.getTarget(event);
    };
    
    var link = document.getElementById("myLink");
    link.onclick = function(event) {
        event = EventUtil.getEvent(event);
        EventUtil.preventDefault(event);
    };
    
    var btn = document.getElementById("myBtn");
    btn.onclick = function(event) {
        alert("Clicked");
        event = EventUtil.getEvent(event);
        EventUtil.stopPropagation(event);
    };
    document.body.onclick = function(event) {
        alert("Body clicked");
    };
    

    相关文章

      网友评论

          本文标题:十九

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