Skip to content

2-4事件监听和案例 ​

本页对应 第二章WebAPIs,共 6 段视频,约 40 分钟。

学习目标 ​

  • 能够使用 addEventListener 为指定元素注册 click 事件,并准确说明事件源、事件类型、事件处理函数三要素。
  • 能够独立实现“点击关闭登录窗口”案例,使用 querySelector 获取元素,并通过 style.display = 'none' 隐藏目标容器。
  • 能够识别并解释回调函数,说明将函数作为参数传给 setTimeout 或 addEventListener 时的执行时机。
  • 能够对比 DOM0 的 onclick 与 DOM2 的 addEventListener 注册同名事件时的差异,并优先使用 addEventListener。
  • 能够为元素绑定 click、mouseenter、mouseleave 鼠标事件,并通过控制台输出验证对应回调函数的触发结果。

知识点 ​

一、事件类型总览与命名规则 ​

为什么需要:页面不是只靠“点击”工作,鼠标经过、离开、输入框聚焦、键盘输入等都需要不同事件来响应。把事件分类后,才能按场景选对事件。
怎么想:本节把常见事件分成鼠标事件、焦点事件、键盘事件、表单/input 事件等,先认类型,再看触发时机。事件类型是大小写敏感的字符串,统一用小写字母。
怎么做:写事件监听时,第一个参数传事件类型字符串,如 'click'、'focus'、'keydown'。
易错点:事件类型要加引号且全小写;不要写成 Click、CLICK。
事件类型分类示意图

二、鼠标事件 ​

为什么需要:鼠标点击、经过、离开是最常见的交互来源,例如点按钮、鼠标移入显示效果、移出恢复状态。
怎么想:先明确“监听谁”,再选事件类型,最后在回调函数里写要执行的代码。转写稿中的调试过程是:先获取 .box,分别注册 click、mouseenter、mouseleave,然后反复点击、移入、移出,观察控制台是否分别打印对应内容。
怎么做:使用 addEventListener('事件类型', function () { ... })。
易错点:mouseenter 是“经过/进入”,mouseleave 是“离开”,拼写不要写成 mouseinter、mouselive;事件类型仍是小写字符串。

html
<body>
  <div class="box"></div>
  <script>
    // 鼠标事件类型
    const box = document.querySelector('.box')
    // 1. 鼠标点击
    box.addEventListener('click', function () {
      console.log('我点击了盒子')
    })
    // 2. 鼠标经过
    box.addEventListener('mouseenter', function () {
      console.log('我鼠标经过了盒子')
    })
    // 3. 鼠标离开
    box.addEventListener('mouseleave', function () {
      console.log('我鼠标离开了盒子')
    })
  </script>
</body>

三、焦点事件 ​

为什么需要:表单输入框需要知道“什么时候获得光标、什么时候失去光标”,例如输入框聚焦时提示、失焦时校验。
怎么想:焦点事件主要针对表单是否获得光标:获得焦点是 focus,失去焦点是 blur。转写稿外的笔记还给出拓展:元素.focus() 可以让元素自动获得焦点,类似百度首页搜索框自动聚焦。
怎么做:给输入框注册 focus、blur;若要自动聚焦,调用 search.focus()。调试时手动点进第一个输入框看是否打印“获得了焦点”,再点外面看是否打印“失去了焦点”,刷新页面观察第二个输入框是否自动获得焦点。
易错点:focus/blur 是事件类型字符串;focus() 是方法调用,不要混淆。blur() 在笔记中作为“自动失去焦点”的拓展方法出现,实际写事件监听时仍用 'blur'。

html
<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>焦点事件</title>
    <style>
      [type=text] {
        width: 245px;
        height: 50px;
        padding-left: 20px;
        border: 1px solid #ccc;
        font-size: 17px;
        outline: none;
      }
    </style>
  </head>

  <body>
    <input type="text" class="search-text">
    <input type="text" class="search">
    <script>
      // 1. 焦点事件(手动触发)
      const search_text = document.querySelector('.search-text')
      // 1.1 获得焦点  focus
      search_text.addEventListener('focus', function () {
        console.log('获得了焦点')
      })
      // 1.2 失去焦点 blur
      search_text.addEventListener('blur', function () {
        console.log('失去了焦点')
      })
      // 2. 拓展 自动获得焦点 focus()    自动失去焦点 blur()
      //  2.1 语法: 元素.focus()  比如百度首页搜索框自动获得焦点
      const search = document.querySelector('.search')
      search.focus()
    </script>
  </body>

</html>

四、键盘事件和 input 事件 ​

为什么需要:用户输入内容时,有时要监听“按下键盘”,有时要监听“表单值变化”,它们的触发时机和拿到的值不同。
怎么想:看表格区分:keydown 按下键盘时触发,但取不到最后一次按键值;keyup 弹起键盘时触发,能得到输入内容;input 在表单 value 发生变化时触发,也能得到输入内容。
怎么做:在 textarea 上分别注册三个事件并打印 tx.value。转写稿中的调试思路是逐键输入 abc,观察控制台:keydown 时值还是上一次内容,input 和 keyup 时能拿到最新内容;执行顺序为 keydown → input → keyup。
易错点:不要用 keydown 判断最终输入值;要拿最新输入内容优先看 input 或 keyup。事件名仍要小写。

事件触发时机得到表单值
keydown按下键盘时触发不带最后一次按键值 ab
keyup弹起键盘时触发输入内容 abc
input表单value发生变化时触发输入内容 abc
html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>input事件和键盘事件</title>
  <style>
    textarea {
      width: 300px;
      height: 30px;
      padding: 10px;
      border-color: transparent;
      outline: none;
      resize: none;
      background: #f5f5f5;
      border-radius: 4px;
    }
  </style>
</head>

<body>
  <textarea id="tx" placeholder="发一条友善的评论" rows="2"></textarea>
  <script>
    // 获取元素
    const tx = document.querySelector('#tx')

    // 1. 键盘事件 
    // 1.1 键盘按下事件  keydown  当我们按下键盘的时候就触发
    tx.addEventListener('keydown', function () {
      console.log('我是keydown事件' + tx.value)
    })

    // 1.2 键盘弹起事件  keyup 当我们键盘弹起的时候就触发
    tx.addEventListener('keyup', function () {
      console.log('我是keyup事件' + tx.value)
    })

    // 2. 用户输入事件 input ,是表单value的值发生变化的时候触发
    tx.addEventListener('input', function () {
      console.log('我是input事件' + tx.value)
    })
    // 3. 注意事项
    // 3.1 执行顺序  keydown →  input   →  keyup
    // 3.2 keydown 获取值的时候得不到最后一次按键的值, keyup和input可以得到用户输入内容
  </script>
</body>

</html>

注意事项

  1. 执行顺序 keydown → input → keyup
  2. keydown 获取值的时候得不到最后一次按键的值, keyup和input可以得到用户输入内容

五、调试与易错点小结 ​

为什么需要:事件类型多,写错一个字母或选错触发时机,效果就不对。
怎么想:先按“事件源—事件类型—回调函数”三要素梳理,再用控制台打印验证触发次数和取值。
怎么做:鼠标事件看点击/经过/离开;焦点事件看 focus/blur;键盘输入看 keydown/input/keyup 的执行顺序和 value 差异。
易错点:事件类型大小写敏感,统一小写并加引号;mouseenter、mouseleave、focus、blur、keydown、keyup、input 拼写要准确;keydown 不要当作最终输入值。

事件对象是什么 ​

为什么需要:事件监听让代码在“点击、键盘弹起”等特定时机才执行;但执行时我们往往还要知道“这次事件发生了什么”,比如鼠标点在哪里、用户按了哪个键。事件对象就是用来携带这些信息的。

怎么想:事件对象本身也是一个对象,里面有事件触发时的相关信息和相关方法。例如鼠标点击事件中,事件对象就存了鼠标点在哪个位置等信息。它不是我们手动创建的对象,而是事件回调函数被调用时,作为第一个参数传进来的。

怎么做:在事件回调函数的小括号里写一个形参,通常命名为 event、ev 或 e。这个形参就是事件对象。本节笔记强调:事件回调函数的【第1个参数】即事件对象。常见命名如 event、ev、e,名字只是形参名,关键是它在第一个参数位置。

易错点:不要漏写这个参数;不要把它当成全局变量,在回调函数外面直接用;不同事件类型的事件对象属性不完全一样,鼠标事件和键盘事件要看的属性不同。

事件回调函数第一个参数是事件对象

为什么需要事件对象 ​

为什么需要:只注册事件还不够。比如“按下回车键可以发布新闻”“判断鼠标点击了哪个元素,从而做相应操作”,都需要从本次事件中读取信息。

怎么想:先明确事件三要素:事件源、事件类型、事件处理函数。事件处理函数就是回调函数。转写稿里讲回调函数时提到:函数当作参数传给另一个函数,这个函数就叫回调函数;事件监听里的处理函数要等事件触发后“回头调用”。因此,回调函数执行时,第一个参数就是本次事件的事件对象。

怎么做:写事件监听时,在回调函数形参位置接收事件对象,再按需读取属性。例如点击盒子时打印事件对象,观察它包含哪些信息。

易错点:不要因为回调函数“不是自己手动调用”就以为拿不到参数;参数由事件触发时的调用过程传入。调试时先 console.log(e) 看对象,再决定用哪个属性。

鼠标点击中查看事件对象 ​

为什么需要:鼠标点击是最直观的场景。先看到事件对象长什么样,才能理解它确实携带了本次事件的信息。

怎么想:给 .box 注册 click,在回调函数里接收 e,用 console.log(e) 输出。转写稿里反复强调事件类型要写成小写字符串,如 click;事件处理函数每次点击都会执行一次。

怎么做:

html
<body>
  <div class="box"></div>
  <textarea id="tx" placeholder="发一条友善的评论" rows="2"></textarea>
  <script>
    // 事件对象
    const box = document.querySelector('.box')
    box.addEventListener('click', function (e) {
      console.log(e)
    })
    const tx = document.querySelector('#tx')
    tx.addEventListener('keyup', function (e) {
      // e 就是事件对象
      // console.log(e)
      // console.log(e.key)  // a 
      // 用户如果按下的是回车键,则弹出框提示按下了回车键
      if (e.key === 'Enter') {
        alert('您按下了回车键')
      }
    })
  </script>
</body>

易错点:click 要加引号且小写;回调函数第一个参数不要写成 e() 或漏掉;控制台里要展开对象查看,不要只看一行 [object Object] 就下结论。

键盘事件中判断按键 ​

为什么需要:键盘事件里要知道用户按了哪个键。笔记场景是:按下回车键可以发布新闻;代码用 keyup 监听键盘弹起,并判断 e.key。

怎么想:先监听 #tx 的 keyup,回调函数接收 e;再读取 e.key。调试时可以先输入字母 a,在控制台看 e.key 是否为 a;再按回车,判断 e.key === 'Enter',满足就 alert。

怎么做:用 if (e.key === 'Enter') { alert('您按下了回车键') }。注意 Enter 的拼写和大小写。

易错点:不要写成 'enter';keyup 是键盘弹起时触发,不是按下;判断按键用 e.key,不要和鼠标位置等属性混用。

易错点与调试建议 ​

为什么需要:事件对象相关代码短,但形参、事件类型、属性名任一写错都可能“没反应”。

