DOM事件

DOM 事件

事件监听函数

HTML 的 on 属性

实例

<button onclick="alert('123')"></button>
语法
<button onclick="函数名()"></button>
细节
on 属性的值原样传入 JS 引擎执行,不要忘了加圆括号执行
<!-- on 属性的值原样传入 JavaScript 引擎执行,不要忘了加圆括号执行。 -->

<!-- 正确 -->
<div onload="doSomething()">

<!-- 错误 -->
<div onload="doSomething">
on 只在冒泡阶段触发
<!-- 只在冒泡阶段触发。先 儿子 后 爸爸 -->
<div onClick="alert('爸爸')">
  <button onClick="alert('儿子')"></button>
</div>
元素节点的 setAttribute 方法设置 on 属性,效果是一样的
<!-- 元素节点的setAttribute方法设置on属性,效果是一样的。 -->
<script>abc.setAttribut("onclick", "console.log('hi!')")</script>
<!-- 等同于 -->
<div id="abc" onclick="console.log('hi!')">

元素节点对象的 on 事件属性

语法
// 元素节点的 on 属性值是函数名,不用括号。
button.onlick = doSomething;

button.onclick = function (event) {
  alert('元素节点对象的 on');
};

Node 对象的 addEventListener 方法

语法
target.addEventListener(type, listener, useCapture)
// 目标对象.addEventListener(事件类型,监听函数,捕获布尔值)
// useCapture 是指要在捕获阶段还是冒泡阶段触发,默认值 false 是冒泡阶段触发。
细节
// addEventListener 的事件尊许队列的先进先出特性。
btn.addEventListener('click',function(){})

// 同一事件多次添加同一个监听函数,该函数只会执行一次。
function fn1() {alert('123')}
target1.addEventListener('click', fn1)
target1.addEventListener('click', fn1)
// 同一事件(click),同一监听函数(fn1),上面只 alert 一次。

<div onclick='fn()'>div.onclick=function(){} 和 `div.addEventListener(‘click’,function()

<div onclick='fn()'>违反了 HTML 和 JS 的分离原则。

div.onclick=function(){} 不能添加个 onclick,前一个会被后一个覆盖。

div.addEventListener('click',function(){}) 可以添加多个 click,可以指定捕获还是冒泡触发,是整个 JavaScript 统一的监听函数接口。

事件传播

图示与语法

事件传播(propagation),捕获阶段(capture phase)、目标阶段(target phase)、冒泡阶段(bubbling phase)

DOM事件传播模型

grand1.addEventListener('click', function () {
    console.log('爷爷')
}, false)
parent1.addEventListener('click', function () {
    console.log('爸爸')
}, false)
child1.addEventListener('click', function () {
    console.log('儿子')
}, false)
// 捕获阶段:爷爷 -> 爸爸 -> 儿子
// 冒泡阶段:儿子 -> 爸爸 -> 爷爷
// 目标阶段:儿子是同时存在 捕获 和 冒泡 的,不分顺序。只按照代码顺序执行。

// 第3个参数是 false,儿子爸爸爷爷。
// 第3个参数是 true,爷爷爸爸儿子。
// 不传第3个useCapture参数,即默认是 false,在冒泡阶段触发函数。

目标阶段,不分顺序,只按照代码顺序执行。

child1.addEventListener('click', function () {
    console.log('儿子捕获')
}, true)
child1.addEventListener('click', function () {
    console.log('儿子冒泡')
}, false)
// 儿子捕获 - 儿子冒泡

child1.addEventListener('click', function () {
    console.log('儿子冒泡')
}, false)
child1.addEventListener('click', function () {
    console.log('儿子捕获')
}, true)
// 儿子冒泡 - 儿子捕获

阻止事件传播

// 事件捕获到 爸爸 后,就不再向 儿子 传播了。但是 爸爸 本身的函数还是能执行的。
parent1.addEventListener('click', function (event) {
    event.stopPropagation();
    console.log('爸爸')
}, true);

// 事件冒泡到 爸爸 后,就不再向 爷爷 传播了。但是 爸爸 本身的函数还是能执行的。
parent1.addEventListener('click', function (event) {
    event.stopPropagation();
    console.log('爸爸')
}, false);

事件的代理

利用冒泡,把儿子们的监听函数定义在爸爸上,就是事件的代理(delegation)。

<ul>
    <li></li>
    <li></li>
    <li></li>
</ul>
var ul = document.querySelector('ul');

ul.addEventListener('click', function (event) {
    if (event.target.tagName.toLowerCase() === 'li') {
        // some code
    }
});

如何做「点击其他地方关闭浮层」

需求

点击其他地方关闭浮层

点击按钮出现浮层,浮层和按钮不会冒泡到父元素,点击别处关闭浮层。

实现

HTML

<div id="wrapper">
    <button id="clickMe">点我</button>
    <div id="popover"><input type="checkbox">浮层</div>
</div>

方案一

// 监听按钮,点击显示浮层。
$(clickMe).on('click', function () {
    $(popover).show()
})
// 阻止 按钮+浮层 冒泡。
$(wrapper).on('click', function (e) {
    e.stopPropagation()
})
// 监听 document,隐藏浮层。
$(document).on('click', function () {
    $(popover).hide()
})

缺点:一直监听 document,如果页面每个按钮都添加一个 document 监听,太浪费内存。

方案一改进

// 监听按钮,点击显示浮层。
$(clickMe).on('click', function () {
    $(popover).show()
    // 监听一次 document,document 被点击后监听销毁,很节省内存。
    $(document).one('click', function () {
        $(popover).hide()
    })
})
// 阻止 按钮+浮层 冒泡。
$(wrapper).on('click', function (e) {
    e.stopPropagation()
})

方案二

$(clickMe).on('click', function () {
    $(popover).show()

    $(document).one('click', function () {
        $(popover).hide()
    })

})
// 把阻止冒泡去掉,为什么浮层点不出来?
// 不是应该浮层出现,才监听 document,不会触发 document 的点击么?
// 因为点击按钮后,目标阶段 触发 浮层显示 + document监听,然后在冒泡阶段把 document监听 执行了。

缺点:浮层点不出来

方案二改进

$(clickMe).on('click', function() {
  $(popover).show()

  setTimeout(function() {
    $(document).one('click', function() {
      $(popover).hide()
    })
  }, 0)
})
// 用 setTimeout 让 document监听 延迟执行。让 document监听 在冒泡阶段后才执行。