视频加载失败

移动Web端事件

3984 字
20 分钟
移动Web端事件

z在早期的时候,是没有移动端事件,大部分鼠标事件在移动端一样有用,后面苹果推出了touch 触摸事件,后续浏览器跟进,微软推出pointer指针事件(统一鼠标、触摸、笔),那在PC和移动端表现有什么区别,为什么要学习,举一个轮播图例子,正常在pc端通过点击左右按钮切换轮播,而在移动端,可以通过触摸滑动图片实现轮播的切换

Touch 触摸事件#

Touch事件基础#

touch事件类型有以下4种:

事件触发时机
touchstart手指接触屏幕
touchmove手指在屏幕上移动
touchend手指离开屏幕
touchcancel触摸操作被浏览器或系统中断

示例:

// touch事件类型
// touchstart touchmove touchend touchcancel
const $box = document.getElementById('box');
//触摸开始
$box.addEventListener(
'touchstart',
function (e) {
console.log('手指接触屏幕', e);
},
false,
);
// 移动
$box.addEventListener(
'touchmove',
function (e) {
console.log('手指在屏幕上移动', e);
},
false,
);
// 离开
$box.addEventListener(
'touchend',
function (e) {
console.log('手指离开屏幕', e);
},
false,
);
// 触摸操作被浏览器或系统中断
$box.addEventListener(
'touchcancel',
function (e) {
console.log('触摸操作被浏览器或系统中断', e);
},
false,
);

注意:即使触摸点移出目标元素,touchmove事件依然会持续触发,前提是手指一直触摸不要离开屏幕的触摸滑动,而mousemove这个鼠标事件不会触发;

Touch事件在pc端不会触发,鼠标事件在PC和移动端都会触发;touch事件的特征检测可以通过 'ontouchstart' in window检测是否支持,返回一个布尔值

Touch事件的event对象#

event对象常用属性

属性 / 方法作用
event.touches当前仍接触屏幕的所有触摸点
event.targetTouches当前仍接触屏幕,且最初在事件目标元素上开始触摸的触摸点
event.changedTouches本次事件中发生变化的触摸点
event.target最初触发事件的元素
event.currentTarget当前绑定事件处理程序的元素
event.type事件类型,如 touchstart
event.cancelable当前事件是否可以取消默认行为
event.preventDefault()阻止浏览器的默认行为

其中,touches、targetTouches 和 changedTouches 是 Touch 事件最重要的三个属性。

box.addEventListener('touchstart', (event) => {
console.log(event.touches);
console.log(event.targetTouches);
console.log(event.changedTouches);
});

触摸点的常用属性

上面的三个属性返回的都是 TouchList 对象,可以通过下标获取其中的触摸点。

box.addEventListener('touchstart', (event) => {
const touch = event.changedTouches[0];
console.log(touch.clientX);
console.log(touch.clientY);
});

常用的触摸点属性如下:

属性作用
identifier触摸点的唯一标识,用于区分不同触摸点
target该触摸点最初开始触摸的元素
clientX / clientY触摸点相对于浏览器视口的坐标
pageX / pageY触摸点相对于整个文档的坐标
screenX / screenY触摸点相对于屏幕的坐标
radiusX / radiusY触摸接触区域的椭圆半径
rotationAngle触摸接触区域的旋转角度
force触摸压力值,通常在 0 到 1 之间

日常开发中,最常用的是 clientX、clientY,例如获取手指的位置、计算滑动距离。

阻止浏览器的默认行为

移动端触摸操作可能触发浏览器的默认行为,例如页面滚动、缩放等。

可以使用 event.preventDefault() 阻止可取消的默认行为。

box.addEventListener(
'touchmove',
(event) => {
event.preventDefault();
},
{ passive: false },
);

这里需要注意两点:

  • preventDefault() 是阻止默认行为,不是阻止事件冒泡;阻止冒泡使用 stopPropagation()。
  • 现代浏览器中,部分触摸事件监听器默认可能采用被动监听(passive)。被动监听器中调用 preventDefault() 无法阻止默认行为,因此需要时应明确设置 { passive: false }。

实际开发建议: 如果只是要控制页面是否允许滚动或缩放,优先考虑 CSS 的 touch-action;只有确实需要通过 JavaScript 取消默认行为时,再使用 preventDefault()。

单指拖拽

单指拖拽是指用户用一根手指按住元素并移动,从而改变元素的位置。

实现思路:

  1. touchstart:记录手指初始坐标和元素初始位置。
  2. touchmove:计算手指移动距离,更新元素位置。
  3. touchend:结束拖拽。

示例代码

<div class="box">拖动我</div>
<style>
.box {
position: fixed;
left: 50px;
top: 100px;
width: 120px;
height: 120px;
background-color: skyblue;
display: flex;
justify-content: center;
align-items: center;
touch-action: none;
user-select: none;
}
</style>
<script>
const box = document.querySelector('.box');
let startX = 0;
let startY = 0;
let startLeft = 0;
let startTop = 0;
let dragging = false;
box.addEventListener(
'touchstart',
(event) => {
// 只允许单指拖拽
if (event.touches.length !== 1) {
dragging = false;
return;
}
const touch = event.changedTouches[0];
const rect = box.getBoundingClientRect();
// 记录手指初始坐标
startX = touch.clientX;
startY = touch.clientY;
// 记录元素初始位置
startLeft = rect.left;
startTop = rect.top;
dragging = true;
},
{ passive: true },
);
box.addEventListener(
'touchmove',
(event) => {
if (!dragging) return;
// 多指触摸时取消拖拽
if (event.touches.length !== 1) {
dragging = false;
return;
}
const touch = event.touches[0];
// 计算手指移动距离
const moveX = touch.clientX - startX;
const moveY = touch.clientY - startY;
// 更新元素位置
box.style.left = `${startLeft + moveX}px`;
box.style.top = `${startTop + moveY}px`;
},
{ passive: true },
);
box.addEventListener('touchend', () => {
dragging = false;
});
box.addEventListener('touchcancel', () => {
dragging = false;
});
</script>

核心计算公式

变量计算方式
水平移动距离moveX = 当前手指X - 初始手指X
垂直移动距离moveY = 当前手指Y - 初始手指Y
元素新位置初始位置 + 移动距离

例如,手指初始横坐标为 100px,移动后变成 150px,那么:

  • 手指水平移动了 50px。
  • 如果元素原本位于 200px,新位置就是 250px。

注意事项

  • touch-action: none 用于禁止浏览器在该元素上执行原生平移、缩放手势,避免与自定义拖拽冲突。
  • 如果改用 event.preventDefault() 阻止默认行为,touchmove 监听器需要设置 { passive: false },否则可能无法阻止。
  • touchend 和 touchcancel 都要处理,避免拖拽状态没有正确结束。

记住核心思路:记录初始位置 → 计算手指移动距离 → 更新元素位置。

pointer指针事件#

Pointer Events(指针事件) 是统一处理鼠标、触摸屏、触控笔等输入设备的事件机制。与 Mouse Events 和 Touch Events 相比,Pointer Events 最大的优势是:一套事件代码,可以同时处理 PC 端和移动端的交互。

Pointer事件基础#

Pointer事件类型

pointerdown指针按下时开始拖拽、按压
pointermove指针移动时拖拽、绘图
pointerup指针释放时结束拖拽
pointercancel指针操作被浏览器取消时清理拖拽状态
pointerover指针进入元素区域悬停效果
pointerout指针离开元素区域取消悬停
pointerenter指针进入元素或其子元素区域鼠标进入
pointerleave指针离开元素及其子元素区域鼠标离开
gotpointercapture元素获得指针捕获时监听捕获状态
lostpointercapture元素失去指针捕获时监听捕获释放
pointerrawupdate指针状态发生变化时,提供更直接的更新高精度绘图等

其中,最常用的是 pointerdown、pointermove、pointerup、pointercancel

实现一个简单的拖拽操作:

const box = document.querySelector('.box');
let isDragging = false;
box.addEventListener('pointerdown', (e) => {
isDragging = true;
box.setPointerCapture(e.pointerId);
});
box.addEventListener('pointermove', (e) => {
if (!isDragging) return;
console.log(e.clientX, e.clientY);
});
box.addEventListener('pointerup', () => {
isDragging = false;
});
box.addEventListener('pointercancel', () => {
isDragging = false;
});

Pointer 事件注意事项

  1. Pointer 事件不会自动阻止浏览器默认行为

在移动端,浏览器默认会处理页面滚动、缩放等触摸操作。如果使用 Pointer 事件实现拖拽,可能触发 pointercancel,导致拖拽中断。解决方法:使用 CSS 的 touch-action 属性。

.box {
touch-action: none;
}

常见取值:

属性值作用
auto浏览器默认处理触摸操作
none禁止浏览器处理该区域的触摸平移和缩放手势
pan-x允许浏览器处理水平滚动
pan-y允许浏览器处理垂直滚动
manipulation允许滚动和双指缩放等操作,不允许双击缩放

注意:touch-action: none 应只设置在需要自定义触摸交互的区域,避免影响正常页面滚动。

  1. pointermove 不一定需要按下指针

pointermove 只要指针移动就可能触发。例如,鼠标没有按下,只是在元素内部移动,也会触发 pointermove。因此实现拖拽时,需要判断是否处于拖拽状态。

let isDragging = false;
box.addEventListener('pointerdown', () => {
isDragging = true;
});
box.addEventListener('pointermove', (e) => {
if (!isDragging) return;
console.log(e.clientX, e.clientY);
});
box.addEventListener('pointerup', () => {
isDragging = false;
});
box.addEventListener('pointercancel', () => {
isDragging = false;
});
  1. 指针离开元素可能导致事件无法继续接收

默认情况下,鼠标移出元素后,元素可能无法继续接收后续的 pointermove 和 pointerup。解决方法:使用指针捕获。

box.addEventListener('pointerdown', (e) => {
box.setPointerCapture(e.pointerId);
});

这样即使指针移出元素,后续事件仍然可以发送到该元素。

相关 API:

API作用
setPointerCapture()捕获指定指针
releasePointerCapture()释放指定指针
hasPointerCapture()判断是否捕获指定指针

注意:pointerup 或 pointercancel 后,浏览器会自动释放指针捕获。捕获期间也不会正常触发该指针的 pointerenter、pointerleave 等边界事件。

  1. 必须考虑 pointercancel

pointerup 和 pointercancel 并不相同。

  • pointerup:用户正常释放指针。
  • pointercancel:浏览器或系统取消了当前指针操作。

例如,浏览器接管触摸操作进行页面滚动时,可能触发 pointercancel。因此拖拽开发中,通常需要同时监听两个事件进行状态清理。

function stopDrag() {
isDragging = false;
}
box.addEventListener('pointerup', stopDrag);
box.addEventListener('pointercancel', stopDrag);
  1. 多指触摸需要区分 pointerId

Pointer Events 支持多点触控,每个活动指针都有自己的 pointerId。如果只需要单指拖拽,可以记录当前操作的指针 ID,避免其他手指干扰。

let activePointerId = null;
box.addEventListener('pointerdown', (e) => {
if (activePointerId !== null) return;
activePointerId = e.pointerId;
box.setPointerCapture(e.pointerId);
});
box.addEventListener('pointermove', (e) => {
if (e.pointerId !== activePointerId) return;
console.log(e.clientX, e.clientY);
});
function stopDrag(e) {
if (e.pointerId !== activePointerId) return;
activePointerId = null;
}
box.addEventListener('pointerup', stopDrag);
box.addEventListener('pointercancel', stopDrag);
  1. Pointer 事件可能同时触发 Mouse 事件

为兼容旧代码,浏览器可能在 Pointer 事件之外继续触发对应的 Mouse 事件。例如,一次鼠标点击可能产生以下事件:

pointerdown;
mousedown;
pointerup;
mouseup;
click;

所以,同一个交互不要同时使用 Pointer 和 Mouse 事件重复处理,否则可能执行两次业务逻辑。

另外,pointerdown 不完全等同于 mousedown:鼠标多个按键同时操作时,pointerdown 只在进入有按键按下的状态时触发。

  1. pointerenter 和 pointerleave 不支持冒泡
事件是否冒泡
pointerover✅
pointerout✅
pointerenter❌
pointerleave❌
pointerdown✅
pointermove✅
pointerup✅

因此,使用事件委托处理指针进入、离开元素时,通常选择 pointerover 和 pointerout,并根据 relatedTarget 判断是否真正跨越了目标元素边界。

总结

注意事项推荐做法
浏览器默认触摸行为干扰使用 touch-action
指针未按下也能触发移动事件判断拖拽状态
指针离开元素导致事件丢失使用 setPointerCapture()
指针操作可能被取消同时处理 pointercancel
多指触摸相互干扰使用 pointerId 区分
Pointer 与 Mouse 事件可能重复避免重复绑定相同业务逻辑
pointerenter/leave 不冒泡事件委托使用 pointerover/out

实际开发中,最需要掌握的是前三项加上 pointercancel 和 pointerId。 掌握这五点,基本就可以使用 Pointer Events 完成常见的 PC 与移动端拖拽、滑动交互。

Pointer事件的event对象#

Pointer 事件触发时,浏览器会向事件处理函数传入一个 PointerEvent 对象,用于获取指针类型、坐标、按键状态、压力等信息。

box.addEventListener('pointerdown', (event) => {
console.log(event);
console.log(event.pointerType);
console.log(event.clientX, event.clientY);
});

PointerEvent 继承自 MouseEvent,因此既可以使用鼠标事件的属性,也可以使用 Pointer 事件独有的属性。

  1. event 对象的常用属性
属性说明
pointerId当前指针的唯一标识,用于区分多个活动指针
pointerType指针设备类型:mouse、touch、pen
isPrimary是否为当前指针类型的主指针
clientX / clientY指针相对于浏览器视口的坐标
pageX / pageY指针相对于文档的坐标
screenX / screenY指针相对于屏幕的坐标
offsetX / offsetY指针相对于事件目标元素内边距边缘的坐标
button本次事件中发生状态变化的按键
buttons当前按下的按键组合
pressure指针压力,范围为 0~1
width / height指针接触区域的宽度和高度
target事件的目标元素
currentTarget当前正在执行事件监听器的元素
type当前事件名称,如 pointerdown
preventDefault()阻止可取消事件的默认行为
  1. pointerType:判断指针设备
box.addEventListener('pointerdown', (event) => {
if (event.pointerType === 'mouse') {
console.log('鼠标操作');
} else if (event.pointerType === 'touch') {
console.log('触摸操作');
} else if (event.pointerType === 'pen') {
console.log('触控笔操作');
}
});

通过 pointerType,可以在同一套事件逻辑中区分 PC 和移动端输入设备。

  1. pointerId 与 isPrimary:区分多点触控
  • pointerId:区分不同的指针,例如两根手指同时触摸屏幕,会分别对应不同的 ID。
  • isPrimary:判断当前指针是否为该设备类型的主指针。例如,多指触摸时,通常第一根手指为主指针。
box.addEventListener('pointerdown', (event) => {
if (!event.isPrimary) return;
console.log('主指针ID:', event.pointerId);
});

注意:pointerId 不是永久唯一的,指针结束后,其 ID 可能被重新使用。

  1. 坐标属性

最常用的是 clientX 和 clientY。

box.addEventListener('pointermove', (event) => {
console.log(event.clientX, event.clientY);
console.log(event.pageX, event.pageY);
console.log(event.offsetX, event.offsetY);
});

拖拽开发通常使用 clientX/clientY 计算移动距离。

  1. button 与 buttons:判断按键
