JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)

这篇博客,我们来说下自定义 DOM 事件。
我们平常用的事件,其实都是一个很被动的状态,什么意思呢?
就是说,你就是等着它那事件过来,你自己不会去触发事件。
但是事实上我们可以做到这点,并且这个在很多时候其实是比较有需要的。

比方说,就我们前面博客写的 touch 滚动库,它就有这个需求。
目前来看,我们这个滚动库看起来还蛮正常的,但是如果我们在里面加个按钮,和 click 事件:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

可以看到,拖还是照样拖,但是当我们点这个按钮的时候就没反应了。
那这是什么情况,为啥给干掉了?
原因其实特别简单,因为我们现在整个库是依赖于 touch 事件在工作的。
而且我们还做了一个特别不靠谱的事:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

我们在 __start 的时候,做了一个 preventDefault,当然这个 preventDefault 我们也必须要做。
那么如果说我们不做这个,首先肯定一点,这个点击会没问题:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

但是我们整个拖动也乱套了,页面也跟着一起跑。
所以不做 preventDefault,也不对。
那么在这种时候,如果我们想解决这个问题,我们必须得依赖于我们的库,来触发 click 事件才可以。
所以说在这个时候,我们其实是有需求的,就是在特定的情况下,我们需要自己手动的来触发一个标准的 DOM 事件。

那么我们就进入正题,比方说我们现在有 2 个按钮。
按钮 1 上有个 alert,和刚才类似:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

然后我现在希望的是,点击按钮 2 的时候,可以触发按钮 1 上的事件:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

那么这个怎么办呢?
其实这个事不复杂,官方已经替我们想到了。
首先,所有的元素,包括按钮之类的,都有一个方法,叫做 dispatchEvent:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

dispatch 就是分派,派发的意思,而 event 则是事件的意思。
这里的 btn1.dispatchEvent() 其实说白了,就是我向按钮 1 去发送一个事件,就这么简单。

但是这个时候我们肯定不能空着,如果空着的话,它出不来,还会报错:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

它说,我需要一个参数,但是你没有给我。
因为我们要做的是派发一个事件,那你得有事件吧?所以这个报错一点都不奇怪。

所以这时候,我们就需要来 new Event(),然后给到它:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

然后这时候你会发现,点击按钮 2 的时候,它又报错了:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

这个时候就是 Event 报错了,它说我需要一个参数,你没有给我。
那么到这为止,我们有没有发现不对劲的地方?比如,我们触发的是什么事件?是 click?还是 mouseover?
所以在 new Event() 这,我们就要申明事件类型是什么,比方说 click:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

然后这时候,我们再点按钮 2,你可以看到,alert 就出来了:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

虽然我们点的是按钮 2,但按钮 1 就间接的被我们触发了。
我们依赖的就是 dispatchEvent。

当然有人可能会想,搞这么复杂干嘛,直接去调用 btn1.onclick() 不就行了吗?
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

可以看到,现在是可以的。

但是它有时候却是不行的。
因为我们现在的 click 事件,是这么加的:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

如果我们换个方法,用 addEventListener:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

那这时候再点按钮 2,就没反应了:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

因为我们 btn1.onclick() 这个写法,只能触发 2 种:
一种就是行间事件。


另一种就是这么加的:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

如果用的是 addEventListener 事件监听器,那么就无法处理了。
而反过来,如果我们用 dispatchEvent,那么它是万能的,什么都能给你弄出来:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

所以这个 dispatchEvent 是比较靠谱的。
并且,这个 dispatchEvent 它的适用范围很广,你除了能够去派发一个系统已有的事件,比如 click。
你要愿意的话,你也可以去派发一个自己的事件。

比如,我们是没有听说过一个叫 abc 事件的。
如果这时候,我们让它派发一个叫做 abc 的事件,那么也是可以的:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

所以事件可以自定义,你想叫什么名字都是你的自由。

而且也没人规定我一定得用 Event。
比如我们现在就不用 Event,自己搞个 json:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

那么这时候你可以看到,点击的时候就报错了。
它说,它需要一个 Event。
所以,如果我们想自定义的话,我们可以继承自 Event,比如我们自己搞一个 MyEvent:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

我们知道,标准的事件对象里面是没有一个叫做 a 的属性的,如果我们自己加一个 this.a = 12。
然后接下来,我们在 addEventListener 里面来接收这个 ev,并且打印出来看看:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

那么你可以看到,当我点按钮 2 的时候,首先 alert 一样会出来:
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

其次,这个打印里面也有一大堆东西,里面就有个 a:12。
JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)
文章图片

所以,我们可以去创建自己的事件类型,随便怎么折腾都没问题。
但是有一个问题,就是我们知道一件事,只要你沾了原生 DOM 就不太好,为什么呢?
首先,我们肯定一点,就是兼容性,这个东西不兼容最低级的 IE 6 和 IE 7。
IE 6 和 IE 7 有它们自己的一套东西,但是我们平常也不处理 IE 6、7,所以也不用去管它,所以你可以认为这个兼容性也是没有问题的。

那么我们来稍微的总结一下。
首先,这个 dispatchEvent() 里面,它需要一个参数,Event 对象。
并且这个 Event 对象,它必须得是一个 Event,你可以直接用 Event,也可以是继承自 Event 的。

然后在这,我们先说明一个问题。
首先,事件这个东西,我们说的是浏览器默认的那套事件,它是 DOM 的功能,事件并不是 js 的功能。
所以,事件跟其他的 DOM 功能有共通的地方,那就是慢,很慢,非常慢。

我们说原生的 DOM 事件,它有两个最大的缺陷,导致这个东西其实发挥不出太大的优势。
第一,就是性能低。
这个我之前测试过,跟我们自己实现的相比,性能大概是我们的十几分之一。
也就是说,如果我们自己来搞一套东西的话,可以比它提高十多倍。

第二,就是依赖于浏览器。
如果你使用 DOM 事件的话,你的这个 js 库,就绑在前台了,以后就只能在前台来工作了。
而现在很多时候,其实我们同一套库,我们要求它可以在各种端都能够运行,不管是 node 那一层,还是前台这一层,我们希望适用范围更广一些。
所以原生 DOM 事件,它的第二个缺陷,就是依赖于浏览器,说的更直白一点,就是 node 没法用。
包括像其他一些见不到浏览器环境的地方,比方说某些小程序之类的,它根本就不给你开放 document、window 相关的这些东西,你也没法用。
所以原生 DOM 事件有这 2 个问题,也就导致了我们是需要自己来搞一套事件的机制。
【JavaScript进阶|JavaScript进阶(五十)(自定义 DOM 事件,原生 DOM 事件的缺陷)】

    推荐阅读