I. Description of business scenarios

When adapting a finished PC site to mobile, we want the site to respond faster on mobile devices for a better user experience. In this case, we should use a mobile-specific event system, such as the TouchStart event instead of the Click event.

Why does it work better? According to the Google developer documentation:

mobile browsers will wait approximately 300ms from the time that you tap the button to fire the click event. The reason for this is that the browser is waiting to see if you are actually performing a double tap.

Browsers on mobile devices will delay the click event by 300ms to ensure that it is a “click” event rather than a “double click” event.

In the case of the TouchStart event, the bound event is triggered the moment the user’s finger touches the screen. So the point of using touchstart instead of the click event is to save the user 300ms per click. This technique is necessary in situations where the page is frequently clicked, or where clicking occurs in an animation that requires high animation fluency.

However, going back to our original scenario, we can’t simply replace the TouchStart and Click events when adapting the PC site to the mobile because the PC doesn’t recognize the TouchStart events.

Ii. Causes of conflicts

Of course, we could bind both the TouchStart and click events to an element, but that would lead to the problem that this article addresses — the two events would clash on mobile devices.

Since mobile devices recognize both touchstart and click events, when the user clicks on the target element, the touchstart and click events bound to the target element (about 300ms later) are fired in sequence, which means that our bound callback is executed twice! . This is obviously not what we want.

Iii. Solutions

There are two solutions to this situation:

(1) Use preventDefault

The first solution is to use the preventDefault method in the event object, as explained on the MDN:

The Event interface’s preventDefault() method tells the user agent that if the event does not get explicitly handled, its default action should not be taken as it normally would be.

The preventDefault method is: Prevents the default event behavior of the element from occurring, but interestingly, when we bind both touchStart and Click events to the target element, using this method in the TouchStart event callback prevents subsequent click events from occurring.

This doesn’t make sense. After all, the click event we added is not the element’s “default event,” but it does work, or is implemented by the browser, so we can use this method to resolve the conflict between the TouchStart and Click events on mobile devices, as follows:

const Button = document.getElementById("targetButton")

Button.addEventListener("touchstart", e => {
    e.preventDefault()
    console.log("touchstart event!")
})

Button.addEventListener("click", e => {
    e.preventDefault()
    console.log("click event!")})Copy the code

When you click on the target element after emulating the mobile device in the browser, you only see the TouchStart Event on the console! Field, obviously, the click event was successfully blocked.

conclusion

The nice thing about this is that it’s simple, it’s straightforward, and it’s going to be great for our goal, but the downside is that preventDefault is browser implemented to prevent click events, not preventDefault, which creates some uncertainty. Although I haven’t found a specific scenario where this method fails yet.

(2) Detect binding events based on functions

The second way, inspired by this blog post, is to wrap the element’s click event by checking whether the browser supports the TouchStart event, so that the client determines the type of event to which the element should be bound based on the current environment, as follows:

const Button = document.getElementById("targetButton")

const clickEvent = (function() {
  if ('ontouchstart' in document.documentElement === true)
    return 'touchstart';
  else
    return 'click'; }) (); Button.addEventListener(clickEvent, e => { console.log("things happened!")})Copy the code

conclusion

The advantage of this approach is that we eliminate an unnecessary event binding for the element by adding a judgment, thus avoiding the touchStart/click event collision issue. This approach saves us from having to write the same code twice, and is more logical than the first approach, so it’s my recommendation.