zoukankan      html  css  js  c++  java
  • 【原创翻译】深入理解javascript事件处理函数绑定三部曲(二)——传统处理函数绑定模型


    原文地址

    这一次我要解释最优秀的事件处理函数绑定方式:确保当事件发生在某个html元素上时,能有相应的脚本与之对应

     

    在早期运行javascript的浏览器中,处理函数绑定只能通过行内模型。但自从DHTML彻底改变生成网页的方式后,事件绑定模型也得到拓展并且更加灵活。所以浏览器厂商引进了新的事件模型。Netscape是从第三代开始的,而IE在第四代中也同样引进。

    因为Netscape3已经支持新的绑定模型,所以至少在浏览器大战之前,从某种意义上来说它已经是一种执行的标准,。因此,微软为了使它的浏览器具有兼容性(因为大多数网页都遵循了Netscape的事件模型),最后一次不得不也采用了这种标准。

    所以两家浏览器,事实上是所有的浏览器,都认可这段代码:

    element.onclick = doSomething;

    作为一种绑定事件函数的正确方式。无论用户什么时候在什么浏览器上点击这个HTML元素,函数,doSomething()都会被执行。因为这种写法是通用,更因为它是跨浏览器绑定事件函数的唯一方式。但有一点非常重要,就是你也要非常明白这种方式的局限性和应该出现的场合。

    因为当这种模型被引进时没有任何的官方标准可以遵循,所以我把它称为传统事件绑定模型。与此同时W3C已经标准化了事件绑定,并且微软也发明了更先进的模型(参见下一篇),但是传统的模型仍然能良好的工作。

    更先进的事件绑定

    在Netscape3和Explorer4之后,javascript对每一种可以发生在元素上的事件,都作为自己的一种属性。因此HTML元素有onclick,onmouseover,onkeypress属性等。但究竟哪一种html元素有哪一种属性,哪一种html元素支持事件——依据浏览器而定。

    就这些属性而言,并不是什么很新鲜的东西。在早期的javascript浏览器中已经存在了:

    <a href="somewhere.html" onclick="doSomething()">

    这个A标签有一个onclick属性,也就意味着javascript变成了一种元素的属性。在早期的浏览器中,处理函数用这种写入标签的方式。所以如果你想在页面上的每一个超链接都有同样的处理函数,工作量是非常巨大的。

    随着传统绑定模型的出现,onclick,onmouseover等所有html元素的事件属性都可以通过javascript访问。在你通过DOM访问html元素后,现在你可以在最小程度的修改html代码的基础上,新增,修改或者移除处理函数。你可以把你的函数写入html的属性中:

    element.onclick = doSomething;

    在我们的例子中,函数doSomething()绑定到onclick属性上,并且无论用户点击这个元素,函数都会被执行。注意事件名称必须小写。

    要移除处理函数,只要简单的把onclick方法置空

    element.onclick = null;

    处理函数也是一个普通的javascript函数,就算事件没有发生它也能被执行,如果你这么作:

    element.onclick()

    doSomething()将被执行,虽然没有事件真的发生。但是这种执行处理函数的方式使用的并不非常多

    微软也为自己的IE5.5和之后的IE新增了fireEvent()方法,目的是为了实现上面的效果。

    语法应该这么写

    element.fireEvent('onclick')

    没有括号!

    请注意在绑定处理函数过程中不能使用括号()。分配给onclick给它的是整个函数。如果你这么做:

    element.onclick = doSomething();

    函数将会被执行,却是它的结果被绑定在onclick上。这并不是我们想要的,我们希望当事件发生时函数被执行。再者说,通常函数是期望有相应的事件发生,如果在没有上下文的情况下就执行它,会引起错误。

    this关键字

    在javascript中this关键字总是对一个函数“拥有者”的引用。在处理函数的例子中,如果this是代指正在处理事件的html元素,这将会非常有用。你可以很轻松的访问它。

    不幸的是this关键字虽然非常强大,但是如果你不是非常清楚它的工作原理的话,使用起来也很困难。我在另一篇文章中有谈论。这里我只给出一个简短的结论。

    在传统绑定模式中的this关键字,同行内绑定模式(上一篇)中的有所不同。在这里this关键字存在于函数中,而不是Html某个属性。具体的不同之处会在另一个页面单独指出

    element.onclick = doSomething;

    another_element.onclick = doSomething;

    function doSomething() {

    this.style.backgroundColor = '#cc0000';

    }

    如果你给任何一个Html元素的click事件绑定一个doSomething()函数,则用户在任何时候点击它时。它的背景都会变为红色。

    匿名函数

    假设你想在改变所有DIV的背景颜色,在onmouseover是改变在onmouseout时恢复,恰当的this应该这么使用:

    var x = document.getElementsByTagName('DIV');

    for (var i=0;i<x.length;i++) {
    x[i].onmouseover = over;
    x[i].onmouseout = out;
    }

    function over() {
    this.style.backgroundColor='#cc0000'
    }

    function out() {
    this.style.backgroundColor='#ffffff'
    }

    代码当然能正常工作,但是over()和out()函数这么简单,把他们绑定为匿名函数更为明智:

    ...

    for (var i=0;i<x.length;i++) {
    x[i].onmouseover = function () {this.style.backgroundColor='#cc0000'}

    x[i].onmouseout = function () {this.style.backgroundColor='#ffffff'}
    }

    onmouseover和onmouseoout期望能匹配对应的处理函数,相比复制over()和out()函数而言,我们在事件绑定脚本中立即定义了处理函数。因为他们没有函数名,所以称之为匿名函数。

    这两种绑定事件处理函数的方法是完全相同的的,唯一的不同之处是第二个用更少的代码。当我需要绑定一个简单的事件处理函数时,我更乐意使用匿名函数。

    问题

    传统模式的一个明显缺陷的就是onclick只能容下一个函数,那么当你想为一个事件绑定多个处理函数时这就变成了一个大问题。

    举个例子,假设你在写一个功能模块,实现一个元素的拖拽。模块借助于一个onclick处理函数上,当点击这个元素时开始执行拖拽。你同时也在实现另一个模块,能悄悄追踪用户的点击情况,并且在发生onunload事件时,把信息发送给服务器,你就可以发现用户到底是如何使用你的页面的。这个模块,同样借助于onclick处理函数。

    所以你可以这么做:

    element.onclick = startDragDrop;
    element.onclick = spyOnUser;

    但是问题发生了。第二个绑定onclick上的函数会覆盖第一个函数,所以当用户点击某个元素时,只有第二个spyOnUser被执行。解决方案当然是绑定一个能同时执行两个函数的函数

    element.onclick = function () {startDragDrop(); spyOnUser()}

    更灵活的绑定方式

    但是假设你不需要在每一个页面都需要这两个功能。现在如果你真的这么做:

    element.onclick = function () {startDragDrop(); spyOnUser()}

    你也许会得到错误的信息,因为这两个函数的其中之一可能还没有定义。当我们想要绑定spyOnUser()时,startDrapDrop()可能还没有绑定,所以我们这么做:

    var old = (element.onclick) ? element.onclick : function () {};

    element.onclick = function () {old(); spyOnUser()};

    首先你要定义一个名为old的变量,如果这个元素有一个onclick事件处理函数,把这个函数存储于old中,如果它没有,把一个空函数放进old中。再给div元素绑定一个新的处理函数,它首先执行的是old,再是spyOnUser()

    现在新的事件处理函数添加给元素了,并且之前的处理(如果有的话)还能得以保存

    还有最后一个问题:如果你想移除其中一个处理函数,而不是全部的,应该怎么办?现在我不知道应该如何是好了。你又不得不以另一种方式再次编辑element.onclick事件,但是我并不想真的去研究这个问题

    其他的模型

    现在我们已经见识到传统的事件绑定模型使用起来非常简单,但是也存在一些棘手的问题,比如说当你想给同一个事件添加不止一个处理函数时。所以W3C事件绑定模型很好的解决了这个问题

    下篇继续谈《先进的绑定模型》

  • 相关阅读:
    C++练习--实现客户机(CLIENT)类
    C++创建一个名为Ellipse的椭圆类--练习
    C++创建People类--练习
    C++定义一个简单的Computer类
    C++创建学生类练习
    第十四届浙江财经大学程序设计竞赛重现赛--A-A Sad Story
    第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛--I-填空题
    第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛--K-密码
    第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛--G-旋转矩阵
    Hadoop学习笔记(2) 关于MapReduce
  • 原文地址:https://www.cnblogs.com/hh54188/p/2373341.html
Copyright © 2011-2022 走看看