【Web APIs-Day4】节点操作与移动端事件
📺 对应视频:P117-P126 | 🎯 核心目标:掌握日期对象、DOM节点的增删克隆操作,了解Swiper插件使用
一、日期对象
1.1 创建日期对象
// 当前时间
let now = new Date()
// 指定时间
let d1 = new Date('2024-01-01')
let d2 = new Date('2024-01-01 12:30:00')
let d3 = new Date(2024, 0, 1) // 注意:月份从0开始!
let d4 = new Date(1704067200000) // 时间戳(毫秒)
1.2 日期方法
let now = new Date()
// 获取各部分
now.getFullYear() // 年份,如 2024
now.getMonth() // 月(0-11!使用时要 +1)
now.getDate() // 日(1-31)
now.getDay() // 星期(0-6,0=周日)
now.getHours() // 时(0-23)
now.getMinutes() // 分(0-59)
now.getSeconds() // 秒(0-59)
now.getMilliseconds() // 毫秒
// 设置各部分
now.setFullYear(2025)
now.setMonth(11) // 12月(注意:0-11)
now.setDate(25)
1.3 时间戳
// 时间戳:从 1970-01-01 00:00:00 UTC 至今的毫秒数
Date.now() // 获取当前时间戳(常用)
new Date().getTime() // 也可以
+new Date() // 隐式转换(也常用)
// 时间戳的应用
const start = Date.now()
// ... 执行一些操作
const end = Date.now()
console.log(`耗时:${end - start}ms`)
1.4 格式化日期(实战)
function formatDate(date = new Date()) {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0') // 补零
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
console.log(formatDate()) // '2024-01-15 09:05:30'
// 星期几
const weeks = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
console.log(weeks[new Date().getDay()])
1.5 倒计时案例
function countdown(targetDate) {
const target = new Date(targetDate).getTime()
function update() {
const now = Date.now()
const diff = target - now // 剩余毫秒数
if (diff <= 0) {
clearInterval(timer)
console.log('时间到!')
return
}
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
const hours = Math.floor(diff % (1000 * 60 * 60 * 24) / (1000 * 60 * 60))
const minutes = Math.floor(diff % (1000 * 60 * 60) / (1000 * 60))
const seconds = Math.floor(diff % (1000 * 60) / 1000)
document.querySelector('.days').textContent = String(days).padStart(2, '0')
document.querySelector('.hours').textContent = String(hours).padStart(2, '0')
document.querySelector('.minutes').textContent = String(minutes).padStart(2, '0')
document.querySelector('.seconds').textContent = String(seconds).padStart(2, '0')
}
update()
const timer = setInterval(update, 1000)
}
countdown('2025-01-01 00:00:00')
二、DOM 节点操作
2.1 节点类型与关系
const parent = document.querySelector('.parent')
const child = document.querySelector('.child')
// 父节点
child.parentNode // 直接父节点(含文本节点)
child.parentElement // 直接父元素节点(推荐)
// 子节点
parent.childNodes // 所有子节点(含文本、注释节点)
parent.children // 所有子元素节点(推荐,HTMLCollection)
parent.firstChild // 第一个子节点(可能是文本节点)
parent.firstElementChild // 第一个子元素节点(推荐)
parent.lastElementChild // 最后一个子元素节点
// 兄弟节点
child.previousSibling // 前一个兄弟节点
child.previousElementSibling // 前一个兄弟元素节点(推荐)
child.nextSibling // 后一个兄弟节点
child.nextElementSibling // 后一个兄弟元素节点(推荐)
2.2 创建节点
// 创建元素节点
const div = document.createElement('div')
const li = document.createElement('li')
const img = document.createElement('img')
// 创建文本节点
const text = document.createTextNode('Hello World')
// 创建文档片段(批量操作用,减少回流)
const fragment = document.createDocumentFragment()
for (let i = 0; i < 100; i++) {
const li = document.createElement('li')
li.textContent = `第${i}项`
fragment.appendChild(li)
}
document.querySelector('ul').appendChild(fragment) // 只触发一次渲染
2.3 插入节点
const parent = document.querySelector('.parent')
const newEl = document.createElement('div')
const refEl = document.querySelector('.reference')
// appendChild:追加到末尾
parent.appendChild(newEl)
// insertBefore:插入到参考元素之前
parent.insertBefore(newEl, refEl) // 在 refEl 前面插入
// insertAdjacentElement(更灵活,ES6+)
refEl.insertAdjacentElement('beforebegin', newEl) // refEl 之前
refEl.insertAdjacentElement('afterbegin', newEl) // refEl 内部开头
refEl.insertAdjacentElement('beforeend', newEl) // refEl 内部末尾
refEl.insertAdjacentElement('afterend', newEl) // refEl 之后
// insertAdjacentHTML(直接插入HTML字符串)
refEl.insertAdjacentHTML('afterend', '<p>新段落</p>')
2.4 克隆节点
const original = document.querySelector('.card')
// cloneNode(false):浅克隆,只克隆元素本身(不含子节点)
const shallow = original.cloneNode(false)
// cloneNode(true):深克隆,克隆元素及所有子节点(推荐)
const deep = original.cloneNode(true)
// 克隆后需要插入到文档中才能显示
document.querySelector('.container').appendChild(deep)
// ⚠️ 注意:克隆不会复制事件监听器(addEventListener绑定的)
2.5 删除节点
const el = document.querySelector('.target')
// 现代方式(推荐)
el.remove()
// 旧方式(通过父节点删除)
el.parentNode.removeChild(el)
// 清空所有子节点
parent.innerHTML = '' // 简单但可能有内存泄漏(事件监听残留)
while (parent.firstChild) {
parent.removeChild(parent.firstChild) // 逐个删除(干净)
}
三、Swiper 插件
3.1 什么是 Swiper?
Swiper 是一个成熟的移动端滑动插件,支持触摸滑动、轮播、无限循环等效果,无需手写复杂逻辑。
CDN 引入:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"/>
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
3.2 基本使用
<!-- HTML 结构(固定格式) -->
<div class="swiper">
<div class="swiper-wrapper">
<div class="swiper-slide">幻灯片1</div>
<div class="swiper-slide">幻灯片2</div>
<div class="swiper-slide">幻灯片3</div>
</div>
<!-- 分页器 -->
<div class="swiper-pagination"></div>
<!-- 导航按钮 -->
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
// JS 初始化
const swiper = new Swiper('.swiper', {
// 基础配置
loop: true, // 无限循环
autoplay: {
delay: 3000, // 自动播放,3秒切换
disableOnInteraction: false // 用户操作后继续自动播放
},
// 分页器
pagination: {
el: '.swiper-pagination',
clickable: true // 点击分页器可跳转
},
// 导航按钮
navigation: {
prevEl: '.swiper-button-prev',
nextEl: '.swiper-button-next'
},
// 效果
effect: 'fade', // 'slide'(默认)| 'fade' | 'cube' | 'coverflow'
// 回调
on: {
slideChange() {
console.log('当前索引:', this.activeIndex)
}
}
})
// 操作方法
swiper.slideNext() // 下一张
swiper.slidePrev() // 上一张
swiper.slideTo(2) // 跳转到索引2
swiper.autoplay.stop() // 停止自动播放
swiper.autoplay.start() // 开始自动播放
四、移动端事件
4.1 触摸事件
const el = document.querySelector('.touch-area')
el.addEventListener('touchstart', e => {
console.log('手指按下')
console.log(e.touches) // 所有触摸点
console.log(e.changedTouches) // 本次变化的触摸点
const touch = e.changedTouches[0]
console.log(touch.clientX, touch.clientY) // 触摸位置
})
el.addEventListener('touchmove', e => {
e.preventDefault() // 阻止默认滚动
console.log('手指移动')
})
el.addEventListener('touchend', e => {
console.log('手指抬起')
})
4.2 手势识别(滑动方向判断)
let startX, startY
el.addEventListener('touchstart', e => {
startX = e.touches[0].clientX
startY = e.touches[0].clientY
})
el.addEventListener('touchend', e => {
const endX = e.changedTouches[0].clientX
const endY = e.changedTouches[0].clientY
const deltaX = endX - startX
const deltaY = endY - startY
if (Math.abs(deltaX) > Math.abs(deltaY)) {
// 水平滑动
if (deltaX > 50) console.log('向右滑')
else if (deltaX < -50) console.log('向左滑')
} else {
// 垂直滑动
if (deltaY > 50) console.log('向下滑')
else if (deltaY < -50) console.log('向上滑')
}
})
五、知识图谱
节点操作与移动端事件
├── 日期对象
│ ├── new Date(),getFullYear/Month/Date/Day...
│ ├── 时间戳:Date.now()
│ └── 应用:格式化、倒计时
├── DOM 节点操作
│ ├── 节点关系:parentElement / children / nextElementSibling
│ ├── 创建:createElement / createDocumentFragment
│ ├── 插入:appendChild / insertBefore / insertAdjacentElement
│ ├── 克隆:cloneNode(true/false)
│ └── 删除:el.remove() / removeChild
├── Swiper 插件
│ ├── 引入 CSS + JS
│ ├── HTML 结构(swiper/wrapper/slide)
│ └── JS 配置(loop/autoplay/pagination/navigation)
└── 移动端事件
├── touchstart / touchmove / touchend
├── e.touches / e.changedTouches
└── 手势判断(deltaX/deltaY)
六、高频面试题
Q1: childNodes 和 children 的区别?
childNodes返回所有子节点(含文本节点、注释节点),是 NodeList;children只返回子元素节点,是 HTMLCollection。日常开发用children更方便。
Q2:如何高效地批量插入 DOM?
使用
document.createDocumentFragment()创建文档片段,把所有元素先加入片段,最后一次性插入 DOM,只触发一次重排/重绘,性能更好。
Q3: cloneNode 会克隆事件监听吗?
不会。通过
addEventListener绑定的事件监听器不会被克隆(inlineonclick属性会被克隆,但不推荐)。克隆后需要重新绑定事件。
⬅️ 上一篇:Web APIs Day3 - 事件进阶 ➡️ 下一篇:Web APIs Day5 - BOM与本地存储