怎么想:按“事件源 → 事件类型 → 事件处理函数”检查。转写稿里说事件监听三要素是事件源、事件类型、事件处理函数;事件对象是处理函数的第一个参数,属于处理函数内部使用的信息。

怎么做:先确认元素获取成功,再确认事件类型字符串正确,最后在回调函数里 console.log(e) 或 console.log(e.key) 调试。点击 .box 看输出;在文本框输入 a 看 e.key;按回车看是否弹窗。

易错点:事件类型大小写敏感,统一用小写字符串;回调函数每次触发都会执行,不要把只该执行一次的代码误以为只跑一次;事件对象只在本次回调函数执行过程中使用最稳妥。

先理解:环境对象就是函数里的 this ​

为什么需要: 函数内部的 this 是一个特殊的环境对象,它指向哪个对象,会受当前调用环境影响。弄清楚 this 的指向,可以让我们在对象方法、事件处理里写出更简洁的代码。笔记里给的核心结论是:函数的调用方式不同,this 指代的对象也不同;“谁调用,this 就是谁”是粗略判断规则;直接调用函数,其实相当于 window.函数,所以 this 指代 window。

怎么想: 不要只看函数写在哪里,要先看函数怎么被调用。转写稿讲事件监听时反复强调:事件处理函数会被注册到元素对象身上,事件触发后再“回头调用”这个函数。这个“回头调用”的过程,正好可以帮助理解 this:谁最终调用函数,this 就倾向指向谁。

怎么做: 把笔记中的四类环境逐个放进同一段代码里观察:全局环境、普通函数、对象方法、事件处理函数。

易错点: 不要把“函数定义的位置”直接当成 this 的指向;判断时先找调用者。下面代码保留笔记原意:

html
<body>
  <button>点击</button>
  <script>
    // 环境对象 this   粗略规则: 谁调用函数,this就指向谁

    // 1. 全局环境
    // console.log(this)  // this 指向 window 全局对象

    // 2. 普通函数
    function fn() {
      console.log(this)  // this 指向 window 全局对象
    }
    window.fn()

    // 3. 对象方法
    const obj = {
      uname: '佩奇',
      sing: function () {
        console.log(this)  // this 指向 obj对象
      }
    }
    obj.sing()

    // 4. 事件
    const btn1 = document.querySelector('button')
    btn1.addEventListener('click', function () {
      // console.log(this)  // this 指向 btn 这个对象
      // btn1.style.backgroundColor = 'pink'
      this.style.backgroundColor = 'pink'
    })
  </script>
</body>

全局环境和普通函数:默认落到 window ​

为什么需要: 先建立最基础的参照:在全局环境里直接使用 this,以及在普通函数里直接调用函数时,this 指向全局对象 window。这样后面看对象方法和事件时才有对比。

怎么想: 笔记写“直接调用函数,其实相当于是 window.函数”。所以 fn() 可以理解为 window.fn(),调用者是 window,按“谁调用,this 就是谁”,this 就是 window。转写稿里讲事件监听时也强调“事件处理函数注册到元素对象身上”,这和“调用者决定 this”是同一类思路。

怎么做: 在浏览器中先观察 console.log(this),再定义 fn 并用 window.fn() 调用。笔记注释已经标出结果:全局环境指向 window,普通函数也指向 window。

易错点: 不要把 fn() 误认为“没有调用者”。在浏览器全局作用域下,它等价于 window.fn();因此普通函数里的 this 不是函数自己,也不是 obj。

对象方法:obj.sing() 时 this 指向 obj ​

为什么需要: 对象方法里经常要访问对象自己的属性。如果每次都用 obj.uname,对象名一变就不方便;用 this.uname 可以让方法跟着当前调用对象走,这就是笔记说的“代码更简洁”。

怎么想: sing 是 obj 的方法,调用写法是 obj.sing()。按“谁调用,this 就是谁”,这里的调用者是 obj,所以 this 指向 obj。转写稿里讲“事件源、事件类型、事件处理函数”三要素,也是在提醒我们:先找到“谁被触发/谁在调用”,再判断后续行为。

