事件对象
博物馆的墙上有一块牌子。外层盒子是 #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()。
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。
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。
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。
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 不切断路程。
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() 之后,墙已经有过机会。
<!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。下一页是事件委托。印章本来就在标记里。这一页不在监听器之后再添加子元素。
牌子旁的五次核对
- 把监听器放在印章上再点击。纸条是 click。
- 把监听器放在牌子上,点击印章,读取
event.target.id。纸条是 seal。 - 同一个监听器写下两个 id。纸条是 seal plaque。监听器若在印章自己身上,会写成 seal seal。
- 调用
preventDefault,再读event.defaultPrevented。纸条是 true。不调用则是 false。 - 在牌子上调用
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
🧠 测试您的知识
测试您的知识
通过这个互动测验挑战自己,看看你对这个主题的理解程度如何
📝 说明
- 仔细阅读每个问题
- 为每个问题选择最佳答案
- 您可以随时重新参加测验
- 您的进度将显示在顶部