Ionic 2: Fix iOS side-menu double tap bug

Are you using a side-menu in your app? Does it work fine in Android but crap in iOS? Here's the fix.

The Problem:

If you use a side-menu in your app, you will have probably followed the components example from the documentation and have something resembling this e.g.:

<ion-menu id="leftMenu" [content]="content" side="left">
    <ion-content>
        <ion-list>
            <ion-item *ngFor="let page of pages" (click)="openMenuPage(page)">
                <ion-icon name="{{page.icon}}" item-left></ion-icon>
                {{page.title}} 
            </ion-item>
        </ion-list>
    </ion-content>
</ion-menu>
<ion-nav id="nav" #content swipe-back-enabled="false"></ion-nav>

Note: where pages is an array of objects, containing the page component name and an icon.

This will work fine in Android, but suck in iOS. You will find that you need to tap twice, maybe 3 times on the menu item for it to register? This click laggy-ness seems to also happen on other elements like <ion-item> <ion-card> etc.

WTF?

The Fix:

Turns out this is because of the historical 300ms browser delay, that was originally introduced to guard against accidental taps.
Turns out we can also turn this off!

Simply add the 'tappable' attribute to any element in Ionic 2 that isn't either a:

  • button
  • a tag

and you will now get an instant click registered.

E.g. from the example above:

This:

<ion-item *ngFor="let page of pages" (click)="openMenuPage(page)">

Becomes this:

<ion-item tappable *ngFor="let page of pages" (click)="openMenuPage(page)">

Now re-run your app on an iOS device and it will work fine.

Conclusion:

Ionic documentation does mention this, but it's REALLY easy to miss. Here's the link: http://ionicframework.com/docs/troubleshooting/#click-delays

Cheers.

Update (15/05/2017)

Another Ionic dev has suggested to me that a better first approach would be to try to use buttons first, before resorting to adding the tappable attribute. This is for usability reasons.

So in the example above, the markup would change to:

<ion-menu id="leftMenu" [content]="content" side="left">
    <ion-content>
        <ion-list>
            <button ion-item *ngFor="let page of pages" (click)="openMenuPage(page)">
                <ion-icon name="{{page.icon}}" item-left></ion-icon>
                {{page.title}} 
            </button>
        </ion-list>
    </ion-content>
</ion-menu>
<ion-nav id="nav" #content swipe-back-enabled="false"></ion-nav>

Now because each row in the list is a button, there's no need to add the tappable attribute. Great.

As always though, there are caveats and potential render issues that may make your life more hell than it's worth. So I think the takeaway here is to go for accessibility first, but use your discretion to judge the correct approach for each situation you encounter.

Comments: