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事件绑定模型很好的解决了这个问题

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

  • 相关阅读:
    Linux命令应用大词典-第11章 Shell编程
    Kubernetes 学习12 kubernetes 存储卷
    linux dd命令
    Kubernetes 学习11 kubernetes ingress及ingress controller
    Kubernetes 学习10 Service资源
    Kubernetes 学习9 Pod控制器
    Kubernetes 学习8 Pod控制器
    Kubernetes 学习7 Pod控制器应用进阶2
    Kubernetes 学习6 Pod控制器应用进阶
    Kubernetes 学习5 kubernetes资源清单定义入门
  • 原文地址:https://www.cnblogs.com/hh54188/p/2373341.html
Copyright © 2011-2022 走看看