怎么做: 保留笔记中的对象写法,调用时写 obj.sing(),观察 console.log(this) 指向 obj。如果要访问名字,可在方法里写 this.uname。

易错点: 如果只把方法取出来单独调用,比如先赋值再直接执行,调用者就变了,this 也会变;本节先记住:对象方法要通过对象调用,才能稳定指向该对象。

事件处理函数:this 指向绑定事件的 btn ​

为什么需要: 事件处理是本节最实用的场景。笔记代码里,按钮绑定点击事件后,在回调函数中写 this.style.backgroundColor = 'pink',注释说明 this 指向 btn 这个对象。这样就不必在函数里反复写 btn1.style...。

怎么想: 转写稿讲事件监听时说过:事件处理函数会被注册到元素对象身上,事件触发后自动执行;回调函数是“回头调用”的函数。结合笔记规则“谁调用,this 就是谁”,点击按钮时,是按钮这个元素对象在触发并调用处理函数,因此处理函数里的 this 指向 btn1。

怎么做: 先获取按钮 btn1,再用 addEventListener('click', function () { ... }) 注册点击事件。回调函数里用 this.style.backgroundColor = 'pink' 修改当前按钮背景色。笔记中还保留了注释写法 btn1.style.backgroundColor = 'pink',它和 this.style... 在这里效果对应,但 this 更通用。

易错点: 事件类型要写成字符串 'click',转写稿也提醒事件类型是小写字符串;事件处理函数要等点击发生后才执行,不是页面一打开就执行。还要区分:点击的是按钮,修改的是按钮样式,所以 this 指向按钮,而不是 window。

判断流程与小结 ​

为什么需要: 面对 this 题目时,不能凭感觉,要形成固定判断流程。

怎么想: 第一步找函数;第二步找调用者;第三步套规则:fn() / window.fn() 指向 window,obj.sing() 指向 obj,btn1.addEventListener('click', function(){}) 中回调触发时指向 btn1。

怎么做: 先写最小代码验证,再回到完整代码。笔记中的四段代码可以逐段取消注释观察;事件部分点击按钮看背景是否变粉。

易错点: “谁调用,this 就是谁”是粗略规则,不是死记函数定义位置;本节范围内先掌握全局、普通函数、对象方法、事件处理函数四种环境即可。把同音字纠正为:事件监听、事件源、事件处理函数、回调函数。

课件图示 ​

课件第39页:描述 属性/方法 效果 事件监听 元素.addEventListener() 事

课件第7页:1.1 事件监听  语法:  事件发生后,想要执行的代码写到事件处理函数里面

课件第8页:1.1 事件监听  语法: 举例说明 注意: 1. 事件类型要加引号,小写 2

课件第9页:总结 总结 1. 我们为什么需要事件? 我们希望一段代码在某个特定的时机才去执

课件第10页:案例 点击关闭王者荣耀登录窗口 需求:点击关闭按钮之后,关掉登录提示盒子 分析:

课件第15页:1.3 事件监听版本 区别: on 方式同名事件会被覆盖,addEventLi

课件第16页:1.3 事件监听版本 发展史: DOM L0 :是DOM 的发展的第一个版本

课件第24页: 事件类型的大小写敏感的字符串,统一用小写字母 2. 事件类型 3 4 2 1

代码示例 ​

1. 鼠标事件(click / mouseenter / mouseleave) ​

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>鼠标事件</title>
  <style>
    .box {
      width: 200px;
      height: 200px;
      background-color: skyblue;
    }
  </style>
