正在加载...

JavaScript 事件

公寓门边有一个门铃。按钮是 #bell,上面写着 press。下面的 #chime 还写着 silent。HTML 里没有 onclick 属性,所以 bell.onclick 一开始就是 null。
一个老陷阱:JavaScript 里 typeof null 是 "object"。所以 typeof bell.onclick 也是 "object",不是 "null",也不是 "undefined"。比较 bell.onclick === null 为真。
把函数放进 onclick 并不会马上响。文字一直是 silent,直到 bell.click() 执行槽里的那个函数。这个槽只放一个函数。再赋第二个,第一个就没了。一次点击不会两个都跑。addEventListener 是下一页;那里可以把监听叠在一起。这一页不调用它。
bell.onclick = null 把槽清空。之后的点击不再改文字。函数里面,this 就是这个按钮,所以 this.id 是 bell。按钮上的字仍是 press。页面脚本点击一次。门铃下面这一行是 dong / 1 / bell。这里不创建元素,也不写 CSS 属性。

按钮上只有一个槽

上一页用 createElement 做 li 节点,再用 append、prepend 和 insertBefore 放进去。这一页不造节点,也不读 children。再上一页在色卡上写了 style 和 classList。这一页不赋值 style。getElementById 只把按钮和段落交给你。新的东西是 onclick 属性:一个函数,或者 null。

槽里放着什么

按钮从 press 开始,下面是 silent。按顺序读每一行。除了页面那一行,每个片段都从这只新按钮开始。页面那一行是底下的演示,演示里不会赋 null。

📊 槽里放着什么

时刻 onclick 里有什么 门铃文字 容易混
任何赋值之前 null;typeof 是 "object" silent 把 typeof 读成 "null"
放入 ding 函数之后、click 之前 一个函数 silent 以为赋值本身就会响
用 ding 做了 click 之后 那个函数 ding 按钮文字变成 ding
dong 换掉 ding,然后一次 click dong 函数,不是 ding dong 两个函数都执行
onclick = null 再 click,新按钮 null;typeof 是 "object" silent 空槽还会响
页面脚本,一次 click 一个函数;this.id 是 bell dong 这一行以 silent 结尾

onclick 是元素上的属性。没有处理函数时,值是 null,而 typeof null 仍然是 "object"。想知道槽是空的,用 === null。赋值只是把函数存起来,不会调用它。bell.click() 发出一次点击,执行当时槽里的函数。按钮自己的文字不变。第二次赋值会盖掉这个属性。如果给第一个函数留了名字,那个对象还在内存里,但属性不再指向它。onclick = null 拿掉处理函数。HTML 的 onclick 属性占的是同一个槽;这个按钮没有那个属性。箭头函数放在这个槽里,不会把 this 设成按钮。这一页用的是普通 function。

槽一开始是空的

文件打开时只有按钮和段落。还没有赋值。这四条输出问的是属性、它的 typeof、门铃文字,以及按钮上的字。

安静的门铃

javascript
let bell = document.getElementById("bell");
let chime = document.getElementById("chime");
console.log(bell.onclick);
console.log(typeof bell.onclick);
console.log(chime.textContent);
console.log(bell.textContent);

输出是 null、object、silent 和 press。槽是空的。typeof 不会印出 null 这个词。按钮和段落仍显示标记里的字。

函数先等着

onclick = function ... 把函数存起来。函数体不会在这一行执行。门铃仍是 silent,属性也不再是 null。

赋值,先不响

javascript
let bell = document.getElementById("bell");
let chime = document.getElementById("chime");
bell.onclick = function () {
chime.textContent = "ding";
};
console.log(typeof bell.onclick);
console.log(chime.textContent);
console.log(bell.onclick === null);

输出是 function、silent 和 false。槽里能看到函数。段落没变,因为没有人调用这个函数。

click 执行槽里的那一个

bell.click() 执行当前的处理函数。门铃变成 ding。按钮文字仍是 press。点击之后,槽里还是一个函数。

点一次

javascript
let bell = document.getElementById("bell");
let chime = document.getElementById("chime");
bell.onclick = function () {
chime.textContent = "ding";
};
bell.click();
console.log(chime.textContent);
console.log(bell.textContent);
console.log(typeof bell.onclick);

输出是 ding、press 和 function。一次点击,一处改动,改的是段落。press 仍在按钮上。

第二个函数换掉第一个

