JavaScript 事件传播机制

这是我参与更文挑战的第 6 天,活动详情查看:更文挑战javascript

JavaScript 采用异步事件驱动编程模型,与 HTML 的交互是经过事件实现的。html

当咱们执行了特定的事件时,如在一个按钮上监听了 click 点击事件,当你按下按钮时,它将触发你给定的事件句柄(又称事件处理函数,其中执行一些 JS 语句)。但这个触发过程是怎样的呢?下面咱们来经过一些问题来看看今天要讲的事件的传播机制。java

你能够在这👉测试效果,配合下文进行阅读。编程

什么是事件流?

事件流是在网页上接收事件的顺序。当您单击嵌套在其余各类元素中的元素时,在您单击目标元素后,它会先触发目标元素上的 click 事件,在一层层往上触发事件,最终到达最顶层的 window 对象,这是浏览器默认的事件冒泡行为(IE 9+)。事件流有两种方式:浏览器

  • 从外到内(事件捕获)
  • 从内到外(事件冒泡)

简单示例:如下问题都用该结构markdown

<ul id="emoji">
  <li id="smile">😀</li>
  <li>😁</li>
  <li>😂</li>
  <li>🤣</li>
  <li>😃</li>
  <li>😄</li>
</ul>

<script> emoji.onclick = function (e) { console.log(e.target.innerHTML) } </script>
复制代码

尝试一下使用上面示例,你会发现,单击每一个 <li> 标签,你会获得其中的每一个 emoji异步

能获得这样的结果是由于,它使用了事件冒泡一个最重要的原理:事件委托(event delegation),后面会介绍到。函数

IE 中的事件流

这里要介绍一下 IE 6-8 中的事件流,IE 6-8 中的事件流和标准的 DOM 事件流有(独)所(领)不(风)同(骚),IE 的事件流只支持事件冒泡,不支持事件捕获。oop

IE 9 以前,它也不支持 addEventListener,也就不能使用其第三个参数来表示是冒泡仍是捕获,它提供了非标准的事件侦听器 attachEvent() 方法。post

在这个模型中,事件对象使用 event.srcElement 属性,而不是 event.target,但二者的效果相同。

emoji.attachEvent('onclick', function (e) {
  var target = e.target || e.srcElement
  alert(target.innerHTML)
})
复制代码

什么是事件冒泡?

事件冒泡是事件传播的一种,其中事件首先在最内部的目标元素上触发,而后在同一嵌套层次结构中依次在目标元素的祖先(父代)上触发,直到到达最外层的 DOM 元素。

emoji.onclick = function () {
  alert(11)
}

smile.onclick = function () {
  alert(22)
}

// 11
// 22
复制代码

什么是事件捕获?

事件捕获也是事件传播的一种,其中事件首先被最外面的元素捕获,而后在同一嵌套层次结构中依次触发目标元素的后代(子元素),直到到达最里面的 DOM 元素。

当一个事件发生之后,它会在不一样的 DOM 节点之间传播(propagation)。DOM 事件标准描述了事件传播的 3 个阶段:

  • 捕获阶段(capture phase):事件从 Window 对象向下传递到目标节点。

  • 目标阶段(target phase):事件到达目标元素。

  • 冒泡阶段(bubbling phase):事件从元素上开始冒泡。

在捕获阶段中,事件从祖先元素向下传播到目标元素。当事件达到目标元素后,冒泡才开始。

同时,这三个阶段的传播模型,会使得一个事件在多个节点上触发。

由于浏览器默认是从事件冒泡开始,咱们看不到事件捕获,因此想要测试事件捕获咱们须要使用到 addEventListener 方法,addEventListener 用于添加事件句柄、注册监听器,参数三还能够指定在捕获阶段仍是冒泡阶段触发事件:

  • false(默认值)—— 在冒泡阶段开始触发事件;
  • true —— 在捕获阶段开始触发事件。
smile.addEventListener('click', function(e) {
  console.log(e.target.innerHTML)
})

emoji.addEventListener('click', function(e) {
  alert('Emoji List')
}, true)

document.addEventListener('click', function () {
  alert('document')
})

window.addEventListener('click', function () {
  alert('window')
})

// 点击 smile 时,将依次输出 'Emoji List' -> 😀 -> 'document' -> 'window'
// 因为将 list 设置为捕获阶段开始,因此先触发,在触发 😀,而后在依次往上触发。
复制代码

事件委托

事件委托,也称为事件代理,是指将本要添加在自身的事件,添加到别人身上。经过冒泡的原理,将事件添加到父级,触发执行效果。

emoji.addEventListener('click', function(e) {
  console.log(e.target.innerHTML)
})
复制代码

好处

  • 节省内存占用,减小事件注册
  • 新增子对象时,无需再对其进行事件绑定

阻止事件传播

使用 event.stopPropagation() 方法用于阻止捕获和冒泡阶段中当前事件在 DOM 中的进一步传播。

function handler(e) { 
  e.stopPropagation()
}
复制代码

取消默认事件

使用 event.preventDefault() 方法取消浏览器对当前事件的默认行为,例如:

  • 表单元素中使用,它将阻止其提交
  • 锚元素中使用,它将阻止其导航
  • 上下文菜单中使用,它将阻止其显示或隐藏
function handler(e) { 
  e.preventDefault()
}
复制代码

您还能够在事件对象中使用 event.defaultPrevented 查看是否使用了 event.preventDefault() 方法。

它返回一个布尔值用来代表是否在特定元素中调用了event.preventDefault()

function handler(e) {
  e.preventDefault()
  console.log(e.defaultPrevented) // true or false
}
复制代码

return false 用法涉及哪些步骤

事件处理程序中的 return false 语句执行如下步骤:

  • 首先,它中止了浏览器的默认操做或行为。
  • 它防止事件传播 DOM
  • 中止回调执行,并在调用时当即返回。

理解事件传播须要注意

  • 事件自己在传播,而不是绑定在事件上的方法会传播;
  • 并不是全部的事件都会传播,像 focusblur 事件就不传播,mouseentermouseleave 事件也不会传播;
  • 您应该知道哪些事件有默认行为,并在须要时阻止它。
  • 若是是使用 on<event> 分配的处理程序,那么你可使用 return false 来阻止默认行为。
  • 事件处理程序返回的值一般会被忽略。惟一的例外是从使用 on<event> 分配的处理程序中返回的 return false。在全部其余状况下,return 值都会被忽略。而且,返回 true 没有意义。
  • 若是是使用 addEventListener,你可使用 return false,但经常使用的是使用 event 对象中 e.preventDefault() 方法来阻止默认行为的发生。
  • return false 作着与 event.stopPropagation()event.preventDefault() 相似的工做,但它们之间毫无关联。
相关文章
相关标签/搜索