</head>
<body>
  <div class="box"></div>
  <script>
    // 鼠标事件类型
    const box = document.querySelector('.box')
    // 1. 鼠标点击
    box.addEventListener('click', function () {
      console.log('我点击了盒子')
    })
    // 2. 鼠标经过
    box.addEventListener('mouseenter', function () {
      console.log('我鼠标经过了盒子')
    })
    // 3. 鼠标离开
    box.addEventListener('mouseleave', function () {
      console.log('我鼠标离开了盒子')
    })
  </script>
</body>
</html>

关键行与易错点

  • document.querySelector('.box') 先拿到事件源,才能调用 addEventListener,这是事件监听三要素的第一要素。
  • addEventListener('click', 回调函数):第一个参数是事件类型,第二个参数是事件处理函数(回调函数),三要素缺一不可。
  • 易错点:事件类型是大小写敏感的字符串,必须加引号且统一用小写字母,写成 'Click'、click(不加引号)都不行。
  • mouseenter(经过)/ mouseleave(离开)是成对使用的,不要写成 mouseover 之类的拼写错误。
  • 回调函数不是页面一打开就执行,而是每次事件触发就执行一次:点一次打印一次,经过一次打印一次。

2. 焦点事件 + 键盘事件和 input 事件 ​

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>焦点事件和键盘事件</title>
  <style>
    [type=text] {
      width: 245px;
      height: 50px;
      padding-left: 20px;
      border: 1px solid #ccc;
      font-size: 17px;
      outline: none;
    }
    textarea {
      width: 300px;
      height: 30px;
      padding: 10px;
      border-color: transparent;
      outline: none;
      resize: none;
      background: #f5f5f5;
      border-radius: 4px;
    }
  </style>
</head>
<body>
  <input type="text" class="search-text">
  <input type="text" class="search">
  <textarea id="tx" placeholder="发一条友善的评论" rows="2"></textarea>

  <script>
    // 1. 焦点事件(手动触发)
    const search_text = document.querySelector('.search-text')
    // 1.1 获得焦点  focus
    search_text.addEventListener('focus', function () {
      console.log('获得了焦点')
    })
    // 1.2 失去焦点  blur
    search_text.addEventListener('blur', function () {
      console.log('失去了焦点')
    })

    // 2. 拓展 自动获得焦点 focus()  自动失去焦点 blur()
    //    比如百度首页搜索框自动获得焦点
    const search = document.querySelector('.search')
    search.focus()

    // 3. 键盘事件
    const tx = document.querySelector('#tx')
    // 3.1 键盘按下事件  keydown  按下键盘就触发
    tx.addEventListener('keydown', function () {
      console.log('我是keydown事件' + tx.value)
    })
    // 3.2 键盘弹起事件  keyup  键盘弹起就触发
    tx.addEventListener('keyup', function () {
      console.log('我是keyup事件' + tx.value)
    })
    // 4. 用户输入事件  input  表单 value 的值发生变化时触发
    tx.addEventListener('input', function () {
      console.log('我是input事件' + tx.value)
    })
  </script>
</body>
</html>

关键行与易错点

  • focus / blur 是针对表单是否获得光标的事件;search.focus() 是方法调用(带括号),'focus' 是事件类型字符串(带引号),两者不要混。
  • search.focus() 写在 JS 里,页面一加载就让第二个输入框自动获得光标。
  • 执行顺序:keydown → input → keyup。
  • 易错点:keydown 里取 tx.value 拿不到最后一次按键的值(输入 ab 时只得到 a),keyup 和 input 才能拿到完整内容。所以“实时获取用户输入”优先用 input。
  • 三个事件都注册在同一个 tx 上,互不影响,各自触发各自的回调。

3. 事件对象与环境对象 this ​

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>事件对象与环境对象</title>
  <style>
    .box {
      width: 200px;
      height: 200px;
      background-color: skyblue;
    }
    textarea {
      width: 300px;
      height: 30px;
      padding: 10px;
      border-color: transparent;
      outline: none;
      resize: none;
      background: #f5f5f5;
      border-radius: 4px;
    }
  </style>
