正在加载...

事件对象

博物馆的墙上有一块牌子。外层盒子是 #wall。里面的 #plaque 放着一个链接 #seal。链接上的字是 open,href 是 /wing。墙下面的 #slip 还写着 blank。
浏览器交给函数一个对象。参数名叫 event。event.type 是事件的种类。一次点击让这个字符串变成 click。event.target 是点击落下去的那个元素。event.currentTarget 是这个监听器所在的元素。只有点击和监听器在同一个元素上时,两者才相同。
印章是链接。preventDefault 取消跳到 /wing。调用之后,event.defaultPrevented 是 true。不调用的话,标志保持 false。点击接着往外走:印章,牌子,墙。牌子上的 stopPropagation 把这段路程切断,墙上的监听器不会运行。
页面脚本对印章点击一次。牌子下面这一行是 click / seal / plaque / true / quiet。这里不写 onclick,不调用 removeEventListener,也不创建元素。

浏览器传进来的对象

上一页是衣帽钩。wool 和 silk 没有参数,读不到这个对象。那一行是 wool silk / 2 / peg。门铃那一页给 onclick 赋值,还读了 this。这一页不这样做。getElementById 只把墙、牌子、印章和纸条交给你。
addEventListener 仍然只是把函数存起来。对象是在点击运行函数时到的,不是在添加监听器的那一行。currentTarget 要在函数里面读。函数返回之后,这个属性是 null。

一次点击报告什么

纸条从 blank 开始。链接文字一直是 open。按顺序读这些行。每一段都从这面新墙开始,除了页面那一行,那是底部的演示。演示不调用 removeEventListener。

📊 一次点击报告什么

时刻 读什么 纸条文字 容易混
监听器在印章上,seal.click(),读类型 event.type click 把 id 当成类型
监听器在牌子上,点击印章,读 target event.target.id seal 去读 currentTarget
同一次点击,读 currentTarget event.currentTarget.id plaque 去读 target
印章上调用 preventDefault,再读标志 event.defaultPrevented true 以为链接还会离开
没有 preventDefault event.defaultPrevented false 以为每个监听器都会取消链接
牌子上 stopPropagation,墙也在听 marks 拼起来 plaque 期待 plaque wall
页面脚本,一次 click 这一行 click / seal / plaque / true / quiet 行尾是 wall

点在印章上,是同一个对象。路上的每个监听器看到的都是它。type 不变。target 不变。currentTarget 会变:它是当前正在运行的那个监听器所在的元素。
preventDefault 说的是浏览器的默认动作。对这个链接,默认动作是前往 /wing。这次调用不会把监听器从列表里拿掉。stopPropagation 说的是路程。它不取消链接。调用其中一个,并不等于调用另一个。
join(" ") 只在 marks 里已经有的词之间放一个空格。这一页不拼接空列表。纸条从 blank 这个词开始,只在监听器里面改,或者在点击之后由页面脚本改。

类型

监听器在印章上。函数把参数叫做 event,把 event.type 写进纸条。然后运行 seal.click()。

读取类型

javascript
let seal = document.getElementById("seal");
let slip = document.getElementById("slip");
function readType(event) {
slip.textContent = event.type;
}
seal.addEventListener("click", readType);
seal.click();
console.log(slip.textContent);

输出是 click。类型是事件的种类,不是 id,也不是 open 这个词。

点击落在哪个元素上

一面新墙。监听器在牌子上,不在印章上。点击仍然落在印章上。event.target 就是那个元素,所以 event.target.id 是 seal。

读取 target

javascript
let plaque = document.getElementById("plaque");
let seal = document.getElementById("seal");
let slip = document.getElementById("slip");
function readTarget(event) {
slip.textContent = event.target.id;
}
plaque.addEventListener("click", readTarget);
seal.click();
console.log(slip.textContent);

输出是 seal。监听器所在的元素是牌子。target 不报告牌子。它报告点击下面的那个元素。

监听器坐在哪里

还是这面新墙,监听器仍在牌子上,点击仍在印章上。纸条写下两个 id,先是 target,再是 currentTarget。

读取两个 id

javascript
let plaque = document.getElementById("plaque");
let seal = document.getElementById("seal");
let slip = document.getElementById("slip");
function readWhere(event) {
slip.textContent = event.target.id + " " + event.currentTarget.id;
}
plaque.addEventListener("click", readWhere);
seal.click();
console.log(slip.textContent);

输出是 seal plaque。第一个词是点击。第二个词是这个监听器所在的元素。把监听器放在印章上再点印章:两个 id 都是 seal,因为点击和监听器是同一个元素。这里的读法用的是牌子,所以两个 id 不同。currentTarget 要在函数里面读。函数返回之后它是 null。

取消链接

印章的 href 是 /wing。这次点击的默认动作是跟着链接走。preventDefault 取消这个动作。它不移除监听器。调用之后马上,event.defaultPrevented 是 true。

取消链接

javascript
let seal = document.getElementById("seal");
let slip = document.getElementById("slip");
function hold(event) {
event.preventDefault();
slip.textContent = String(event.defaultPrevented);
}
seal.addEventListener("click", hold);
seal.click();
console.log(slip.textContent);

