当前位置: 首页 > news >正文

JavaScript事件监听

目录

一、事件监听

1. 事件监听的基本概念

2. 使用 addEventListener

二、事件对象

1. 常见事件对象属性与方法

2. 示例代码

三、环境对象(this)的指向

1. 传统函数中的 this

2. 箭头函数中的 this

3. 结论

4. 注意事项

三、回调函数(Callback Function)

1. 回调函数的特点

2. 综合应用:事件对象、this 与回调

3. 常见问题与解决方案

示例:随机轮播图


一、事件监听

1. 事件监听的基本概念

  • 事件:用户或浏览器触发的动作,如点击、滚动、键盘输入等。

  • 监听器(Listener):事件发生时执行的函数。

  • 两种绑定方式

    • HTML 属性<button onclick="handleClick()">(不推荐,混合逻辑与结构)。

    • DOM APIaddEventListener(推荐,灵活且可维护)。


2. 使用 addEventListener

  • 语法

    element.addEventListener(eventType, handler [, options]);
  • 参数

    • eventType:事件类型字符串(如 "click""keydown")。

    • handler:事件触发时调用的函数。

    • options(可选):配置对象或布尔值,控制捕获/冒泡阶段、一次性执行等。

  • 步骤

    • 获取 DOM 元素

    • 通过 addEventListener 方法为 DOM 节点添加事件监听

    • 等待事件触发,如用户点击了某个按钮时便会触发 click 事件类型

    • 事件触发后,相对应的回调函数会被执行

  • 示例

    const button = document.querySelector('button');
    button.addEventListener('click', function(event) {
      console.log('按钮被点击!');
    });

   

二、事件对象

当事件触发时,浏览器会自动创建一个 事件对象event),并将其作为参数传递给事件处理函数。该对象包含与事件相关的所有信息,例如触发事件的元素、事件类型、坐标位置、按键信息等。


1. 常见事件对象属性与方法

  • 常用属性/方法

    • event.target:触发事件的元素(如点击的具体子元素)。

    • event.currentTarget:绑定事件的元素(等同于 this)。

    • event.stopPropagation():阻止事件继续传播(冒泡或捕获)。

    • event.preventDefault():阻止默认行为(如表单提交、链接跳转)。

    • event.type:事件类型(如 "click")。

