事件
这是什么:事件监听 = 用 addEventListener 给元素注册一个函数,等某个用户动作(点击、输入)发生时就执行它。
为什么用:上一章的按钮已经会响应点击了,但 onclick 属性把行为写死在 HTML 里。更专业的做法是事件监听:JS 主动"蹲守",用户一动作就出手。
如何用:给按钮加上 addEventListener('click', ...),在页面里点「▶ 运行」再点按钮看效果。
什么是事件
事件 = 用户的动作。点击、输入、鼠标移过……浏览器会在这些瞬间发出"信号",你的 JS 可以提前注册一个函数去接住它:
button.addEventListener('click', function () {
console.log('被点击了!')
})
读法:给 button 添加一个监听器,监听 click 事件,一旦发生就执行这个函数。这个函数就是我们上一章说的回调——当参数传进去,由浏览器在合适的时机调用。
常见事件
| 事件名 | 触发时机 |
|---|---|
click |
点击元素 |
input |
输入框内容每次变化 |
mouseover |
鼠标移入元素 |
完整示例:输入框实时计数
💡 代码可直接复制;想改、想跑,点右上「🧪 在线运行场」。
「你会看到」:在输入框里每打一个字,下面的计数立刻 +1——监听只注册了一次,之后每次输入都自动触发。
逐段解读:
| 代码 | 作用 |
|---|---|
box.value |
输入框里当前的文字(input 的值不叫 textContent,叫 value) |
.length |
字符串的长度属性 |
addEventListener('input', ...) |
每次内容变化就执行一次函数 |
count.textContent = ... |
把计数写进页面 |
onclick 属性 vs addEventListener
| 写法 | 特点 |
|---|---|
onclick='...' 属性 |
简单直接,小示例够用 |
addEventListener |
能加多个监听,逻辑与结构分离,正式写法 |
记忆口诀:动作是事件,addEventListener 来蹲守;click 点击、input 输入、mouseover 移入。
❓ 常见问题
Q1:监听器写好了,点击按钮却毫无反应,也不报错?
原因:事件名写成了 'onclick'——addEventListener 里的事件名不带 on,正确的是 'click'。写成 'onclick' 等于在蹲守一个永远不会发生的动作,JS 不报错,只是安静地白等。
修复三步:① 找到 addEventListener 第一参数;② 把 'onclick' 改成 'click';③ 刷新页面,点按钮验证。
举一反三:同样的规矩适用于全部事件:oninput → 'input',onmouseover → 'mouseover'——属性写法带 on,监听写法不带,两套别串门。
Q2:报 TypeError: Cannot read properties of null (reading 'addEventListener')?
原因:上一章的老朋友换了个马甲:querySelector 没找到元素返回 null,你让 null 去 addEventListener——null 身上没有这个方法,直接报错。
TypeError: Cannot read properties of null (reading 'addEventListener')
修复三步:① 把 querySelector('#btn') 那行单独在控制台跑一遍;② 返回 null 就核对 id 拼写和 # 号;③ 确认 <script> 写在目标元素后面。
举一反三:Cannot read properties of null 与上一章的 Cannot set properties of null 是亲兄弟:一个是"读不到",一个是"改不了",药方相同——先查上面的查找行。
Q3:报 TypeError: box.addEventlistener is not a function?
原因:大小写写错了:正确的是 addEventListener(中间的 L 大写),你写成了 addEventlistener——JS 严格区分大小写,这个名字它不认识。
TypeError: box.addEventlistener is not a function
修复三步:① 跳到报错行;② 把 addEventlistener 改成 addEventListener;③ 以后靠编辑器自动补全输入长方法名,别手打全名。
举一反三:is not a function 九成是方法名拼错或大小写错——名字对不上,JS 就认为"这东西没这个功能"。
Q4:btn.addEventListener('click', greet()) 为什么一刷新就执行,之后再点没反应?
原因:第二个参数带了括号——greet() 立刻执行,addEventListener 收到的是它的返回值而不是函数;之后点击时手里没有可调用的函数,自然没反应。上一章的铁律在这里同样生效:传回调不带括号。
修复三步:① 看第二个参数;② 去掉括号改传函数本身:addEventListener('click', greet);③ 或直接传匿名函数 function () { ... }。
举一反三:setTimeout 的教训 + 这里的教训是同一句话:带括号 = 我现在就要结果;不带 = 货交给你,到点你叫我。
Q5:拿输入框内容用了 box.textContent,结果永远是空?
原因:拿错了属性——input 输入框里打的内容存在 value 里,不叫 textContent;box.textContent 读的是标签夹着的子内容,输入框天生没有,所以永远是空。
修复三步:① 找到读输入框的那行;② 把 box.textContent 改成 box.value;③ 记住分工:普通标签读内容用 textContent,表单元素读值用 value。
举一反三:"输出永远是空/undefined 但不报错"多半是属性名用错了对象——查官方属性比换写法更管用。
⚠️ 新手翻车现场
翻车一:事件名带上了 on,监听器永久蹲空
let btn = document.querySelector('#btn')
btn.addEventListener('onclick', function () {
console.log('被点击了!')
})
完整报错:
(什么也不输出——点击按钮毫无反应,控制台干干净净)
逐行分析:错在第 2 行的事件名 'onclick'。addEventListener 蹲守的是叫 'click' 的事件;浏览器发出的一直是 click,而你蹲的是 onclick——两个名字,永远错过。无报错是最阴的翻车:程序看起来一切正常,就是不听话。
正确写法:
btn.addEventListener('click', function () {
console.log('被点击了!')
})
翻车二:元素还没找到,先上了监听
let btn = document.querySelector('#bttn')
btn.addEventListener('click', function () {
console.log('被点击了!')
})
完整报错:
TypeError: Cannot read properties of null (reading 'addEventListener')
逐行分析:错在第 1 行——HTML 里按钮 id 是 btn,这里拼成了 bttn,querySelector 返回 null;第 2 行让 null 添加监听器,报 TypeError。注意报错指向第 2 行,但根子在第 1 行的查找——先修查找,再谈监听。
正确写法:
let btn = document.querySelector('#btn')
btn.addEventListener('click', function () {
console.log('被点击了!')
})
翻车三:回调带括号,只响一次就哑火
function greet() {
console.log('欢迎回来!')
}
let btn = document.querySelector('#btn')
btn.addEventListener('click', greet())
完整报错:
欢迎回来!
(页面一加载就打印;之后点击按钮再无反应)
逐行分析:错在第 5 行 greet() 带了括号——页面加载到这行时立刻执行了一次,addEventListener 拿到的是 greet 的返回值 undefined;此后每次点击,浏览器试图"调用 undefined",什么也不发生。把括号去掉,把函数本身交出去,点击才会触发。
正确写法:
btn.addEventListener('click', greet)
📦 完整小示例
做一个"夸夸按钮":每点一下,青蛙就被夸一次,计数自动累加——变量、DOM、事件三课同框演出:
<p id='praise'>我是一只没人夸的青蛙</p>
<button id='btn'>夸我一下</button>
<script>
let praise = document.querySelector('#praise')
let btn = document.querySelector('#btn')
let count = 0
btn.addEventListener('click', function () {
count = count + 1
praise.textContent = '呱!你真棒!已被夸 ' + count + ' 次'
praise.style.color = 'tomato'
})
</script>
你会看到:页面初始是一行普通文字和一个按钮;每点一下按钮,文字变成"呱!你真棒!已被夸 N 次"(N 逐次变成 1、2、3……)并且整行变成番茄色——监听只注册了一次,之后每次点击都自动触发。
逐段中文精讲
- 第 1~2 行(搭台):一个段落负责展示,一个按钮负责互动,各自挂好 id 等 JS 来接。
- 第 5~7 行(先找到,再备货):两行 querySelector 把元素抓进变量;
let count = 0准备一个计数器——它会不断累加,所以用 let 不用 const。 - 第 8 行(蹲守):
btn.addEventListener('click', function () {...}):给按钮装上门铃,事件名'click'不带 on,回调是匿名函数不带括号。 - 第 9 行(计数加一):
count = count + 1——老朋友了:取出旧值加一再装回去,每点一次执行一遍,计数步步高。 - 第 10~11 行(更新页面):textContent 换上带计数的句子,
+ count把数字拼进文字;style.color 顺手变色——用户每点一次,页面就"活"一次。
跑通后做个小实验:把 'click' 换成 'mouseover',鼠标滑过按钮就自动夸——夸夸机再升级;再仿照正文留言板的例子给页面加个输入框,监听 'input' 事件,把"你打了 N 个字"实时写进段落。
🎯 什么时候用它
- 点赞、收藏、加购:所有"点一下、数字加一"的交互(视频点赞数、购物车角标)都是 click 监听 + 计数变量,跟夸夸按钮同款。
- 搜索框实时联想:输入框每次键入触发 input 事件,JS 立刻拿着
box.value去查候选词并渲染下拉列表——搜索体验的顺滑全靠它。 - 导航菜单高亮:鼠标滑过菜单项(mouseover)时换背景色提示"可以点我",移开复原——纯样式做不到的"按行为变化"全交给事件。
📝 本章小结
- 事件 = 用户的动作;
addEventListener('事件名', 回调)让 JS 蹲守:注册一次,触发无数次。 - 事件名不带 on:是
'click'、'input'、'mouseover',不是 onclick。 - 回调传函数本身,不带括号——带括号变成立刻执行,监听直接失效。
- 表单元素的值在
value里,普通标签的内容在textContent里,别拿错。 - 记忆锚点:addEventListener 是装门铃——装一次,按一次响一次;门铃名字不带 on,按铃的手(括号)永远在浏览器手里。
动手改
- 把 'input' 换成 'mouseover',鼠标滑过输入框看看会发生什么
- 超过 10 个字时把计数变红:在函数里加
if (box.value.length > 10) { count.style.color = 'red' } - 加一个按钮,点击后清空输入框(提示:
box.value = '')
下一章:综合练习,做一个属于你的网页计算器 🐸