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、门铃文字,以及按钮上的字。
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。
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。点击之后,槽里还是一个函数。
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。
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。
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 接在一起。它不写按钮文字。
<!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 槽。
自己核五次
- 读
onclick、typeof bell.onclick、门铃文字和按钮文字。值是 null、object、silent 和 press。 - 赋上 ding 函数,先不要点击。
typeof是 function,门铃仍是 silent,onclick === null为 false。 - 用这个 ding 函数调用
bell.click()。门铃是 ding,按钮是 press,typeof是 function。 - 先赋 ding,再赋 dong,然后点一次。
onclick === ding为 false,onclick === dong为 true,门铃是 dong。 - 在新按钮上赋一个函数,把
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
🧠 测试您的知识
测试您的知识
通过这个互动测验挑战自己,看看你对这个主题的理解程度如何
📝 说明
- 仔细阅读每个问题
- 为每个问题选择最佳答案
- 您可以随时重新参加测验
- 您的进度将显示在顶部