一、事件基础

1.1 事件监听

事件监听是让程序检测是否有事件发生,一旦事件触发就立即调用函数响应。

基本语法:

元素.addEventListener('事件类型', 事件处理函数)

三要素:

  • 事件源:哪个DOM元素
  • 事件类型:什么事件(click、mouseenter等)
  • 事件处理程序:事件触发时执行的函数

示例:

<button id="btn">点击我</button>
<script>
  const btn = document.querySelector('#btn')
  btn.addEventListener('click', function() {
    console.log('按钮被点击了')
  })
</script>

1.2 事件类型分类

鼠标事件

  • click - 鼠标点击
  • dblclick - 鼠标双击
  • mouseenter - 鼠标移入(无冒泡)
  • mouseleave - 鼠标移出(无冒泡)
  • mouseover - 鼠标移入(有冒泡)
  • mouseout - 鼠标移出(有冒泡)

焦点事件

  • focus - 获得焦点
  • blur - 失去焦点

键盘事件

  • keydown - 键盘按下
  • keyup - 键盘抬起

表单事件

  • input - 输入框内容变化
  • submit - 表单提交

1.3 事件对象

事件触发时会产生事件对象,包含事件相关信息。

常用事件对象属性:

属性说明适用事件
e.type事件类型所有事件
e.target触发事件的元素所有事件
e.clientX/Y光标相对窗口位置鼠标事件
e.offsetX/Y光标相对元素位置鼠标事件
e.key按键值键盘事件
e.preventDefault()阻止默认行为所有事件
e.stopPropagation()阻止事件传播所有事件

获取事件对象:

元素.addEventListener('click', function(e) {
  // e 就是事件对象
  console.log(e.type) // 事件类型
  console.log(e.clientX, e.clientY) // 光标相对窗口位置
  console.log(e.offsetX, e.offsetY) // 光标相对元素位置
  console.log(e.key) // 按键值(键盘事件)
})

二、事件进阶

2.1 事件流

事件流描述事件执行过程,包括两个阶段:

捕获阶段:从window → 目标元素(从父到子)
冒泡阶段:从目标元素 → window(从子到父)

控制执行阶段:

// 捕获阶段触发
element.addEventListener('click', handler, true)

// 冒泡阶段触发(默认)
element.addEventListener('click', handler, false)

2.2 阻止事件传播

element.addEventListener('click', function(e) {
  e.stopPropagation() // 阻止事件冒泡/捕获
  e.preventDefault()  // 阻止默认行为
})

2.3 事件委托

利用事件冒泡,将子元素事件委托给父元素处理。

优势:

  • 减少事件注册数量
  • 动态添加的子元素也能触发事件

示例:

<ul id="list">
  <li>选项1</li>
  <li>选项2</li>
  <li>选项3</li>
</ul>

<script>
  document.getElementById('list').addEventListener('click', function(e) {
    if(e.target.tagName === 'LI') {
      console.log('点击了:', e.target.textContent)
    }
  })
</script>

三、特殊事件

3.1 页面加载事件

事件类型触发时机执行顺序
load所有资源加载完成较晚
DOMContentLoadedDOM加载完成较早
// 等待页面所有资源加载完成
window.addEventListener('load', function() {
  // 执行代码
})

// DOM加载完成即执行(不等待图片等资源)
document.addEventListener('DOMContentLoaded', function() {
  // 执行代码
})

3.2 页面滚动事件

滚动相关属性:

属性说明读写性
scrollTop垂直滚动距离可读写
scrollLeft水平滚动距离可读写
scrollTo(x,y)滚动到指定位置方法
window.addEventListener('scroll', function() {
	// document.documentElement === html
  // 获取滚动距离
  const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
  
  // 滚动到指定位置
  window.scrollTo(0, 0)
  // 或
  document.documentElement.scrollTop = 0
})

3.3 页面尺寸事件

元素尺寸属性对比:

属性包含内容是否包含边框是否包含滚动条
clientWidth/Heightcontent + padding
offsetWidth/Heightcontent + padding + border
scrollWidth/Height实际内容尺寸--
window.addEventListener('resize', function() {
  // 可视区域尺寸
  const clientWidth = document.documentElement.clientWidth
  const clientHeight = document.documentElement.clientHeight
  
  // 元素尺寸(包含padding、border)
  const element = document.querySelector('.box')
  const offsetWidth = element.offsetWidth
  const offsetHeight = element.offsetHeight
})

3.4 相关属性对比总结

属性作用说明
scrollLeft和scrollTop被卷去的头部和左侧配合页面滚动来用,可读写
clientWidth和clientHeight获得元素宽度和高度不包含border,margin,滚动条用于js获取元素大小,只读属性
offsetWidth和offsetHeight获得元素宽度和高度包含border、padding,滚动条等,只读
offsetLeft和offsetTop获取元素距离自己定位父级元素的左、上距离获取元素位置的时候使用,只读属性

四、DOM节点操作

4.1 节点关系查找

节点查找方法对比:

方法返回内容包含文本节点
parentNode父节点
children子元素节点
childNodes所有子节点
previousElementSibling上一个兄弟元素
nextElementSibling下一个兄弟元素

父子关系:

const parent = element.parentNode           // 父节点
const children = element.children           // 所有子元素节点
const childNodes = element.childNodes       // 所有子节点(包含文本节点)

兄弟关系:

const prev = element.previousElementSibling // 上一个兄弟元素
const next = element.nextElementSibling     // 下一个兄弟元素

4.2 节点创建与添加

节点操作方法:

方法作用参数说明
createElement()创建元素节点标签名
appendChild()末尾添加节点要添加的节点
insertBefore()指定位置添加(新节点, 参考节点)
cloneNode()克隆节点true(深克隆)/false(浅克隆)
removeChild()删除节点要删除的节点

创建节点:

const newElement = document.createElement('div')
newElement.textContent = '新创建的元素'
newElement.className = 'new-class'

添加节点:

// 末尾添加
parent.appendChild(newElement)

// 指定位置添加
parent.insertBefore(newElement, referenceElement)

4.3 节点克隆与删除

克隆节点:

// 浅克隆(不包含子节点)
const clone = element.cloneNode(false)

// 深克隆(包含所有子节点)
const deepClone = element.cloneNode(true)

删除节点:

parent.removeChild(childElement)

五、日期对象

5.1 实例化日期

const now = new Date()                    // 当前时间
const specificDate = new Date('2024-01-01') // 指定时间

5.2 日期方法

方法返回范围说明
getFullYear()4位数年份如:2024
getMonth()0-110表示1月
getDate()1-31当月第几天
getDay()0-60表示周日
getHours()0-23小时
getMinutes()0-59分钟
getSeconds()0-59秒数
const date = new Date()

date.getFullYear()   // 四位年份
date.getMonth()      // 月份 (0-11)
date.getDate()       // 日期 (1-31)
date.getDay()        // 星期 (0-6)
date.getHours()      // 小时 (0-23)
date.getMinutes()    // 分钟 (0-59)
date.getSeconds()    // 秒数 (0-59)

console.log(date.getMonth() + 1) // 需要+1得到实际月份

// 本地化字符串
date.toLocaleString()      // 完整日期时间
date.toLocaleDateString()  // 仅日期
date.toLocaleTimeString()  // 仅时间

5.3 时间戳

时间戳表示1970年1月1日至今的毫秒数。

获取时间戳:

const date = new Date()
const timestamp1 = date.getTime()
const timestamp2 = +new Date()
const timestamp3 = Date.now()

六、环境对象与回调函数

6.1 环境对象 (this)

this 指向函数运行时所在的环境。

this指向规则:

  • 普通函数调用:thiswindow
  • 对象方法调用:this → 调用该方法的对象
  • 事件处理函数:this → 绑定事件的元素
  • 构造函数调用:this → 新创建对象
function sayHi() {
  console.log(this)
}

const user = {
  name: '张三',
  sayHi: sayHi
}

sayHi()        // window
user.sayHi()   // user对象

6.2 回调函数

将函数作为参数传递给另一个函数。

场景示例说明
定时器setInterval(callback, 1000)时间间隔执行
事件监听addEventListener('click', callback)事件触发执行
数组方法array.forEach(callback)遍历元素
异步操作fetch().then(callback)请求完成执行
// 回调函数示例
function mainFunction(callback) {
  // 某些操作...
  callback()
}

mainFunction(function() {
  console.log('这是回调函数')
})

// 定时器中的回调
setInterval(function() {
  console.log('定时器回调')
}, 1000)

七、事件监听方式对比

7.1 DOM L0 vs DOM L2

特性DOM L0 (on事件)DOM L2 (addEventListener)
事件覆盖会覆盖不会覆盖
执行阶段仅冒泡可控制捕获/冒泡
事件解绑onclick = nullremoveEventListener
匿名函数支持解绑不支持解绑匿名函数
推荐程度★★☆☆☆★★★★★
// DOM L0
btn.onclick = function() { alert('11') }
btn.onclick = function() { alert('22') } // 覆盖前一个

// DOM L2
btn.addEventListener('click', function() { alert('11') })
btn.addEventListener('click', function() { alert('22') }) // 两个都执行

八、实战案例

8.1 全选/反选功能

<table>
  <tr>
    <th><input type="checkbox" id="checkAll"> 全选</th>
    <th>商品</th>
  </tr>
  <tr><td><input type="checkbox" class="item"></td><td>商品1</td></tr>
  <tr><td><input type="checkbox" class="item"></td><td>商品2</td></tr>
</table>

<script>
  const checkAll = document.getElementById('checkAll')
  const items = document.querySelectorAll('.item')
  
  // 全选控制单项
  checkAll.addEventListener('click', function() {
    items.forEach(item => {
      item.checked = checkAll.checked
    })
  })
  
  // 单项控制全选
  items.forEach(item => {
    item.addEventListener('click', function() {
      checkAll.checked = document.querySelectorAll('.item:checked').length === items.length
    })
  })
</script>

8.2 回到顶部功能

const backToTop = document.createElement('div')
backToTop.textContent = '回到顶部'
backToTop.style.cssText = 'position:fixed;bottom:20px;right:20px;display:none;'

document.body.appendChild(backToTop)

window.addEventListener('scroll', function() {
  const scrollTop = document.documentElement.scrollTop
  backToTop.style.display = scrollTop > 300 ? 'block' : 'none'
})

backToTop.addEventListener('click', function() {
  window.scrollTo({
    top: 0,
    behavior: 'smooth'
  })
})

九、移动端触屏事件

9.1 触屏事件

事件类型触发时机说明
touchstart手指触摸屏幕开始触摸
touchmove手指在屏幕滑动触摸移动
touchend手指离开屏幕触摸结束
element.addEventListener('touchstart', function(e) {
  // 触摸开始
})

element.addEventListener('touchmove', function(e) {
  // 触摸移动
})

element.addEventListener('touchend', function(e) {
  // 触摸结束
})

9.2 Swiper插件

官网:https://www.swiper.com.cn/

这份文档涵盖了Web APIs的核心概念,从基础的事件监听到高级的节点操作,提供了完整的知识体系和实用示例。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