</head>
<body>
  <div class="box"></div>
  <textarea id="tx" placeholder="发一条友善的评论" rows="2"></textarea>
  <button>点击</button>

  <script>
    // ===== 事件对象 =====
    const box = document.querySelector('.box')
    box.addEventListener('click', function (e) {
      console.log(e) // e 就是事件对象,里面存了事件触发时的相关信息
    })

    const tx = document.querySelector('#tx')
    tx.addEventListener('keyup', function (e) {
      // 用户如果按下的是回车键,则弹出框提示按下了回车键
      if (e.key === 'Enter') {
        alert('您按下了回车键')
      }
    })

    // ===== 环境对象 this  粗略规则:谁调用函数,this 就指向谁 =====
    // 1. 全局环境:this 指向 window 全局对象
    // console.log(this)

    // 2. 普通函数:直接调用相当于 window.fn(),this 指向 window
    function fn() {
      console.log(this)
    }
    window.fn()

    // 3. 对象方法:obj.sing() 调用,this 指向 obj 对象
    const obj = {
      uname: '佩奇',
      sing: function () {
        console.log(this)
      }
    }
    obj.sing()

    // 4. 事件:this 指向被点击的 btn 这个对象
    const btn1 = document.querySelector('button')
    btn1.addEventListener('click', function () {
      // btn1.style.backgroundColor = 'pink'
      this.style.backgroundColor = 'pink'
    })
  </script>
</body>
</html>

关键行与易错点

  • 事件回调函数的第 1 个参数就是事件对象,习惯命名为 event、ev、e;e.key 可以判断用户按的是哪个键,e.key === 'Enter' 注意 Enter 首字母大写。
  • 易错点:e 是形参,必须写在回调函数的小括号里,忘了写就会出现 e is not defined。
  • this 的判断规则是“谁调用,this 就是谁”:普通函数相当于 window.fn() 所以 this 是 window;obj.sing() 所以 this 是 obj;事件处理函数里 this 就是事件源(按钮),所以可以直接写 this.style.backgroundColor = 'pink' 代替 btn1.style...。
  • 修改样式一定要经过 style:this.style.backgroundColor,写成 this.backgroundColor 是无效的。

常见错误 ​

  1. 事件名写成 'Click'、'mouseEnter' 或 'onclick' 现象:监听器已注册,但操作元素完全没反应,也不报错。 原因:事件类型是大小写敏感的小写字符串;on 前缀是 L0 赋值写法的语法,不属于事件类型本身。 修法:addEventListener('click', fn)、'mouseenter'、'mouseleave',全部小写且不带 on。

  2. 用 btn.onclick = fn 给同一元素注册两次点击,只有后一个生效 现象:先写的那个回调永远不执行。 原因:onxxx = 函数 本质是赋值,第二次赋值把第一次覆盖掉了(L0 的同名事件覆盖问题)。 修法:改用 addEventListener 注册,两个回调都会执行;老代码看到 onxxx 写法能认出即可。

  3. 在 keydown 里读 value,总差最后一个字符 现象:输入 abc,控制台依次打印 ''、'a'、'ab'。 原因:keydown 在字符真正进入表单之前触发,拿不到本次按键的值。 修法:要实时取值用 input 或 keyup;确实要在 keydown 里取,就把读取延后到本次事件处理结束之后。

  4. 关闭盒子写成 pop.display = 'none',页面毫无变化 现象:点击关闭按钮,盒子还在,控制台也没有报错。 原因:display 是 CSS 样式属性,不是元素对象的直接属性,直接赋值只是给 DOM 对象挂了个无用属性。 修法:pop.style.display = 'none'(同理 this.style.backgroundColor = 'pink')。

  5. 回调函数里用 this 拿元素,却写成箭头函数 现象:this.style.xxx 报错,或 this 打印出来是 window。 原因:箭头函数不绑定自己的 this,会沿用外层作用域的 this;普通函数的 this 才是"谁调用指向谁"。 修法:事件回调用 function () {},或改用事件对象 e.currentTarget,或提前把元素存成变量。

  6. 点击关闭按钮后页面"跳回顶部" 现象:盒子关掉了,但滚动条瞬间回到页面顶端。 原因:关闭按钮是 <a href="#">,点击触发了链接的默认跳转行为。 修法:href="javascript:;",或在回调里 e.preventDefault()。

  7. 回调里拿不到事件对象,e 是 undefined 现象:e.key 报 "Cannot read properties of undefined"。 原因:回调函数声明时没写形参,浏览器传进来的事件对象没被接收。 修法:写成 function (e) { ... },形参名随意(e / event / ev),位置必须是第一个。

