DOM事件的那些事儿

DOM(Document Object Model) 即文档对象模型,是针对HTML和XML文档的一个API,DOM描绘了一个层次化的节点树,允许开发人员进行添加、移除和修改页面的某一部分。

一、DOM事件的级别

  • DOM0 点击事件的JS中写法是: element.onclick=function(){ },如果在HTML中就是onclick属性上加一个JS语句。
    删除DOM0事件处理程序,只要将对应事件属性置为null即可,即element.onclick=null
  • DOM2 新增的点击事件JS中写法是:element.addEventListener('click', function(){}, false)。最后一个参数为true的时候表示在捕获阶段调用程序,如果是false,表示在冒泡阶段调用事件处理程序,不填则默认为false
    删除DOM2事件处理程序,用removeEventListener实现。
    IE中的DOM2级事件处理使用了attachEvent来实现,IE9以下版本只支持冒泡事件,所以attachEvent添加的事件都是冒泡阶段。attachEvent添加的事件第一个参数是onclick而非标准事件中的click。使用detachEvent实现删除事件。
  • DOM3 定义方式没变,知识新增了很多事件类型,包括UI事件,鼠标事件,焦点事件,滚轮事件等等。如:element.addEventListener('keyup', function(){}, false)

因为DOM1主要专注于HTML文档和XML文档,没有涉及事件处理,所以事件处理直接从DOM0跳到DOM2。

二、DOM事件模型

DOM事件模型分为两类:一类是IE所使用的冒泡型事件(Bubbling);另一类是DOM标准定义的冒泡型与捕获型(Capture)的事件。除IE外的其他浏览器都支持标准的DOM事件处理模型。

DOM事件模型

  • 冒泡型事件处理模型(Bubbling)
    如上图所示,冒泡型事件处理模型在事件发生时,首先在最精确的元素上触发,然后向上传播,直到根节点。反映到DOM树上就是事件从叶子节点传播到根节点。

  • 捕获型事件处理模型(Captrue)
    相反地,捕获型在事件发生时首先在最顶级的元素上触发,传播到最低级的元素上。在DOM树上的表现就是由根节点传播到叶子节点。

  • 标准的DOM事件处理模型
    标准的事件处理模型分为三个阶段:
    (1) 父元素中所有的捕获型事件(如果有)自上而下地执行
    (2) 目标元素的冒泡型事件(如果有)
    (3) 父元素中所有的冒泡型事件(如果有)自下而上地执行

三、DOM事件流

DOM标准采用捕获+冒泡。两种事件流都会触发DOM的所有对象,从document对象开始,也在document对象结束。


DOM事件流

标准DOM事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段

  • 事件捕获阶段:实际目标(<div>)在捕获阶段不会接收事件。也就是在捕获阶段,事件从document到<html>再到<body>就停止了。上图中为1~3。
  • 处于目标阶段:事件在<div>上发生并处理。但是事件处理会被看成是冒泡阶段的一部分。
  • 冒泡阶段:事件又传播回文档。

四、DOM事件捕获的具体流程

DOM事件捕获具体流程

首先接收的是window,然后是ducument,再是html标签(js获取html节点用document.documentElement),然后是body,最后随着节点父子关系一级一级往下传,直到目标元素。
冒泡则相反,从目标元素到window一级一级往上。
通过代码来描述事件捕获过程:

<div id="ev"  style='width:100px;height: 100px;background: blue'></div>
// 把打印顺序搅乱,以免误以为是因为执行顺序影响
<script>
        var ev = document.getElementById('ev');

        ev.addEventListener('click', function (e) {
            console.log('ev captrue');
        }, true);

        window.addEventListener('click', function (e) {
            console.log('window captrue');
        }, true);

        document.addEventListener('click', function (e) {
            console.log('document captrue');
        }, true);

        document.body.addEventListener('click', function (e) {
            console.log('body captrue');
        }, true);

        document.documentElement.addEventListener('click', function (e) {
            console.log('html captrue');
        }, true);

        // 打印结果如下:
       // window captrue
       // document captrue
      // html captrue
      // body captrue
      // ev captrue

</script>

通过代码来描述事件冒泡过程:

<div id="ev"  style='width:100px;height: 100px;background: blue'></div>
// 把打印顺序搅乱,以免误以为是因为执行顺序影响
<script>
        var ev = document.getElementById('ev');

        ev.addEventListener('click', function (e) {
            console.log('ev captrue');
        }, false);

        window.addEventListener('click', function (e) {
            console.log('window captrue');
        }, false);

        document.addEventListener('click', function (e) {
            console.log('document captrue');
        }, false);

        document.body.addEventListener('click', function (e) {
            console.log('body captrue');
        }, false);

        document.documentElement.addEventListener('click', function (e) {
            console.log('html captrue');
        }, false);

        // 打印结果如下:
        // ev captrue
        // body captrue
        // html captrue
        // document captrue
       // window captrue
</script>

五、Event对象的常见应用

  • event.preventDefault():阻止默认行为。常用的情况就是给一个<a>标签绑定了click事件,响应函数中设置了 event.preventDefault(),就阻止了链接跳转的行为。
  • event.stopPropagation():阻止冒泡行为。比如:一个父级元素绑定了一个事件,子元素绑定了另一个事件,如果想父级元素做一件事,子元素做一件事,两件事是分开的,互不影响,就需要给子元素事件中设置event.stopPropagation(),否则子元素事件执行时,按照冒泡的原则,父级元素事件也会响应。
  • event.stopImmediatePropagation:除了该事件的冒泡行为被阻止之外(event.stopPropagation方法的作用),该元素绑定的后序相同类型事件的监听函数的执行也将被阻止。如一个元素上绑定了2个click事件a和b,如果给a函数中添加event.stopImmediatePropagation后,则阻止click事件冒泡,并且阻止了b函数的执行。
  • event.currentTarget:当事件遍历DOM时,标识事件的当前目标(类似于this)。如下例子:
    <p>1</p>
    <p>2</p>
    <p>3</p>
    <script>
        var ps = document.getElementsByTagName('p');
        for (var i = 0; i < ps.length; i++) {
            ps[i].addEventListener('click', func, false);
        }

        function func(e) {
            console.log(e.currentTarget);  // 打印所点击对应的<p>节点
            // 该函数用作事件处理器时: this === e.currentTarget
        }
    </script>
  • event.target:表示一个触发事件的对象的引用,常用来实现事件委托。如:
<ul id="ul">
   <li>1</li>
   <li>2</li>
   <li>3</li>
</ul>

    <script>
        document.getElementById("ul").addEventListener('click', function(){
              console.log(event.target);   // 当点击1时,打印<li>1</li>
              console.log(event.currentTarget);  // 当点击1或2或3时,都打印整个<ul>
        });
    </script>

六、自定义事件

以上所讲都是DOM一些自带的事件,当然也可以自己定义一些事件。

  • ① 自定义一个事件:new Event()
  • ② 给一个dom节点绑定自定义的事件
  • ③ 使用dispatchEvent() 分派事件
<div id="ev"></div>

<script>
var ev = document.getElementById('ev');
var evt = new Event('test');
ev.addEventListener('test', function () {
    console.log('test dispatch');
});
setTimeout(function () {
    ev.dispatchEvent(evt);
}, 3000);

// 打开页面3面后打印 'test dispatch'
</script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容