这段再从新按钮开始。先存 ding,再把 dong 写上去,然后才有一次 click。两次赋值之间没有点击,所以这一轮门铃不会变成 ding。点击之后是 dong。bell.onclick === ding 为 false。bell.onclick === dong 为 true。

留下第二个

javascript
let bell = document.getElementById("bell");
let chime = document.getElementById("chime");
function ding() {
chime.textContent = "ding";
}
function dong() {
chime.textContent = "dong";
}
bell.onclick = ding;
bell.onclick = dong;
bell.click();
console.log(bell.onclick === ding);
console.log(bell.onclick === dong);
console.log(chime.textContent);

输出是 false、true 和 dong。名字 ding 仍指向一个函数。属性不再指向它。一次点击只跑了 dong。

null 把槽清空

在新按钮上跑这段。先赋一个函数,再在任何点击之前清掉。null 之后的 bell.click() 不会写出 dong。门铃仍是 silent。typeof 又是 object,因为值是 null。

清空槽

javascript
let bell = document.getElementById("bell");
let chime = document.getElementById("chime");
bell.onclick = function () {
chime.textContent = "dong";
};
bell.onclick = null;
bell.click();
console.log(bell.onclick);
console.log(typeof bell.onclick);
console.log(chime.textContent);

输出是 null、object 和 silent。本来会写 dong 的函数,在点击时不在槽里。下面的页面演示不会清空槽。

门铃下面的一行

脚本在 body 末尾,按钮和段落之后。它存一个函数,然后调用一次 click。函数里 taps 从 0 变成 1,门铃变成 dong,this.id 写进 bell.dataset.who。这一行把门铃文字、次数和这个 id 接在一起。它不写按钮文字。

页面演示 — 门铃

html
<!DOCTYPE html>
<html>
<head>
<title>events — page</title>
</head>
<body>
<button id="bell" type="button">press</button>
<p id="chime">silent</p>
<p id="line">…</p>
<script>
let bell = document.getElementById("bell");
let chime = document.getElementById("chime");
let taps = 0;
bell.onclick = function () {
taps = taps + 1;
chime.textContent = "dong";
bell.dataset.who = this.id;
};
bell.click();
document.getElementById("line").textContent =
chime.textContent + " / " + taps + " / " + bell.dataset.who;
</script>
</body>
</html>

这一行是 dong / 1 / bell。门铃是 dong,不是 silent。数字是 1,因为脚本只点了一次。最后一段是按钮的 id,来自处理函数里的 this,不是 press 这个词。按钮上仍是 press。下一页是事件监听。它的主要工具不是这一个 onclick 槽。

自己核五次

  1. 读 onclick、typeof bell.onclick、门铃文字和按钮文字。值是 null、object、silent 和 press。
  2. 赋上 ding 函数,先不要点击。typeof 是 function,门铃仍是 silent,onclick === null 为 false。
  3. 用这个 ding 函数调用 bell.click()。门铃是 ding,按钮是 press,typeof 是 function。
  4. 先赋 ding,再赋 dong,然后点一次。onclick === ding 为 false,onclick === dong 为 true,门铃是 dong。
  5. 在新按钮上赋一个函数,把 onclick 设为 null,再点击。值是 null、object 和 silent。页面这一行是 dong / 1 / bell。

这个门铃会在哪里骗你

按钮上仍是 press。槽里放错了函数,或者槽是空的。

  • 值是 null 时,把 typeof bell.onclick 读成 "null"
  • 以为赋值本身就会把门铃改成 ding
  • 第二次赋值之后,以为一次点击会让 ding 和 dong 都跑
  • 以为按钮文字会变成 ding
  • 以为 onclick = null 之后,click 还会改门铃
  • 用箭头函数,还以为 this.id 是 bell
  • 在这里读 event.target;这些函数没有参数,事件对象是后面的页
  • 在这里调用 addEventListener;下一页才把监听叠起来
  • 用 createElement 做按钮;那是上一页的事
  • 把这一行读成 silent / 0 / press

带走这些

  • 没有处理函数时,bell.onclick 是 null,typeof bell.onclick 是 object
  • 赋上函数之后,门铃要到 click 才离开 silent
  • 用 ding 函数调用 bell.click(),门铃变成 ding,按钮仍是 press
  • 第二次赋值换掉第一次,所以一次点击只跑 dong
  • 在新按钮上先 onclick = null 再 click,门铃仍是 silent
  • 页面这一行是 dong / 1 / bell,处理函数里的 this.id 是 bell

🧠 测试您的知识

准备开始

测试您的知识

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

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

📝 说明

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