Skip to content

DOM 事件

"IE8 是最后一个还在使用自己的事件系统的浏览器。"

在浏览器发生的事情统称为事件, 比如点击事件, 鼠标移动事件, 键盘事件, 点击事件, 请求事件,加载完成事件。

事件对象(event)存储了事件触发的各种状态包括事件触发的主体对象,事件类型,事件触发的位置等等。event在现代浏览器中即事件函数的第一个形参。其 type 属性, 是当前事件的名字。

javascript
window.onclick = function(e) {
    console.log(e.type) //"click"
}

事件绑定

绑定点击事件可以用(对象.on+事件名)的方式绑定事件,然后挂载上处理函数接受事件对象来进行处理。

我们称on这种方式绑定的事件是DOM0 级事件

  • 优点: 兼容所有的浏览器,因为它是最原始的事件,浏览器设计出来的那一天事件都是这么绑定的。

  • 缺点: 这种绑定事件的方法,一个对象只能绑定一个事件,不能绑定多个事件,否则会覆盖。

在很久之前事件直接写在标签内,现在的原生语法已不推荐这样做:

html
<span onclick="foo()">

</span>

事件监听

不直接写明处理的函数来响应事件,只需要做事件监听addEventListener。事件监听在节点对象的原型上。

javascript
document.addEventListener("click", function(e) {
    console.log(e)
})

生成的事件对象和事件绑定生成的对象是同一个。

javascript
window.onclick = function(e) {
    window._a = e
}
window.addEventListener("click", function(e) {
    window._b = e
})
//点击触发一次事件
_a === _b //true
  • 事件监听接受 3 个参数:事件类型, 触发的回调函数, 是否事件冒泡.

  • 事件类型不需要加 on+事件类型,直接事件类型即可.

  • 一次可以绑定多个事件, 相互之间不影响.并且触发顺序就是绑定顺序

    javascript
    window.addEventListener("click", function() {
        console.log(1)
    })
    window.addEventListener("click", function() {
        console.log(2)
    })
    //点击 打印1 2
  • 事件监听处理函数的 this 指向当前调用(触发)事件的主体对象

  • 取消事件监听用对应的方式,removeEventListener,传入的参数要和添加的参数完全一致

我们称这种事件的监听方式是DOM2 级事件

javascript
function handle(e) {
    if (e.target.tagName.toLowerCase() === "li") {
        console.log(1)
    }
}
let li = document.querySelector("li")
li.addEventListener("click", handle)
// 点击li 触发
li.removeEventListener("click", handle)
// 点击就没有反应了

特殊的: 匿名事件处理函数是没法取消绑定事件的

在低版本的 IE 浏览器中使用: attachEventdetachEvent

事件委托

如果有大量 dom 对象需要绑定事件,并且事件的处理函数是同一个的时候,可以通过监听公共父元素的形式监听事件。

javascript
let list = document.querySelector(".list")
let ali = document.querySelectorAll(".list>li")

function fn(e) {
    console.log(e.target) // 这是事件源(事件对象的触发主体对象)
}
list.addEventListener("click", fn)

在事件处理函数 fn 中,可以通过判断事件源e.target,从而达到事件处理的目的.。

实际运用中还可以结合事件冒泡使用。设计模式中称为代理模式

冒泡和捕获

事件捕获(event capturing): 当鼠标点击或者触发 dom 事件时(被触发 dom 事件的这个元素被叫作事件源),浏览器会从根节点 =>事件源(由外到内)进行事件传播。

事件冒泡(dubbed bubbling): 事件源 =>根节点(由内到外)进行事件传播。

默认行为

默认行为: 指的是浏览器的默认行为。

有时候我们需要阻止默认行为,使用e.preventDefault(),这句不兼容,IE8 以下的浏览器会不生效。在 IE678 的浏览器阻止默认行为的方法:e.returnValue = false

阻止默认行为

阻止右键菜单事件:

javascript
document.oncontextmenu = function(e) {
    // 打开右键菜单的行为
    console.log("右键点击触发")
    e.preventDefault() // 这句阻止了打开右键菜单的行为。
    // e.returnValue = false //IE678的浏览器的操作
}

当我们使用 DOM0 级事件的时候,可以使用return false来实现阻止默认行为,并且结束后续代码的执行。

注意:

  • onmousedown点击事件阻止不了鼠标右键显示浏览器菜单, onmousedown能触发右键的事件,但是右键的事件是oncontextmenu
  • onmousemove鼠标移动触发后的默认会使得onmousedown onmouseup在很短一段时间内无法触发,实际应用中需要清除这个行为。

阻止事件冒泡

javascript
if (e.stopPropagation) {
    e.stopPropagation()
} else {
    e.cancelBubbse = true
}

事件对象

不管是 DOM0 级事件还是 DOM2 级事件都可以获取一个event对象.

这个对象包含了事件的相关信息. 包括触发事件的元素,事件类型等.

所有浏览器都有事件对象但是支持方式不同,存在兼容性问题.

javascript
window.event //低版本ie
属性名含义
bubbles布尔型true 表示冒泡,false 表示不冒泡
cancelable布尔型是否可以取消事件的默认行为
currentTarget元素对象其事件处理程序当前正在处理事件的那个元素
eventPhase数值1 表示捕获阶段;2 表示“目标阶段”;3:冒泡阶段
preventDefault函数执行,就取消事件默认行为,当 cancelable 为 true 才行
stopImmediatePropagation函数取消后续捕获或冒泡。阻止任何事件处理程序
stopPropagation函数取消后续捕获或冒泡。
target元素对象事件的目标
trusted布尔型是否是浏览器生成的事件
type字符串被触发的事件的类型

