🐸 青蛙编程网
首页 / 前端 / JavaScript / 5. 事件

事件

这是什么:事件监听 = 用 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,按铃的手(括号)永远在浏览器手里。

动手改

  1. 把 'input' 换成 'mouseover',鼠标滑过输入框看看会发生什么
  2. 超过 10 个字时把计数变红:在函数里加 if (box.value.length > 10) { count.style.color = 'red' }
  3. 加一个按钮,点击后清空输入框(提示:box.value = '')

下一章:综合练习,做一个属于你的网页计算器 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。