属性/方法说明示例场景
event.target触发事件的实际元素(可能是事件冒泡中的子元素)点击子元素时,target 指向子元素
event.currentTarget绑定事件的元素(等同于 this,在事件处理函数中指向绑定事件的元素)事件委托中,currentTarget 指向父元素
event.type事件类型(如 "click""keydown"判断事件类型以执行不同逻辑
event.preventDefault()阻止事件的默认行为(如阻止链接跳转、表单提交)表单验证失败时阻止提交
event.stopPropagation()阻止事件继续传播(停止捕获或冒泡)避免父元素的事件监听器触发
event.stopImmediatePropagation()阻止同一元素上的其他监听器执行优先级高于 stopPropagation,阻止后续监听器
event.clientX / clientY鼠标相对于浏览器窗口的坐标(不包含滚动偏移)实现拖拽效果时获取鼠标位置
event.pageX / pageY鼠标相对于文档的坐标(包含滚动偏移)绘制画布时准确定位
event.key键盘事件的按键值(如 "Enter""a"监听回车键提交表单
event.relatedTarget关联元素(如 mouseover 时表示鼠标来自哪个元素,focusout 时表示即将获得焦点的元素)实现下拉菜单的悬停交互

2. 示例代码

document.querySelector('button').addEventListener('click', function(event) {
  console.log('事件类型:', event.type); // "click"
  console.log('触发元素:', event.target.tagName); // 可能是子元素
  console.log('绑定元素:', event.currentTarget.tagName); // BUTTON
  event.preventDefault(); // 阻止默认行为(如表单提交)
});

三、环境对象(this)的指向

在事件处理函数中,this 的指向取决于函数的定义方式:

1. 传统函数中的 this

  • 指向绑定事件的元素(与 event.currentTarget 相同)。

  • 示例

2. 箭头函数中的 this

  • 继承外层作用域的 this(通常是 window 或外层函数上下文)。

  • 示例:

    button.addEventListener('click', () => {
      console.log(this === window); // true(假设外层是全局作用域)
    });

3. 结论

  1. this 本质上是一个变量,数据类型为对象

  2. 函数的调用方式不同 this 变量的值也不同

  3. 【谁调用 this 就是谁】是判断 this 值的粗略规则


4. 注意事项

  • 避免在需要 this 指向元素时使用箭头函数。

  • 需要访问元素时,优先使用 event.currentTarget(兼容性更好)。


   

三、回调函数(Callback Function)

如果将函数 A 做为参数传递给函数 B 时,我们称函数 A 为回调函数。


1. 回调函数的特点

  1. 参数传递

    • 默认接收 event 对象作为参数。

    • 若需传递自定义参数,需封装在匿名函数中:

      const handler = (message) => {
        console.log(message);
      };
      button.addEventListener('click', () => handler('按钮被点击!'));
  2. 匿名函数与命名函数

    • 匿名函数:直接定义在 addEventListener 中,但无法直接移除。

      button.addEventListener('click', function() { /* ... */ });
      <script>
        // 调用定时器,匿名函数做为参数
        setInterval(function () {
          console.log('我是回调函数...');
        }, 1000);
      </script>
    • 命名函数:需预先定义,便于复用和移除。

      function handleClick() { /* ... */ }
      button.addEventListener('click', handleClick);
      button.removeEventListener('click', handleClick); // 可成功移除
      <script>
      	function fn() {
          console.log('我是回调函数...');
        }
        // 调用定时器
        setInterval(fn, 1000);
      </script>
  3. 内存管理

    • 若回调函数引用外部变量或 DOM 元素,可能导致内存泄漏(需及时移除监听)。


2. 综合应用:事件对象、this 与回调

<div id="container">
  <button class="btn">点击我</button>
</div>
const container = document.querySelector('#container');
const button = document.querySelector('.btn');

// 传统函数中 this 指向绑定元素
button.addEventListener('click', function(event) {
  console.log('this:', this); // <button> 元素
  console.log('currentTarget:', event.currentTarget); // <button> 元素
  console.log('target:', event.target); // <button> 元素(无子元素时与 currentTarget 相同)
});

// 事件委托:父元素监听子元素事件
container.addEventListener('click', function(event) {
  if (event.target.classList.contains('btn')) {
    console.log('点击的按钮:', event.target);
    event.stopPropagation(); // 阻止冒泡到更外层
  }
});

// 箭头函数中的 this(假设外层为全局作用域)
button.addEventListener('click', () => {
  console.log('箭头函数中的 this:', this); // window 或 undefined(严格模式)
});

3. 常见问题与解决方案

问题场景原因分析解决方案
this 指向不符合预期使用了箭头函数改用传统函数,或通过 event.currentTarget 获取元素
无法移除事件监听回调函数为匿名函数使用命名函数,并确保 removeEventListener 参数一致
事件委托中误判 event.target子元素嵌套导致 target 不准确使用 closest 方法向上查找匹配元素:
event.target.closest('.btn')
高频事件(如 scroll)卡顿回调函数执行过于频繁使用 节流(throttle) 或 防抖(debounce) 优化

示例:随机轮播图

<!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>
    * {
      box-sizing: border-box;
    }

    .slider {
      width: 560px;
      height: 400px;
      overflow: hidden;
    }

    .slider-wrapper {
      width: 100%;
      height: 320px;
    }

    .slider-wrapper img {
      width: 100%;
      height: 100%;
      display: block;
    }

    .slider-footer {
      height: 80px;
      background-color: rgb(100, 67, 68);
      padding: 12px 12px 0 12px;
      position: relative;
    }

    .slider-footer .toggle {
      position: absolute;
      right: 0;
      top: 12px;
      display: flex;
    }

    .slider-footer .toggle button {
      margin-right: 12px;
      width: 28px;
      height: 28px;
      appearance: none;
      border: none;
      background: rgba(255, 255, 255, 0.1);
      color: #fff;
      border-radius: 4px;
      cursor: pointer;
    }

    .slider-footer .toggle button:hover {
      background: rgba(255, 255, 255, 0.2);
    }

    .slider-footer p {
      margin: 0;
      color: #fff;
      font-size: 18px;
      margin-bottom: 10px;
    }

    .slider-indicator {
      margin: 0;
      padding: 0;
      list-style: none;
      display: flex;
      align-items: center;
    }

    .slider-indicator li {
      width: 8px;
      height: 8px;
      margin: 4px;
      border-radius: 50%;
      background: #fff;
      opacity: 0.4;
      cursor: pointer;
    }

    .slider-indicator li.active {
      width: 12px;
      height: 12px;
      opacity: 1;
    }
  </style>
</head>

<body>
  <div class="slider">
    <div class="slider-wrapper">
      <img src="./images/slider01.jpg" alt="" />
    </div>
    <div class="slider-footer">
      <p>对人类来说会不会太超前了?</p>
      <ul class="slider-indicator">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
      <div class="toggle">
        <button class="prev">&lt;</button>
        <button class="next">&gt;</button>
      </div>
    </div>
  </div>
  <script>
    // 1. 初始数据
    const sliderData = [
      { url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' },
      { url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' },
      { url: './images/slider03.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' },
      { url: './images/slider04.jpg', title: '李玉刚:让世界通过B站看到东方大国文化', color: 'rgb(139, 98, 66)' },
      { url: './images/slider05.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' },
      { url: './images/slider06.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' },
      { url: './images/slider07.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' },
      { url: './images/slider08.jpg', title: '谁不想和小猫咪贴贴呢!', color: 'rgb(99, 72, 114)' },
    ]

    function slider(i) {
      const img = document.querySelector('.slider-wrapper img')
      img.src = sliderData[i].url
      const title = document.querySelector('.slider-footer p')
      title.textContent = sliderData[i].title
      const bgc = document.querySelector('.slider-footer')
      bgc.style.backgroundColor = sliderData[i].color
      const li = document.querySelector(`.slider-indicator li:nth-child(${i + 1})`)
      li.classList.add('active')
    }
    function clearSlider(i) {
      const li = document.querySelector(`.slider-indicator li:nth-child(${i + 1})`)
      li.classList.remove('active')
    }

    let i = 0
    slider(i)

    //1.手动轮播
    const next = document.querySelector('.toggle .next')
    next.addEventListener('click', function () {
      clearSlider(i)
      i++;
      if (i === 8)
        i = 0
      slider(i)
    })
    const prev = document.querySelector('.toggle .prev')
    prev.addEventListener('click', function () {
      clearSlider(i)
      i--;
      if (i === -1)
        i = 7
      slider(i)
    })

    //2.自动轮播
    // let timer = setInterval(function () {
    //   clearSlider(i)
    //   i++;
    //   if (i === 8)
    //     i = 0
    //   slider(i)
    // }, 1000)

  </script>
</body>

</html>

相关文章:

  • Vue2和3的vue-router:生命周期、懒加载
  • Nmap全脚本使用指南!NSE脚本全详细教程!Kali Linux教程!(三)
  • 通过Anaconda Prompt激活某个虚拟环境并安装第三方库
  • Linux多线程编程的艺术:封装线程、锁、条件变量和信号量的工程实践
  • 今日行情明日机会——20250331
  • Ansible(2)——部署 Ansible
  • 爬虫:基本流程和robots协议
  • 从零构建大语言模型全栈开发指南:第四部分:工程实践与部署-4.1.1模型量化(INT8/FP16)与剪枝策略
  • 【软考备考】管道一过滤器(Pipe-Filter)的架构风格
  • USB有驱IC卡读卡器
  • Spring中都用到了哪些设计模式
  • 美团小程序 mtgsig1.2 拼好饭案例 分析 mtgsig
  • 六级词汇量积累day13
  • dayjs dayjs时间格式化工具(时间计算、时间格式化)
  • DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加行拖拽排序功能示例14,TableView16_14 拖拽自动保存示例
  • python leetcode简单练习(1)
  • 【FreeRTOS】任务(TASK)——任务的创建(源码解读)
  • Qt中绘制不规则控件
  • ​Linux 中 nmap 命令详解:从基础到实战的全面指南
  • dfs记忆化搜索刷题 + 总结
  • 北京网站建设 网络推广/现在广告行业好做吗
  • 有几家做网站的公司/精准防恶意点击软件
  • 建网站大约得用多少钱/网站seo搜索
  • 佛山企业网站设计公司/在线企业管理培训课程
  • 网站建设平台方案设计/站长工具中文精品
  • 做图片网站咋样/网络营销的十种方法