练习 ​

  1. 搜索框焦点交互 页面上放一个搜索框:获得焦点时边框变蓝并显示"请输入关键词"提示,失去焦点时恢复原样;同时页面加载完成后光标自动落在搜索框里。 要求:焦点相关行为用 focus / blur 事件,自动聚焦用方法调用而不是事件。

  2. 评论框字数统计 + 快捷键提交 一个 textarea,右下角实时显示"已输入 n / 100 字",超过 100 字数字变红;按下回车键时弹出提示"发布成功"并清空内容。 要求:字数统计只能用 input 事件;判断回车只能用事件对象,不能用 keyCode 之外的其他取巧写法。

  3. 三种键盘/输入事件的对比实验 给同一个 textarea 同时绑定 keydown、input、keyup,各自打印 value。 要求:连续输入 abc,记录控制台输出顺序和每次打印的内容,然后用一句话解释为什么 keydown 拿不到最后一个字符。

  4. 图片卡片:悬停遮罩 + 点击选中 三张图片卡片,鼠标经过某张时显示半透明遮罩,离开时隐藏;点击某张时给它加红色边框,其余取消。 要求:给三张卡片统一用循环注册事件;改变边框时不要重复写三次 card1/card2/card3,用环境对象一句话搞定。

  5. L0 与 L2 注册方式对比 给同一个按钮分别用 btn.onclick = fn1; btn.onclick = fn2; 和两次 addEventListener('click', ...) 各注册两个回调。 要求:观察两种写法点击后的执行结果,写出结论,并说明实际开发中应该选哪种、为什么。

原视频对照 ​

#视频时长
1今日内容01:55
2事件监听12:54
3王者荣耀关闭登录窗口案例08:24
4回调函数介绍04:48
5拓展阅读-事件监听版本07:16
6鼠标事件类型04:53

小结 ​

  1. 事件让代码从"自动执行"变成"到点执行"——交互的本质是把某段代码挂到某个时机上,而不是顺序写下来就跑。
  2. 事件三要素是排查一切事件问题的清单:谁触发(事件源)、什么情况触发(事件类型)、触发后干什么(处理函数)。效果没出来,先逐个核对这三项。
  3. 事件名是小写字符串,on 前缀属于 L0 语法,两者不能混着写;混写的表现是"静默失效",最容易被忽略。
  4. keydown / input / keyup 的选择取决于你要不要最新值:要最新输入内容就用后两者,三者执行顺序固定为 keydown → input → keyup。
  5. 事件对象是浏览器在触发瞬间"塞"进回调第一个形参的信息包,判断按了哪个键、点了哪个位置,都靠它而不是靠猜。
  6. this 的粗略规则是"谁调用指向谁":全局和普通函数指向 window,对象方法指向该对象,事件回调指向绑定的元素——所以能用 this 就别再重复 querySelector。
  7. 回调函数不是新东西,只是"把函数当参数传出去、等时机到了再回头调用",定时器和事件监听是它最典型的两处用法。
  8. 注册方式优先选 addEventListener:同名事件不覆盖、可绑多个回调,这也是它取代 onxxx 赋值写法的核心原因。