事件处理程序
事件是某事发生的信号,所有DOM节点都生成这样的信号。
为了对事件做出响应,分配一个事件处理程序 —— 一个在事件发生时运行的JavaScript函数。
on<event>
事件处理程序fn可以设置在HTML标签中名为on<event>的特性(attribute)中,如下
<input type="button" value="Click me" onclick="fn()">
<script>
function fn(){
alert('点击了button')
}
</script>
也可以使用DOM属性(property)on<event>来分配处理程序,例如:
<input id="elem" type="button" value="Click me - dom">
<script>
elem.onclick = fn // 注意是赋值,而不是调用函数
function fn(){
alert('点击了button')
}
</script>
以上两种方式本质上相同,如果处理程序通过HTML特性(attribute)分配,浏览器使用特性的内容创建一个新的函数,并将这个函数写入DOM属性
addEventListener
上述分配处理程序的方式的根本问题是 —— 我们不能为一个事件分配多个处理程序。因此可以用以下方式
element.addEventListener(event, handler[, options]);
event
事件名,例如:"click"。
handler
处理程序。
options
具有以下属性的附加可选对象:
once:如果为 true,那么会在被触发后自动删除监听器。
capture:事件处理的阶段, 冒泡和捕获 。由于历史原因,options 也可以是 false/true,它与 {capture: false/true} 相同。
passive:如果为 true,那么处理程序将不会调用 preventDefault()
总结
3种分配事件监听函数的方式
- HTML (attribute)
onclick="..." - DOM (property)
element.onclick = fn - 方法 (method)
element.addEventListener(event, handler[, phase])
无论如何分类处理程序 —— 它都会将获得一个事件对象作为第一个参数。该对象包含有关所发生事件的详细信息。
事件对象
当事件发生时,浏览器会创建一个event对象,将详细信息放入其中,并将其作为参数传递给处理程序.
event 对象的一些属性:
event.type 事件类型,如 "click"。
event.currentTarget 处理事件的元素(程序员监听的元素),与 this 相同
event.target 实际操作的元素
事件冒泡和事件捕获
W3C标准规定浏览器事件传播有3个阶段
- 捕获阶段:事件从Window向下走进元素
- 目标阶段:事件到达目标元素
- 冒泡阶段:事件从元素上开始上升
下图表示了在表格中点击<td>事件的流动,途中调用处理程序
先捕获:按照爷爷=>爸爸=>儿子的顺序查找是否有事件监听函数,如果有就执行,直到目标元素
再冒泡(这一过程可停止):事件从目标元素开始向上冒泡,直到<html>,再到document对象,有些事件甚至会到达window,会调用路径上所有处理程序
冒泡
冒泡的原理是,当一个事件发生在一个元素上,它会首先运行该元素上的处理程序,然后运行其父元素上的处理程序,一直向上到其它祖先上的处理程序
停止冒泡的方法是 event.stopPropagation()
注意区分
event.target和event.currentTarget:currentTarget是被监听的元素,target是实际上被操作的元素 示例代码
捕获
为了在捕获阶段捕获事件,需要将事件监听函数的capture选项(或第三个选项)设置为true
elem.addEventListener(..., {capture: true})
// 或者,用 {capture: true} 的别名 "true"
elem.addEventListener(..., true)
代码示例
点击最里面的圆圈,先捕获再冒泡,冒泡到第三个div之后会被阻止
事件委托
如果需要给很多相似的元素添加相同的处理,可以借助捕获和冒泡实现事件委托模式,具体实现方式:
- 在祖先元素上分配一个监听函数
- 在监听函数中检查事件源元素
event.target - 如果事件发生在我们感兴趣的元素内,就处理该事件
优点包括:
- 不需要添加许多处理程序,节约内存
- 能够监听动态元素 代码示例
<div id="div1"></div> <script> setTimeout(()=>{ const button = document.createElement('button') button.textContent = 'click 1' div1.appendChild(button) },1000) div1.addEventListener('click',(e)=>{ if(e.target.tagName.toLowerCase() === 'button'){ console.log('button 被 click')} }) </script>
封装事件委托
要求
写出这样一个函数 on('click', '#testDiv', 'li', fn)
当用户点击 #testDiv 里的 li 元素时,调用 fn 函数
要求用到事件委托
答案1
判断target是否匹配li
上面这个答案其实有bug(button包括span)
答案2
递归判断target / target的爸爸 / target的爷爷
浏览器默认行为
许多事件会自动触发浏览器执行某些行为。
例如:
- 点击一个链接 —— 触发导航(navigation)到该 URL。
- 点击表单的提交按钮 —— 触发提交到服务器的行为。
- 在文本上按下鼠标按钮并移动 —— 选中文本。
如果我们使用 JavaScript 处理一个事件,那么我们通常不希望发生相应的浏览器行为。而是想要实现其他行为进行替代。
阻止浏览器默认行为
-
主流的方式是使用 event 对象。有一个
event.preventDefault()方法。 -
如果处理程序是使用
on<event>分配的,那返回 false 也同样有效。<a href="/" onclick="return false">Click here</a> or <a href="/" onclick="event.preventDefault()">here</a>
自定义事件
浏览器自带事件 developer.mozilla.org/zh-CN/docs/…
开发者可以自定义事件
new CustomEvent(type)
new CustomEvent(type, options)
- dispatchEvent派发事件
dispatchEvent(event)
示例如下:
<div id='container'>
<button id="btn">点击触发自定义事件</button>
</div>
<script>
btn.addEventListener('click',()=>{
// 创建自定义事件
const event = new CustomEvent('amber',{
detail:{name:'amber',age:18},
bubbles:true // chrome默认不冒泡
})
btn.dispatchEvent(event) // 触发自定义事件
})
container.addEventListener('amber',(e)=>{
console.log(e.detail)
})
</script>
Javascript和DOM事件的关系
支持,也不支持。
DOM 事件不属于 JS 的功能,属于浏览器提供的 DOM 的功能
JS 只是调用了 DOM 提供的 addEventListener 而已