Stop button losing focus when contextmenu event fires

Viewed 353

I've got a react application that has a need for a piece of content to play when a button is held down and then stop when the button is released.

I've implemented this by creating a helper to handle detecting a longpress, when onMouseDown or onTouchStart is fired then it plays, when onMouseUp, onMouseLeave or onTouchEnd fires it stops. This all works perfectly on desktop. The issues comes when we take a look at mobile, mobile treats a long press as a right click in the desktop and tries to launch a contextmenu or highlight an element/piece of text, to deal with this I've added the following css:

touch-action: none;
user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;

and the following Javascript:

window.oncontextmenu = function (event) {
  event.preventDefault();
  event.stopPropagation();
  return false;
};

This works as expected in the sense that the contextmenu doesn't display, however the button that is being held does lose focus, meaning that the content continues to play and the onMouseUp, onMouseLeave or onTouchEnd events never fire unless you tap somewhere on the button or screen again, I've tried solutions like reapplying the focus to the button during the onContextMenu event however that doesn't work either.

2 Answers

Mobile user agents do not consider a long touch as a right click when the target element is a button (because there are no mobile compatible actions on a button, other than just click). I suggest you to verify that the button you're talking about is an actual button.

const button  = document.querySelector('button');

button.addEventListener('mousedown', function() { this.classList.add('red') });
button.addEventListener('touchstart', function() { this.classList.add('red') });

button.addEventListener('mouseup', function() { this.classList.remove('red') });
button.addEventListener('mouseleave', function() { this.classList.remove('red') });
button.addEventListener('touchend', function() { this.classList.remove('red') });
.red {
  background-color: red;
}
<a href="#">This link does trigger a context menu on a long touch</a> <button>This button doesn’t</button>

If that does not answer your question, please add the actual HTML code.

When using this on desktop clicking my mouse would fire the mousedown event, which is expected, when using this on mobile it was firing two events at the same time, meaning when it would fire touchend it would also fire mouseleave or mouseup, i'm unsure why this was happening but to resolve my issue I just conditionally added the event listeners, so for mobile I now only add event listeners for touchstart, touchend and touchcancel.

Related