移动端事件详解:触摸、滑动与点击的全面指南

更新日期: 2025-12-02 阅读: 21 标签: 事件

在移动端开发中,事件系统和传统电脑浏览器有很大区别。相比鼠标的点击、移动、拖拽,移动端主要靠"手指操作"——包括触摸、滑动、点击、双击、长按、缩放、旋转等。

本文将重点介绍移动端常见的触摸事件、滑动事件和点击事件,并讲解开发中经常遇到的300毫秒点击延迟和点透问题。


一、触摸事件(Touch Events)

触摸事件是移动端最基础、最核心的事件类型。当用户用手指操作屏幕时,系统会触发这些事件:

事件名触发时机说明
touchstart手指碰到屏幕时类似电脑端的mousedown
touchmove手指在屏幕上滑动时可以获取手指移动轨迹
touchend手指离开屏幕时类似电脑端的mouseup
touchcancel系统中断触摸行为时比如来电、弹窗导致触摸中断

重要提示:每个事件的回调函数都会收到一个TouchEvent对象,可以通过以下属性获取触点信息:

  • touches:当前屏幕上所有手指的列表

  • targetTouches:当前元素上所有手指的列表

  • changedTouches:本次事件涉及的手指列表

示例代码

document.addEventListener('touchmove', e => {
  const touch = e.touches[0];
  console.log(`手指位置:(${touch.clientX}, ${touch.clientY})`);
});


二、滑动事件(Swipe)

滑动事件不是浏览器原生事件,而是第三方库(比如Zepto、jquery Mobile)基于触摸事件封装的高级事件。

常见类型:

事件名触发条件
swipe任意方向滑动
swipeLeft向左滑动
swipeRight向右滑动
swipeUp向上滑动
swipeDown向下滑动

原理简单说明

  1. 在touchstart时记录起始坐标

  2. 在touchend时记录结束坐标

  3. 对比移动方向和距离,判断是什么类型的滑动

示例代码:

let startX, startY;

document.addEventListener('touchstart', e => {
  startX = e.touches[0].clientX;
  startY = e.touches[0].clientY;
});

document.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('向上滑动');
    }
  }
});


三、点击事件(Tap与Click)

1. tap事件(Zepto封装)

浏览器没有原生的tap事件。tap是Zepto、FastClick等库对触摸事件的封装。原理是:在touchstart和touchend间隔很短、并且没有明显滑动时,判断为一次轻触。

常见事件:

  • tap:轻触屏幕

  • longTap:长按屏幕

  • singleTap:单击屏幕

  • doubleTap:双击屏幕

2. click事件与300毫秒延迟

在移动浏览器中,click事件有300毫秒延迟。原因是浏览器需要判断用户是否要进行双击缩放操作。也就是说,系统会等300毫秒,看你会不会再点一次。

解决300毫秒延迟的方法:

方法一:用touchstart/touchend代替click

const button = document.getElementById('btn');
let touchStartTime;

button.addEventListener('touchstart', () => {
  touchStartTime = Date.now();
});

button.addEventListener('touchend', (e) => {
  const touchDuration = Date.now() - touchStartTime;
  
  // 如果触摸时间很短(比如小于300ms),就认为是点击
  if (touchDuration < 300) {
    e.preventDefault(); // 防止触发click事件
    handleButtonClick(); // 执行点击处理函数
  }
});

方法二:使用FastClick库

// 安装FastClick后
if ('addEventListener' in document) {
  document.addEventListener('domContentLoaded', function() {
    FastClick.attach(document.body);
  }, false);
}

方法三:在meta标签中禁用缩放

<meta name="viewport" content="width=device-width, user-scalable=no">


四、点透问题(Click Through Issue)

问题场景

假设有两个上下重叠的元素A和B:

  • A在上层,绑定了touch事件

  • B在下层,绑定了click事件

用户点击A时,A触发touch事件然后隐藏。浏览器在300毫秒后触发click事件,此时点击落在B上,导致B的click事件被误触发——这就是点透。

解决方案

方案一:在touchend时阻止默认行为

A.addEventListener('touchend', e => {
  e.preventDefault(); // 阻止后续的click事件
  A.style.display = 'none'; // 隐藏元素
});

方案二:延迟隐藏上层元素

A.addEventListener('touchstart', () => {
  // 延迟300毫秒再隐藏,让click事件先触发
  setTimeout(() => {
    A.style.display = 'none';
  }, 300);
});

方案三:使用FastClick库
FastClick会自动处理点透问题。


五、完整示例:实现自定义手势识别

class TouchGesture {
  constructor(element) {
    this.element = element;
    this.startX = 0;
    this.startY = 0;
    this.startTime = 0;
    this.isSwiping = false;
    
    this.init();
  }

  init() {
    this.element.addEventListener('touchstart', this.onTouchStart.bind(this));
    this.element.addEventListener('touchmove', this.onTouchMove.bind(this));
    this.element.addEventListener('touchend', this.onTouchEnd.bind(this));
  }

  onTouchStart(event) {
    const touch = event.touches[0];
    this.startX = touch.clientX;
    this.startY = touch.clientY;
    this.startTime = Date.now();
    this.isSwiping = false;
  }

  onTouchMove(event) {
    if (!this.isSwiping) {
      const touch = event.touches[0];
      const deltaX = touch.clientX - this.startX;
      const deltaY = touch.clientY - this.startY;
      
      // 如果移动距离超过10像素,认为是滑动
      if (Math.abs(deltaX) > 10 || Math.abs(deltaY) > 10) {
        this.isSwiping = true;
      }
    }
  }