属性常见取值说明
button0主按键(通常是鼠标左键)
button1鼠标中键
button2次按键(通常是鼠标右键)
button-1本次事件没有按键状态变化
buttons0没有按键按下
buttons1主按键处于按下状态
buttons2次按键处于按下状态
buttons4中键处于按下状态

buttons 是位掩码,可以同时表示多个按键。

例如,只在鼠标左键按下时执行:

box.addEventListener('pointermove', (event) => {
if (event.pointerType === 'mouse' && event.buttons & 1) {
console.log('鼠标左键正在拖动');
}
});
  1. 其他属性(了解即可)

这些主要用于触控笔、绘图软件等特殊场景。

属性说明
tiltX触控笔 X 方向倾斜角度
tiltY触控笔 Y 方向倾斜角度
twist触控笔的旋转角度
tangentialPressure触控笔的切向压力
altitudeAngle触控笔相对于屏幕平面的角度
azimuthAngle触控笔在屏幕平面上的方向角度

这些属性的实际数据取决于硬件支持情况。

总结

对于普通 Web 开发,重点掌握以下属性即可:

使用场景重点属性
判断鼠标、触摸屏、触控笔pointerType
单指拖拽、多点触控pointerId、isPrimary
获取指针位置、计算拖拽距离clientX、clientY
判断鼠标按键button、buttons
判断事件目标target、currentTarget
绘图、手写板pressure、tiltX、tiltY

学习重点: pointerType、pointerId、clientX/clientY、button/buttons。其他属性根据项目需求再掌握即可。

单指拖拽#

<div class="box">拖动我</div>
<style>
.box {
position: fixed;
left: 50px;
top: 100px;
width: 120px;
height: 120px;
background-color: skyblue;
display: flex;
justify-content: center;
align-items: center;
touch-action: none;
user-select: none;
cursor: grab;
}
</style>
<script>
const box = document.querySelector('.box');
let startX = 0;
let startY = 0;
let startLeft = 0;
let startTop = 0;
let dragging = false;
let activePointerId = null;
// 记录当前按下的触摸指针
const activeTouches = new Set();
window.addEventListener(
'pointerdown',
(event) => {
if (event.pointerType !== 'touch') return;
activeTouches.add(event.pointerId);
// 多指触摸时取消拖拽
if (activeTouches.size > 1) {
dragging = false;
}
},
true,
);
box.addEventListener('pointerdown', (event) => {
// 鼠标仅允许左键拖拽
if (event.pointerType === 'mouse' && event.button !== 0) return;
// 只允许单指拖拽
if (event.pointerType === 'touch' && activeTouches.size !== 1) return;
if (activePointerId !== null) return;
const rect = box.getBoundingClientRect();
// 记录指针初始坐标
startX = event.clientX;
startY = event.clientY;
// 记录元素初始位置
startLeft = rect.left;
startTop = rect.top;
dragging = true;
activePointerId = event.pointerId;
// 捕获指针
box.setPointerCapture(event.pointerId);
});
box.addEventListener('pointermove', (event) => {
if (!dragging || event.pointerId !== activePointerId) return;
// 计算指针移动距离
const moveX = event.clientX - startX;
const moveY = event.clientY - startY;
// 更新元素位置
box.style.left = `${startLeft + moveX}px`;
box.style.top = `${startTop + moveY}px`;
});
function stopDrag(event) {
if (event.pointerType === 'touch') {
activeTouches.delete(event.pointerId);
}
if (event.pointerId === activePointerId) {
dragging = false;
activePointerId = null;
}
}
window.addEventListener('pointerup', stopDrag);
window.addEventListener('pointercancel', stopDrag);
</script>

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

移动Web端事件
https://f.youzi177.cn/posts/2026102301-yi-dong-web-duan-shi-jian/
作者
小封鸭
发布于
2026-10-22
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
小封鸭
小楼一夜听春雨,深巷明朝卖杏花
公告
欢迎来到我的博客。这里主要记录前端全栈和日常内容
分类
标签
最新动态
站点统计
文章
60
分类
3
标签
12
总字数
107,734
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
EdgeOne Pages
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录