在事件回调中,this 等同于 currentTarget,而 target 只是包含事件的实际目标。如果事件处理函数正好在点击事件触发的对象中,这几个东西是同一个。

如果是attachEvent添加的 event 则有些不同。但那是 ie 浏览器了。

常用事件

所有的事件见Event - Web API 接口参考 | MDN (mozilla.org)

在 ts 中需要指定明确的 event 以及其 target 类型这很重要

鼠标事件

双击会触发两次单击 单击也会继而触发按下抬起

即使是鼠标事件 它的 e.shiftKey 和 e.ctrlKey 也能判断热键。

移入和移出事件有 2 套事件分别是默认冒泡和默认不冒泡的,

实现拖拽时,移动有关的鼠标事件一定要去除冒泡以及默认事件,以避免鼠标抬起后 DOM 依然跟随鼠标。

input 的默认行为:点击内部和外部可以触发它的得到和失去焦点事件。

表单元素的默认行为:会自动捕获按钮的点击来触发 submit。

键盘事件

如果被绑定到具体标签上,则需要聚焦到当前目标,没有聚焦的不能触发,一般写到 input 标签中或者 document 中。input 标签的键盘事件推荐使用oninput

keypress 在 ie 和 edge 浏览器中只有回车空格触发,且其他浏览器中(alt ctrl shift等不会输入的)是不会触发keypress的。

表单元素的默认行为:表单里含有 type=submit 的控件时回车会触发 submit

load 事件

当页面加载完成的load事件,比如 window,img,script 的加载都会触发load事件

javascript
window.onload = function() {
    console.log("页面加载成功")
}

滚轮事件

onscroll 滚动事件是监听浏览器的滚动条,只要滚动条的滑块发生变化,就会触发。(火狐需要另外处理)

javascript
window.onscroll = function(e) {
    //监听滚动位值发生变化
    console.log("scrolling")
    e.preventDefault() //这里是无法阻止滚动事件的
}
javascript
window.onmousewheel = function(e) {
    //监听鼠标中键滑轮滚动,不监听滚动条拖拽
    console.log("scrolling~")
    e.preventDefault() //这里阻止了默认的滚动事件
    //火狐用DOMMouseScroll也只支持火狐。并且只支持DOM2级事件。
}

调整页面大小滚动页面 (resize, scroll

对于这种高频触发的事件经常需要节流 / 防抖

节流:给事件设置只能触发一次,每隔一段时间重置使用次数。
防抖:给事件设置冷却时间,冷却时间未到时触发会重置冷却时间。

drag 事件

拖拉(drag)指的是,用户在某个对象上按下鼠标键不放,拖动它到另一个位置,然后释放鼠标键,将该对象放在那里。

为了让元素节点可拖拉,可以将该节点的draggable属性设为true

html
<div draggable="true">
 此区域可拖拉
</div>

当元素节点或选中的文本被拖拉时,就会持续触发拖拉事件,包括以下一些事件。(火狐需要另外处理)

  • drag:拖拉过程中,在被拖拉的节点上持续触发
  • dragstart:用户开始拖拉时,在被拖拉的节点上触发
  • dragend:拖拉结束时(释放鼠标键或按下 ESC 键)在被拖拉的节点上触发。不管拖拉是否跨窗口,或者中途被取消,总是会触发的
  • dragenter:拖拉进入当前节点时,在当前节点上触发一次
  • dragover:拖拉到当前节点上方时,在当前节点上持续触发。只要没有离开这个节点会持续触发,这个事件默认会重置拖拽元素,需要阻止默认行为
  • dragleave:拖拉操作离开当前节点范围时,在当前节点上触发
  • drop:被拖拉的节点或选中的文本,释放到目标节点时,在目标节点上触发。注意,如果当前节点不允许drop,即使在该节点上方松开鼠标键,也不会触发该事件。如果 ESC 取消这个操作,也不会触发该事件

如果拖拽是含有文件,可以在 event.dataTransfer.files 中获取,见 WebAPI 中的 FileList 对象。

MutationObserver

MutationObserver 代替了旧的 Mutation Event 功能,可以监视 DOM 树的修改。

javascript
const observer = new MutationObserver((mutationsList, obs) => {
    console.dir(mutationsList)
})

observer.observe(dom, {
    attributes: true,
    childList: true,
    subtree: true,
})

事件派发

Event 可以简单声明事件,用 EventTarget 类(DOM 的父父父类)的 dispatchEvent 方法去派发

javascript
let event = new Event("click")
window.dispatchEvent(event) //"click"

事件名还可以自定义,自定义的事件可以用 addEventListener 来绑定

除了 Event,CustomEvent 也可以,还能携带自定义数据。

javascript
let xx = new Event("xx", {
    bubbles: true,
    cancelable: false,
})

let xx = new CustomEvent("xx", {
    detail: {
        msg: "你好",
    },
})

在鼠标事件中 event.detail 默认会显示当前事件已经触发的次数。
如果使用 CustomEvent 携带自定义数据 detail 触发后则会让原本记录的触发次数的功能失效

给某些 html 原生标签派发默认事件可能需要MouseEvent