输出是 true。去掉 preventDefault,同样的读取是 false。纸条会写成 false,链接的默认动作还在。stopPropagation 不会设置这个标志。

点击往外走

印章在牌子里,牌子在墙里。点印章会先到达牌子的监听器,再到墙的监听器,除非路程被切断。牌子上的 stopPropagation 切断它。墙的函数不运行。preventDefault 不切断路程。

切断路程

javascript
let wall = document.getElementById("wall");
let plaque = document.getElementById("plaque");
let seal = document.getElementById("seal");
let slip = document.getElementById("slip");
let marks = [];
function onPlaque(event) {
marks.push("plaque");
event.stopPropagation();
}
function onWall() {
marks.push("wall");
}
plaque.addEventListener("click", onPlaque);
wall.addEventListener("click", onWall);
seal.click();
slip.textContent = marks.join(" ");
console.log(slip.textContent);

输出是 plaque。wall 这个词不会进 marks。没有 stopPropagation 的话,同一次点击会写成 plaque wall。下面的演示既切断路程,也取消链接。

牌子下面的一行

脚本在 body 末尾,墙和段落已经在页面上。印章的监听器调用 preventDefault。牌子的监听器调用 stopPropagation,然后记下类型、target 的 id、currentTarget 的 id 和标志。墙的监听器本来会把 heard 设成 wall,可路程已经切断,所以 heard 保持 quiet。这一行写在 seal.click() 之后,墙已经有过机会。

页面演示 — 博物馆牌子

html
<!DOCTYPE html>
<html>
<head>
<title>event-object — page</title>
</head>
<body>
<div id="wall">
<div id="plaque">
<a id="seal" href="/wing">open</a>
</div>
</div>
<p id="slip">blank</p>
<p id="line">…</p>
<script>
let wall = document.getElementById("wall");
let plaque = document.getElementById("plaque");
let seal = document.getElementById("seal");
let slip = document.getElementById("slip");
let kind = "";
let from = "";
let on = "";
let held = "";
let heard = "quiet";
seal.addEventListener("click", function (event) {
event.preventDefault();
});
plaque.addEventListener("click", function (event) {
event.stopPropagation();
kind = event.type;
from = event.target.id;
on = event.currentTarget.id;
held = String(event.defaultPrevented);
});
wall.addEventListener("click", function () {
heard = "wall";
});
seal.click();
slip.textContent = kind + " " + from + " " + on;
document.getElementById("line").textContent =
kind + " / " + from + " / " + on + " / " + held + " / " + heard;
</script>
</body>
</html>

这一行是 click / seal / plaque / true / quiet。类型是 click。target 的 id 是 seal。currentTarget 的 id 是 plaque,因为那个监听器在牌子上。标志是 true,因为印章的监听器在牌子读取之前调用了 preventDefault。最后一个词是 quiet,不是 wall,因为 stopPropagation 在牌子上运行了。open 这段文字不在行里。纸条变成 click seal plaque。下一页是事件委托。印章本来就在标记里。这一页不在监听器之后再添加子元素。

牌子旁的五次核对

  1. 把监听器放在印章上再点击。纸条是 click。
  2. 把监听器放在牌子上,点击印章,读取 event.target.id。纸条是 seal。
  3. 同一个监听器写下两个 id。纸条是 seal plaque。监听器若在印章自己身上,会写成 seal seal。
  4. 调用 preventDefault,再读 event.defaultPrevented。纸条是 true。不调用则是 false。
  5. 在牌子上调用 stopPropagation,墙也在听。marks 拼成 plaque。读这一行:click / seal / plaque / true / quiet。

这块牌子会在哪里骗人

链接上仍然是 open。纸条不是链接文字。

  • 以为函数参数是印章元素,而不是事件对象
  • 想要监听器所在的元素时,却去读 event.target
  • 想要点击下面的元素时,却去读 event.currentTarget
  • 以为 preventDefault 会拆掉监听器,像衣帽钩上的 removeEventListener
  • 以为 stopPropagation 会取消跳到 /wing
  • 以为牌子上调用了 stopPropagation 之后,墙的监听器还会运行
  • 函数返回之后再读 event.currentTarget,还期待 plaque
  • 去写 onclick,或去读 this 和 typeof null;那是门铃那一页
  • 用 createElement 造链接;那是更早的一页
  • 把这一行读成以 wall 结尾,或读成 wool silk / 2 / peg

留下的几点

  • 点击之后 event.type 是 click
  • 监听器在牌子上、点击在印章上:event.target.id 是 seal
  • 同一次点击,event.currentTarget.id 是 plaque
  • 监听器在印章上时,target 和 currentTarget 都是 seal
  • preventDefault 让 event.defaultPrevented 变成 true,不调用则保持 false
  • 牌子上的 stopPropagation 让墙的监听器不运行
  • 页面这一行是 click / seal / plaque / true / quiet

🧠 测试您的知识

准备开始

测试您的知识

通过这个互动测验挑战自己,看看你对这个主题的理解程度如何

❓
8
问题
🎯
70%
及格要求
♾️
∞
时间
🔄
∞
尝试次数

📝 说明

  • 仔细阅读每个问题
  • 为每个问题选择最佳答案
  • 您可以随时重新参加测验
  • 您的进度将显示在顶部