首页 > 网站 > WEB开发 > 正文

最详细的JavaScript和事件解读

2024-04-27 14:11:43
字体:
来源:转载
供稿:网友

最详细的javaScript和事件解读

与浏览器进行交互的时候浏览器就会触发各种事件。比如当我们打开某一个网页的时候,浏览器加载完成了这个网页,就会触发一个load事件;当我们点击页面中的某一个“地方”,浏览器就会在那个“地方”触发一个click事件。

这样,我们就可以编写 Javascript,通过监听某一个事件,来实现某些功能扩展。例如监听load事件,显示欢迎信息,那么当浏览器加载完一个网页之后,就会显示欢迎信息。

下面就来介绍一下事件。

基础事件操作

监听事件

浏览器会根据某些操作触发对应事件,如果我们需要针对某种事件进行处理,则需要监听这个事件。监听事件的方法主要有以下几种:

HTML 内联属性(避免使用)

HTML 元素里面直接填写事件有关属性,属性值为 JavaScript 代码,即可在触发该事件的时候,执行属性值的内容。

例如:

<button onclick="alert('你点击了这个按钮');">点击这个按钮</button>

onclick 属性表示触发 click,属性值的内容(JavaScript 代码)会在单击该 HTML 节点时执行。

显而易见,使用这种方法,JavaScript 代码与 HTML 代码耦合在了一起,不便于维护和开发。所以除非在必须使用的情况(例如统计链接点击数据)下,尽量避免使用这种方法。

DOM 属性绑定

也可以直接设置 DOM 属性来指定某个事件对应的处理函数,这个方法比较简单:

element.onclick = function(event){    alert('你点击了这个按钮');};

上面代码就是监听 element 节点的 click 事件。它比较简单易懂,而且有较好的兼容性。但是也有缺陷,因为直接赋值给对应属性,如果你在后面代码中再次为 element 绑定一个回调函数,会覆盖掉之前回调函数的内容。

虽然也可以用一些方法实现多个绑定,但还是推荐下面的标准事件监听函数。

使用事件监听函数

标准的事件监听函数如下:

element.addEventListener(<event-name>, <callback>, <use-capture>);

表示在 element 这个对象上面添加一个事件监听器,当监听到有 <event-name> 事件发生的时候,调用 <callback> 这个回调函数。至于 <use-capture> 这个参数,表示该事件监听是在“捕获”阶段中监听(设置为 true)还是在“冒泡”阶段中监听(设置为 false)。关于捕获和冒泡,我们会在下面讲解。

用标准事件监听函数改写上面的例子:

var btn = document.getElementsByTagName('button');btn[0].addEventListener('click', function() {    alert('你点击了这个按钮');}, false);

这里最好是为 HTML 结构定义个 id 或者 class 属性,方便选择,在这里只作为演示使用。

Demo:

移除事件监听

当我们为某个元素绑定了一个事件,每次触发这个事件的时候,都会执行事件绑定的回调函数。如果我们想解除绑定,需要使用 removeEventListener 方法:

element.removeEventListener(<event-name>, <callback>, <use-capture>);

需要注意的是,绑定事件时的回调函数不能是匿名函数,必须是一个声明的函数,因为解除事件绑定时需要传递这个回调函数的引用,才可以断开绑定。例如:

var fun = function() {    // function logic};element.addEventListener('click', fun, false);element.removeEventListener('click', fun, false);

Demo:

事件触发过程

在上面大体了解了事件是什么、如何监听并执行某些操作,但我们对事件触发整个过程还不够了解。

下图就是事件的触发过程,借用了W3C 的图片

捕获阶段(Capture Phase)

当我们在 DOM 树的某个节点发生了一些操作(例如单击、鼠标移动上去),就会有一个事件发射过去。这个事件从 Window 发出,不断经过下级节点直到目标节点。在到达目标节点之前的过程,就是捕获阶段(Capture Phase)。

所有经过的节点,都会触发这个事件。捕获阶段的任务就是建立这个事件传递路线,以便后面冒泡阶段顺着这条路线返回 Window。

监听某个在捕获阶段触发的事件,需要在事件监听函数传递第三个参数 true。

element.addEventListener(<event-name>, <callback>, true);

但一般使用时我们往往传递 false,会在后面说明原因。

目标阶段(Target Phase)

当事件跑啊跑,跑到了事件触发目标节点那里,最终在目标节点上触发这个事件,就是目标阶段。

需要注意的时,事件触发的目标总是最底层的节点。比如你点击一段文字,你以为你的事件目标节点在 div 上,但实际上触发在 <p>、<span> 等子节点上。例如:

在 Demo 中,我监听单击事件,将目标节点的 tag name 弹出。当你点击加粗字体时,事件的目标节点就为最底层的 <strong> 节点。

冒泡阶段(Bubbling Phase)

当事件达到目标节点之后,就会沿着原路返回,由于这个过程类似水泡从底部浮到顶部,所以称作冒泡阶段。

在实际使用中,你并不需要把事件监听函数准确绑定到最底层的节点也可以正常工作。比如在上例,你想为这个 <div> 绑定单击时的回调函数,你无须为这个 <div> 下面的所有子节点全部绑定单击事件,只需要为 <div> 这一个节点绑定即可。因为发生它子节点的单击事件,都会冒泡上去,发生在 <div> 上面。

针对这三个阶段,wilsonpage 做了一个非常棒的Demo,可以看下:

为什么不用第三个参数 true

介绍完上面三个事件触发阶段,我们来看下这个问题。

所有介绍事件的文章都会说,在使用 addEventListener 函数来监听事件时,第三个参数设置为 false,这样监听事件时只会监听冒泡阶段发生的事件。

这是因为 IE 浏览器不支持在捕获阶段监听事件,为了统一而设置的,毕竟 IE 浏览器的份额是不可忽略的。

IE 浏览器在事件这方面与标准还有一些其他的差异,我们会在后面集中介绍。

使用事件代理(Event Delegate)提升性能

因为事件有冒泡机制,所有子节点的事件都会顺着父级节点跑回去,所以我们可以通过监听父级节点来实现监听子节点的功能,这就是事件代理。

使用事件代理主要有两个优势:

  1. 减少事件绑定,提升性能。之前你需要绑定一堆子节点,而现在你只需要绑定一个父节点即可。减少了绑定事件监听函数的数量。
  2. 动态变化的 DOM 结构,仍然可以监听。当一个 DOM 动态创建之后,不会带有任何事件监听,除非你重新执行事件监听函数,而使用事件监听无须担忧这个问题。

看一个例子:

上面例子中,为了简便,我使用 jQuery 来实现普通事件绑定和事件代理。我的目标是监听所有 a 链接的单击事件,.ul1 是常规的事件绑定方法,jQuery 会循环每一个 .ul > a 结构并绑定事件监听函数。.ul2 则是事件监听的方法,jQuery 只为 .ul2 结构绑定事件监听函数,因为 .ul2 下面可能会有很多无关节点也会触发 click 事件,所以我在 on 函数里传递了第二个参数,表示只监听 a 子节点的事件。

它们都可以正常工作,但是当我动态创建新 DOM 结构的时候,第一个 ul 问题就出现了,新创建结构虽然还是 .ul1 > a,但是没有绑定事件,所以无法执行回调函数。而第二个 ul 工作的很好,因为点击新创建的 DOM ,它的事件会冒泡到父级节点进行处理。

如果使用原生的方式实现事件代理,需要注意过滤非目标节点,可以通过 id、class 或者 tagname 等等,例如:

element.addEventListener('click', function(event) {    // 判断是否是 a 节点    if ( event.target.tagName == 'A' ) {        // a 的一些交互操作    }}, false);

停止事件冒泡(stopPRopagation)

所有的事情都会有对立面,事件的冒泡阶段虽然看起来很好,也会有不适合的场所。比较复杂的应用,由于事件监听比较复杂,可能会希望只监听发生在具体节点的事件。这个时候就需要停止事件冒泡。

停止事件冒泡需要使用事件对象的 stopPropagation 方法,具体代码如下:

element.addEventListener('click', function(event) {    event.stopPropagation();}, false);

在事件监听的回调函数里,会传递一个参数,这就是 Event 对象,在这个对象上调用 stopPropagation 方法即可停止事件冒泡。举个停止事件冒泡的应用实例:

JS Bin

在上面例子中,有一个弹出层,我们可以在弹出层上做任何操作,例如 click 等。当我们想关掉这个弹出层,在弹出层外面的任意结构中点击即可关掉。它首先对 document 节点进行 click 事件监听,所有的 click 事件,都会让弹出层隐藏掉。同样的,我们在弹出层上面的单击操作也会导致弹出层隐藏。之后我们对弹出层使用停止事件冒泡,掐断了单击事件返回 document 的冒泡路线,这样在弹出层的操作就不会被 document 的事件处理函数监听到。

更多关于 Event 对象的事情,我们会在下面介绍。

事件的 Event 对象

当一个事件被触发的时候,会创建一个事件对象(Event Object),这个对象里面包含了一些有用的属性或者方法。事件对象会作为第一个参数,传递给我们的毁掉函数。我们可以使用下面代码,在浏览器中打印出这个事件对象:

<button>打印 Event Object</button><script>    var btn = document.getElementsByTagName('button');    btn[0].addEventListener('click', function(event) {        console.log(event);    }, false);</script>

就可以看到一堆属性列表:

事件属性列表

事件对象包括很多有用的信息,比如事件触发时,鼠标在屏幕上的坐标、被触发的 DOM 详细信息、以及上图最下面继承过来的停止冒泡方法(stopPropagation)。下面介绍一下比较常用的几个属性和方法:

type(string)

事件的名称,比如 “click”。

target(node)

事件要触发的目标节点。

bubbles (boolean)

表明该事件是否是在冒泡阶段触发的。

preventDefault (function)

这个方法可以禁止一切默认的行为,例如点击 a 标签时,会打开一个新页面,如果为 a 标签监听事件 click 同时调用该方法,则不会打开新页面。

stopPropagation (function)

停止冒泡,上面有提到,不再赘述。

stopImmediatePropagation (function)

与 stopPropagation 类似,就是阻止触发其他监听函数。但是与 stopPropagation 不同的是,它更加 “强力”,阻止除了目标之外的事件触发,甚至阻止针对同一个目标节点的相同事件,Demo:http://jsfiddle.net/yujiangshui/ju2ujmzp/2/。

cancelable (boolean)

这个属性表明该事件是否可以通过调用 event.preventDefault 方法来禁用默认行为。

eventPhase (number)

这个属性的数字表示当前事件触发在什么阶段。none:0;捕获:1;目标:2;冒泡:3。

pageX 和 pageY (number)

这两个属性表示触发事件时,鼠标相对于页面的坐标。Demo:http://api.jquery.com/event.pagex/。

isTrusted (boolean)

表明该事件是浏览器触发(用户真实操作触发),还是 JavaScript 代码触发的。

jQuery

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表