移动端有时候触发不了touchend 使用event.preventDefault()后页面无法滚动

在移动端开发中,遇到touchend事件有时无法触发的问题。为解决此问题,通常会使用event.preventDefault()阻止默认行为,但这同时也阻止了页面滚动。文章介绍了touchcancel和touchend事件的区别,并提出了解决方案:将两个事件绑定到同一处理方法,避免阻止页面滚动的同时确保能监听到手指抬起事件。
  • 使用移动端设备监听手指触摸事件时发现有时候无法触发touchend事件,因此在监听touchend事件时通过 阻止页面默认事件 event.preventDefault()来实现事件监听,但是发现页面的滚动事件也被阻止了。怎么样既不会阻止页面滚动又可以监听手指抬起事件?
  • 通过查看资料发现元素上绑定了touchcanceltouchend两个事件:
    1、长按后不移动直接抬起手指,触发的是touchcancel;
    2、长按后轻轻移动一下再抬起手指,触发的是touchend;
  • 针对这细微的变化实际上用户很难去辨别,因此给元素这两个事件绑定同一个方法,此时不再需要阻止页面默认事件也可以触发手指抬起动作:
<div
    class="chat"
    id="chat"    
    @touchcancel="handleTouchEnd" 
    @touchend="handleTouchEnd"
  ></div>
// useHandleEvent.js import { ref, onMounted, onUnmounted } from "vue" interface EventType { type: "click" | "dblclick" | "longclick" cell: { row: number; col: number; element: any } originalEvent: MouseEvent | TouchEvent | any } export function useHandleEvent(containerRef: Ref<HTMLElement>, callback: (cb: EventType) => void) { const touchInfo = ref<{ startX: number startY: number startTime: number target: any } | null>(null) const isMobile = ref(false) const lastClickInfo = ref({ time: 0, target: null as any }) const longPressTimer = ref<NodeJS.Timeout | null>(null) const isLongPress = ref(false) const detectMobile = () => { return ( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1) ) } // 获取事件单元格信息 const getCellFromEvent = (event: any) => { if (!event) return null let target = event.target while (target && target !== containerRef.value) { if (target.dataset?.row !== undefined && target.dataset?.col !== undefined) { return { row: parseInt(target.dataset.row), col: parseInt(target.dataset.col), element: target } } target = target.parentNode } return null } // 触发回调的统一方法 const triggerCallback = (type: "click" | "dblclick" | "longclick", cell: any, event: any) => { const resData = { type, cell, originalEvent: event } return callback(resData) } // 处理鼠标按下事件 const handleMouseDown = (event: MouseEvent) => { // 阻止右键菜单 if (event.button === 2 || event.button === 3) { event.preventDefault() return } const cell = getCellFromEvent(event) if (!cell) return // 设置长按定时器 longPressTimer.value = setTimeout(() => { isLongPress.value = true triggerCallback("longclick", cell, event) }, 500) } // 处理鼠标抬起事件 const handleMouseUp = (event: MouseEvent) => { if (longPressTimer.value) { clearTimeout(longPressTimer.value) longPressTimer.value = null } if (isLongPress.value) { isLongPress.value = false return } const cell = getCellFromEvent(event) if (!cell) return const now = Date.now() if (lastClickInfo.value.target === cell && now - lastClickInfo.value.time < 300) { triggerCallback("dblclick", cell, event) lastClickInfo.value = { time: 0, target: null } } else { triggerCallback("click", cell, event) lastClickInfo.value = { time: now, target: cell } } } // 处理触摸开始事件 const handleTouchStart = (event: TouchEvent) => { if (event.touches.length > 1) return const touch = event.touches[0] const cell = getCellFromEvent(event) if (!cell) return touchInfo.value = { startX: touch.clientX, startY: touch.clientY, startTime: Date.now(), target: cell } longPressTimer.value = setTimeout(() => { event.preventDefault() isLongPress.value = true triggerCallback("longclick", cell, event) }, 500) } // 处理触摸结束事件 const handleTouchEnd = (event: TouchEvent) => { if (longPressTimer.value) { clearTimeout(longPressTimer.value) longPressTimer.value = null } if (!touchInfo.value) return if (isLongPress.value) { isLongPress.value = false touchInfo.value = null return } const touch = event.changedTouches[0] const cell = getCellFromEvent(touch) if (!cell || cell !== touchInfo.value.target) { touchInfo.value = null return } const moveDistance = Math.sqrt( Math.pow(touch.clientX - touchInfo.value.startX, 2) + Math.pow(touch.clientY - touchInfo.value.startY, 2) ) if (moveDistance < 10) { const now = Date.now() if (lastClickInfo.value.target === cell && now - lastClickInfo.value.time < 300) { triggerCallback("dblclick", cell, event) lastClickInfo.value = { time: 0, target: null } } else { triggerCallback("click", cell, event) lastClickInfo.value = { time: now, target: cell } } } touchInfo.value = null } // 阻止默认的右键菜单行为 const handleContextMenu = (event: MouseEvent) => { event.preventDefault() } // 鼠标移出时清除长按状态 const handleMouseLeave = () => { if (longPressTimer.value) { clearTimeout(longPressTimer.value) longPressTimer.value = null } isLongPress.value = false } onMounted(() => { if (!containerRef.value) return isMobile.value = detectMobile() containerRef.value.addEventListener("mousedown", handleMouseDown) containerRef.value.addEventListener("mouseup", handleMouseUp) containerRef.value.addEventListener("mouseleave", handleMouseLeave) containerRef.value.addEventListener("contextmenu", handleContextMenu) containerRef.value.addEventListener("touchstart", handleTouchStart, { passive: true }) containerRef.value.addEventListener("touchend", handleTouchEnd, { passive: true }) }) onUnmounted(() => { if (!containerRef.value) return containerRef.value.removeEventListener("mousedown", handleMouseDown) containerRef.value.removeEventListener("mouseup", handleMouseUp) containerRef.value.removeEventListener("mouseleave", handleMouseLeave) containerRef.value.removeEventListener("contextmenu", handleContextMenu) containerRef.value.removeEventListener("touchstart", handleTouchStart) containerRef.value.removeEventListener("touchend", handleTouchEnd) if (longPressTimer.value) clearTimeout(longPressTimer.value) }) return { isMobile } } 请根据这段代码进行优化,在PC端和移动端统一都只返回按下down,弹起up,单击click,双击dblclick和长按longclick 5种事件,其他的都阻止屏蔽,请确保能准确区分识别单击,双击和长按事件
最新发布
08-06
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值