You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

同一元素绑定触摸与点击事件时出现的浏览器特定异常问题咨询

Unexpected Behavior When Binding Touch and Click Events to the Same Element

I’ve run into a browser-specific quirk when attaching both touch and click events to an element, paired with CSS state handling that misbehaves across devices. Let’s break down the scenario, the code involved, and how to resolve it.

The Core Issue

When using both touch events (like touchstart/touchend) and click events on the same button, you’ll notice two key problems:

  • Mobile browsers often fire both events in sequence (touch first, then click after a 300ms delay), leading to duplicate actions.
  • The CSS :active state behaves inconsistently between touch and mouse inputs, causing visual glitches (like the red overlay not hiding properly during touch interactions).

Relevant Code

Here’s the code that demonstrates the issue:

HTML

<button class="button"> </button>

CSS

html,body,div,button {margin:0;padding:0;border:0;background:transparent;outline:none;}
.button { 
  position: relative; 
  width: 100px; 
  height: 50px; 
  background: #0f0; 
}
.button:after { 
  position: absolute; 
  display:block; 
  content: ''; 
  background: #f00; 
  top: 0; 
  bottom:0; 
  left: 0; 
  right:0; 
  margin: auto; 
  display:none; 
}
.button:not(:active):after { display:block; }

JavaScript

// Find target element
var button = $("button")

// Bind click event
button.on('click', function() {
  console.log('Click event triggered');
});

// Bind touch event
button.on('touchend', function() {
  console.log('Touch event triggered');
});

Why This Happens

  1. Dual Event Execution: Mobile browsers historically add a 300ms delay after touch events to detect double-taps for zoom. During this delay, they also fire a click event—so both touch and click handlers run, causing unintended duplicate actions.
  2. CSS State Mismatch: The :active state works differently for touch vs. mouse. On touch devices, the :active state might not persist through the entire touch interaction, making the :not(:active):after rule show the red overlay when it shouldn’t.

Practical Fixes

  • Prevent Click After Touch: Add event.preventDefault() to your touch handler to stop the browser from firing the subsequent click event. Note: This may disable other default behaviors like text selection.
    button.on('touchend', function(e) {
      e.preventDefault();
      console.log('Touch event triggered');
    });
    
  • Use Unified Pointer Events: Replace separate touch/click events with pointerdown/pointerup events. These work seamlessly for both mouse and touch inputs, eliminating duplicate triggers:
    button.on('pointerup', function() {
      console.log('Pointer event triggered');
    });
    
  • Optimize CSS for Touch: Add touch-action: manipulation to the button to remove the 300ms delay and improve :active state consistency on touch devices:
    .button { 
      /* existing styles */
      touch-action: manipulation;
    }
    

Content of the question originates from Stack Exchange, question author jebunin

火山引擎 最新活动