  onTouchEnd(event) {
    const touch = event.changedTouches[0];
    const endX = touch.clientX;
    const endY = touch.clientY;
    const endTime = Date.now();
    
    const deltaX = endX - this.startX;
    const deltaY = endY - this.startY;
    const duration = endTime - this.startTime;
    
    // 如果是点击(不是滑动)
    if (!this.isSwiping) {
      if (duration < 300) {
        // 短时间触摸,触发点击
        this.emit('tap', { x: endX, y: endY });
      } else {
        // 长时间触摸,触发长按
        this.emit('longtap', { x: endX, y: endY, duration });
      }
    } else {
      // 处理滑动
      if (Math.abs(deltaX) > Math.abs(deltaY)) {
        // 水平滑动
        if (deltaX > 50) {
          this.emit('swiperight', { deltaX, deltaY, duration });
        } else if (deltaX < -50) {
          this.emit('swipeleft', { deltaX, deltaY, duration });
        }
      } else {
        // 垂直滑动
        if (deltaY > 50) {
          this.emit('swipedown', { deltaX, deltaY, duration });
        } else if (deltaY < -50) {
          this.emit('swipeup', { deltaX, deltaY, duration });
        }
      }
    }
  }

  emit(eventName, data) {
    const event = new CustomEvent(eventName, { detail: data });
    this.element.dispatchEvent(event);
  }
}

// 使用示例
const box = document.getElementById('touch-box');
const gesture = new TouchGesture(box);

box.addEventListener('tap', (e) => {
  console.log('点击事件', e.detail);
});

box.addEventListener('swipeleft', (e) => {
  console.log('向左滑动', e.detail);
});


六、总结对比

类型特点注意事项
Touch原生事件,很灵活需要自己封装功能
Swipe第三方封装,能判断滑动方向不是标准事件
Tap类似click,但没有延迟需要引入库或自己封装
Click原生支持,兼容性好有300毫秒延迟和点透风险

七、实际开发建议

  1. 交互敏感区域:用touchstart事件,响应更快

  2. 滚动或拖动:监听touchmove事件

  3. 普通点击操作:用tap事件,避免300毫秒延迟

  4. 避免混用:不要同时用click和touch事件处理同一个功能

  5. 性能优化:及时移除不需要的事件监听器

  6. 兼容性考虑:测试不同手机和浏览器的表现

掌握这些移动端事件知识,不仅能让你的页面响应更快,还能显著提升用户体验。在实际项目中,根据具体场景选择合适的事件类型,可以避免很多常见问题。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/13250

js中addEventListener事件监听器参数详解

我们都知道addEventListener() 的参数约定是:useCapture是可选参数,默认值为false,目前DOM 规范做了修订:addEventListener() 的第三个参数可以是个对象值了。passive就是告诉浏览器我可不可以用stopPropagation...

js监听浏览器返回,pushState,popstate 事件,window.history对象

在WebApp或浏览器中,会有点击返回、后退、上一页等按钮实现自己的关闭页面、调整到指定页面、确认离开页面或执行一些其它操作的需求。可以使用 popstate 事件进行监听返回、后退、上一页操作。

CSS中的pointer-events属性实现点穿效果

具有层级关系的结构中,使用了pointer-events:none 属性将会使当前元素中的事件不会被捕获,从而实现了点穿的效果。而当代码示例中假如top元素具有子元素且显示指定pointer-events属性不为none的时候,top元素注册的事件将会被捕获/冒泡触发

js鼠标事件参数,获取鼠标在网页中的坐标

事件对象 event,JavaScript 将事件event作为参数传递,IE中把 event 事件对象作为全局对象 window 的一个属性,获取鼠标在网页中的坐标 = 鼠标在视窗中的坐标 + 浏览器滚动条坐标

js事件冒泡和默认事件处理(原生js、vue)

何为默认事件?比如 a 会跳转页面,submit 会提交表单等。普通js方法:e.preventDefault()函数。Vue.js方法: .prevent 是vue 的内置修饰符,调用了 event.preventDefault()阻止默认事件

js keyup、keypress和keydown事件 详解

js keyup、keypress和keydown事件都是有关于键盘的事件,当一个按键被pressed 或released在每一个现代浏览器中,都可能有三种客户端事件。

深入nodejs-核心模块Events详解(事件驱动)

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O的模型,使其轻量又高效。比如,文件操作中的fs事件流,网络编程所用到的tcp,http模块等,当你回想自己写的程序后,会发现很多操作都基于事件驱动,Events类。

纯CSS实现点击事件展现隐藏div菜单列表/元素切换

在写移动端导航的时候经常用到点击按钮出现/隐藏导航条的情况,最常见的方法当然还是前端框架直接调用,省心省力,不易出错;当然还有使用纯JS实现的小代码段。我这里整理了纯CSS实现方式,给需要的人和给自己做个笔记:实现原理利用CSS伪类:target

关于鼠标移动太快导致moseleave事件不触发的问题

我做的是一个table的编辑功能,当移入某行的时候展示编辑状态,在移出某行的时候显示的是原始状态,此时遇到一种情况,就是.当mousenter事件触发之后,由于鼠标移动得太快,同一个tr上绑定的mouseleave事件压根儿就没有执行。

Js事件传播流程

js事件传播流程主要分三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。在我们平常用的addEventListener方法中,一般只会用到两个参数,一个是需要绑定的事件,另一个是触发事件后要执行的函数

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!