0

How can I get a handler to run when the touch is canceled? (For example, so I can unhighlight a button when CSS doesn't do it for me). In older Chrome Mobile browsers, I can catch touchcancel events. However, in Chrome Mobile 108 (from 2022), there is no touchcancel:

enter image description here

What event is generated when the touch (and subsequent click event) is canceled due to timeout? (Note: moving the touch off the element is a separate question from 2011 with a decent polyfill.)

I am using the following script to catch events:

<html>

<head>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<style>
html, body, pre {
  position:absolute; top:0; left:0; right:0; bottom:0;
  margin: 0; user-select: none;
}
pre {margin: 10px; overflow-y:auto}
</style>
</head>
<body>
<pre id="ELEM"></pre>

<script>

ELEM = document.getElementById("ELEM")
ELEM.appendChild(document.createTextNode(""));

function Print(s)
{
  let t = (new Date() / 1000 % 60).toFixed(2).padStart(5,0);
  ELEM.firstChild.nodeValue += `${t}: ${s}\n`;
  ELEM.scrollTop = ELEM.scrollHeight;
}

Print("Hello, world!");
EVENTS = ["mouseup", "mousedown", "touchstart", "touchend", "click", "touchcancel", "mouseenter", "mouseout", "focusin"];
for (let e of EVENTS)
  document.body.addEventListener(e, () => {Print(e)});

</script>
</body>
</html>
personal_cloud
  • 3,943
  • 3
  • 28
  • 38

1 Answers1

0

I'm really really hoping there's a way to fix this without rewriting click. However, otherwise (or in the meantime) here is a possible approach:

  • The application ignores the built-in click event (as we can't catch its timeout).
  • On touchstart, start a timeout (what time value should I use?).
    • If the timeout fires, then generate a normal touchcancel event.
  • On touchend, if the timeout has not fired, then generate a click event to the application and clear the timeout.

Here is an example of a Better_Click() function that uses the above method to launch touchcancel and better-click events to the application:

<html>

<head>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<style>
html, body, pre {
  position:absolute; top:0; left:0; right:0; bottom:0;
  margin: 0; user-select: none;
}
pre {margin: 10px; overflow-y:auto}
</style>
</head>
<body>
<pre id="ELEM"></pre>

<script>

ELEM = document.getElementById("ELEM")
ELEM.appendChild(document.createTextNode(""));

function Print(s)
{
  let t = (new Date() / 1000 % 60).toFixed(2).padStart(5,0);
  ELEM.firstChild.nodeValue += `${t}: ${s}\n`;
  ELEM.scrollTop = ELEM.scrollHeight;
}

function Better_Click(el)
{
  let to;
  function timeout()
  {
    to = undefined;
    el.dispatchEvent(new CustomEvent("touchcancel", {}));
  }
  el.addEventListener('touchstart', () => to = setTimeout(timeout, 700));
  el.addEventListener('touchend',   () => {
    if (to === undefined)
      return;
    clearTimeout(to);
    to = undefined;
    el.dispatchEvent(new CustomEvent("better-click", {}));
  });
}
Better_Click(document.body);


Print("Hello, world!");
EVENTS = ["mouseup", "mousedown", "touchstart", "touchend", "touchcancel", "mouseenter", "mouseout", "better-click"];
for (let e of EVENTS)
  document.body.addEventListener(e, () => Print(e));

</script>
</body>
</html>
personal_cloud
  • 3,943
  • 3
  • 28
  • 38