onClick() sometimes being ignored on iOS

The environment

React application (using TypeScript) running via Capacitor on both iOS and Android. The same app also runs in a browser outside of Capacitor.

The issue

Sometimes, roughly 5-10% of the times, the app ignores taps. As in, tapping the same component multiple times will call the onClick() function sometimes, but not others.

This only occurs when running on iOS via Capacitor. It does not lose any taps on Android via Capacitor, or on desktop browser outside Capacitor.

What makes it really weird? The unreliable behavior only shows up if there is also an onClick() on another element as well.

Sample Code


export interface Props { }
interface State {
  itemList: number[];
  tapCount: number;
}

export class DemoComponent extends React.Component<Props, State> {
  private clickHandler = this.handleClick.bind(this);

  public constructor(props: Props) {
    super(props);
    this.state = {
      tapCount: 0,
      itemList: [], // would normally be data about the item, but we'll just use index for now
    };
    setInterval(this.designItem.bind(this), 700); // design a new item every 700 msec
  }

  public render() {
    return (
      <div>
        <h2>onClick test</h2>
        <hr />

        <h4> Count: {this.state.tapCount} &nbsp;</h4>
        <div onClick={this.clickHandler} style={{ cursor: "pointer", background: "purple", color: "white", width: "200px", height: "100px", padding: "10px" }} >
          Increment
        </div>
        <hr />

        {this.state.itemList.map((id, i) =>
          <div id={"#" + id} style={{ width: "150px", cursor: "pointer" }} key={i}
            onClick={() => console.log("CLICK")} /* this line makes the previous onClick unreliable on iOS w/ Capacitor! */
          >

            <div style={{ width: "160px", height: "40px", padding: "10px", margin: "10px", background: "blue", color: "white" }}>
              {id}
            </div >
          </div>
        )}
      </div>
    );
  }

  private handleClick(e: any) {
    const tapCount = this.state.tapCount + 1;
    console.info("TrivialCharsheet.handleClick #" + tapCount);
    this.setState({ tapCount });
  }

  // simulate collecting data for each item
  private designItem() {
    if (this.state.itemList.length > 24) {
      this.setState({ itemList: [] });
    } else {
      this.state.itemList.push(this.state.itemList.length + 1);
      this.setState({ itemList: this.state.itemList });
    }
  }
}

While this is running, if you tap repeatedly on the “Increment button” (purple div at top) 20 times, you’ll typically see the count go up by 15-18 or so. If you disable the 2nd onClick (on the blue div), it will instead show all 20.

Things I’ve tried

Searching has turned up multiple questions which are somewhat similar to this, but they generally relate to:

  • bugs under older iOS versions
  • fast tapping (this problem occurs even with slow taps)
  • using the pointer cursor in the style (which is included here)
  • specific React component libraries (but this code doesn’t use any, just good old div)

Using onClick={() => handleClick()} syntax instead of the onClick={this.clickHandler} doesn’t make any difference.

Changing from onClick() to onTap() eliminates the problem with taps being ignored. However, in the real app the user can scroll, so I don’t believe that’s a solution.