Fixes for tap/click events ready for testing

We’ve done a significant overhaul of the tap/click system, basically to remove the 300 millisecond delay between tapping an element and firing its click method. There are a few pending issues with Ionic (like Android firing the click twice), but before I start to close them out I was hoping to get a few more testers involved to verify the fixes are working as expected.

The updates are in the master branch (v0.9.22-alpha), but not in a release yet. If you’ve got a second we could use to assistance testing.

A few notes about tap detection:

  • When it comes to other tap detection solutions, much of the code to detect a tap we already have in Ionic. Therefore, so we hate to duplicate code and create more global event listeners which in the end do the same thing. Plus with ionic’s event system it’s reusable, like ionic.on('tap', function(){})
  • ngTouch is good, but it does tap detection for the ng-click directive, and not everything. So standard <a href> links and focusing in on inputs wouldn’t have removed the 300ms delay. Ionic instead removes the 300ms delay for almost every touch interaction (like tapping inside a textarea and it quickly focuses without having to add ng-click to it). And for the same reasons of bullet point 1, we chose not to include duplicate code and want to reduce dependencies/code. However, if you are not using Ionic and just Angular, then I recommend using ngTouch.
  • fastclick.js is also a widely known tap detection solution. It works great, highly tested and used by many. However, out of the box it didn’t work well with angular, and we were starting to add too many hacks to get it to work. And again, it’s duplicate code (which isn’t reusable), it adds more listeners and more overhead, and relying on a 3rd party dependency is something we’d like to avoid. But if you’re not using Ionic/Angular and looking for tap detection then I highly recommend fastclick.

The good news is that browsers are starting to remove the 300ms delay if zooming has been disabled via the viewport meta tag (and for that reason is another reason why simple tap solutions gets wonky). Right now I recommend this meta tag to go in the <head> of your Ionic app:

<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">

Please let me know how its working and what we can do to improve it.
Thanks

2 Likes

Hi Adam, this is to confirm that double click issue has been resolved on Android 4.1 through 4.3.

Thanks for all your efforts!

1 Like

Confirmed here, too. Even works on Android 2.3. :smile:

1 Like

Tested on Android 4.4 and obviously… it works

1 Like

@adam Just dabbling a bit right now. On the surface, all of my click events are now dead. Nothing works. What I’ve done so far is simply replace the ionic.js, ionic-angular.js, and ionic.css files from master.

Is there something else that needs to be done? Do we NOT include angular-touch now?

UPDATE : Ionic controlled events work. Like toggling a menu. But my own ng-click handlers do not work. Like :

<div family-member ng-repeat="member in family.members" ng-click="showPasscodeForm(member.membersId)"></div>

FYI : From previously using 0.9.20

The problem seems to be related to ng-repeat. Here is a sample of code I used in my side menu :smile:

<item ng-repeat="item in menuItems" item="item" ng-click="{{item.action}}">{{item.label}}</item>

Basically, my side menu items are generated on the fly. Each item will have an action to perform on click. Now when I use the syntax ng-click="{{item.action}}, I get this error :

Error: [$parse:syntax] Syntax Error: Token 'item.action' is unexpected, expecting [:] at column 3 of the expression [{{item.action}}] starting at [item.action}}].
http://errors.angularjs.org/1.2.8/$parse/syntax?p0=item.action&p1=is%20unexpected%2C%20expecting%20%5B%3A%5D&p2=3&p3=%7B%7Bitem.action%7D%7D&p4=item.action%7D%7D

I did NOT get that error on 0.9.20. I’m not sure if Ionic is causing that or AngularJS. However, I’ve used the same in other non-ionic projects.

Changing to this prevents the error, but ng-click does not work:

<item ng-repeat="item in menuItems" item="item" ng-click="item.action">{{item.label}}</item>

So, that’s an edge case.

Here’s a non-edge case. The code below will not fire the ng-click.

<div family-member ng-repeat="member in fc.family.members" ng-click="showPasscodeForm(member.membersId)"></div>

More Info. In Chrome, if I disable touch emulation, the click on the family members works properly. Clicks on menu items still don’t work.

@adam I’ve upgraded to .22-alpha and unfortunately I am experiencing the same issue as @Calendee.

But its been broken since moving from .20 to .21

@Calendee @gregorypratt, do you have angular-touch included at all? If not, could you try including it and then adding ngTouch to the modules for your angular app?

I’m curious if it’s related to ng-click not getting the simulated click event.

@max - I normally have angular-touch included (we’re we supposed to?). Anyway, I also tried removing it completely. that did not help.

@Calendee, for your item directive be sure to add () at the end of the method name within the ng-click, such as:

<item ng-repeat="item in menuItems" item="item" ng-click="item.action()">{{item.label}}</item>

And within the controller it could look something like:

$scope.menuItems = [
  { 
    label: 'Label',
    actionItem: function() {
      console.log('actionItem')
    }
  }
];

For your familyMember directive, would you be able to post the code? And if you’d rather not post it here would you be able to email it to me at adam at drifty com? It may help me track down where I might not have something isn’t hooked up right.

Also for everyone, I’ve excluded ngTouch from angular.module('ionic', [ within ionic-angular.js, so even if the script is in the head tag it won’t matter. If you’d still like to use ngTouch you can add it to your app’s module, like angular.module('ionicApp', ['ionic', 'ngTouch']).

However, if you step through the click events you’ll see that they get fired twice after ngTouch is enabled. To see what I mean take a look at /js/ext/angular/test/clickTests.html in the repo. With just angular.module('ionicApp', ['ionic']) then the ng-click fires just once, but with angular.module('ionicApp', ['ionic', 'ngTouch']) the ng-click fires twice.

Still more to research on this, thanks for all the help so far.

@adam : Let me try to give as much detail as I can :

Issue : {{item.action}} no longer working:

The menu is built from this array. The item.action is based on the array below:

var parentDrawer = [
    {
        label : 'Home',
        action : 'goTo("home")'
    },
    {
        label : 'Manage Family',
        action : 'goTo("family")'
    },
    {
        label : 'Friends',
        action : 'goTo("friends")'
    },
    {
        label : 'Account',
        action: 'goTo("account")'
    },
    {
        label : 'Logout',
        action: 'logout()'
    },
    {
        label : 'About',
        action: 'about()'
    }
];

Ionic 0.9.20 & Angular 1.2.8:

Works Properly :

<item ng-repeat="item in menuItems" item="item" ng-click="{{item.action}}">{{item.label}}</item>

Working Demo : http://jsbin.com/iFuwOmOl/2/edit?html,css,js,console,output


Ionic 0.9.22 & Angular 1.2.8:

Fails :

Failing Demo : http://jsbin.com/ubUrUkel/1/edit?html,css,js,console,output

<item ng-repeat="item in menuItems" item="item" ng-click="{{item.action}}">{{item.label}}</item>

Error :

Error: [$parse:syntax] Syntax Error: Token 'item.action' is unexpected, expecting [:] at column 3 of the expression [{{item.action}}] starting at [item.action}}].
http://errors.angularjs.org/1.2.8/$parse/syntax?p0=item.action&p1=is%20unexpected%2C%20expecting%20%5B%3A%5D&p2=3&p3=%7B%7Bitem.action%7D%7D&p4=item.action%7D%7D
    at http://localhost:8000/js/angular/angular.js:78:12
    at Parser.throwError (http://localhost:8000/js/angular/angular.js:9897:11)
    at Parser.consume (http://localhost:8000/js/angular/angular.js:9934:12)
    at Parser.object (http://localhost:8000/js/angular/angular.js:10239:14)
    at Parser.primary (http://localhost:8000/js/angular/angular.js:9865:22)
    at Parser.unary (http://localhost:8000/js/angular/angular.js:10123:19)
    at Parser.multiplicative (http://localhost:8000/js/angular/angular.js:10106:21)
    at Parser.additive (http://localhost:8000/js/angular/angular.js:10097:21)
    at Parser.relational (http://localhost:8000/js/angular/angular.js:10088:21)
    at Parser.equality (http://localhost:8000/js/angular/angular.js:10079:21) <div class="item item-complex" ng-repeat="item in menuItems" item="item" ng-click="{{item.action}}"> 

Ionic 0.9.22 & Angular 1.2.8:

Fails :

<item ng-repeat="item in menuItems" item="item" ng-click="item.action">{{item.label}}</item>

Simply no clicks register


Ionic 0.9.22 & Angular 1.2.8:

Fails :

<item ng-repeat="item in menuItems" item="item" ng-click="item.action()">{{item.label}}</item>

Error :

TypeError: string is not a function
    at http://localhost:8000/js/angular/angular.js:10199:15
    at http://localhost:8000/js/angular/angular.js:17834:17
    at Scope.$eval (http://localhost:8000/js/angular/angular.js:11949:28)
    at Scope.$apply (http://localhost:8000/js/angular/angular.js:12049:23)
    at HTMLDivElement.<anonymous> (http://localhost:8000/js/angular/angular.js:17833:21)
    at http://localhost:8000/js/angular/angular.js:2612:10
    at forEach (http://localhost:8000/js/angular/angular.js:309:20)
    at HTMLDivElement.eventHandler (http://localhost:8000/js/angular/angular.js:2611:5)
    at inputTapPolyfill (http://localhost:8000/js/ionic/ionic.js:1914:9)
    at HTMLDocument.tapPolyfill (http://localhost:8000/js/ionic/ionic.js:1954:16) 

@adam : Here’s a very simple demo of the click failing on the home page. I did it this way to show that the problem is not really because of my familyMember directive.

Be sure to have the console tab open in with Touch events turned off in Chrome, you can click on each of the “Person” divs. The clicks will register in the console.

Then, turn emulate touch events on & then refresh the page. Then click on each of the Person’s. The click events will not register.

NOTE : If you turn the “Emulate Touch Screen” on and don’t refresh the page, CodePen registers the clicks.

0.9.22 version : http://codepen.io/calendee/pen/yCbnE

Now, here is a working version using 0.9.20

0.9.20 version : http://codepen.io/calendee/pen/wxHvC

Now, do this. Open in both versions in 2 tabs. Make sure that “Emulate Touch Events” is OFF.

In the 0.9.20 version, rapidly click Person 1. Watch how quickly the console logs them.
In the 0.9.22 version, rapidly click Person 2. Notice how many events don’t fire?

@Calendee Ok I put some more work into correctly tying ng-click to the tap polyfill. Its all in master and I updated the nightly directory in the CDN. I also cloned your jsbin and it appears to work:

Would you be able to test out the latest code and see if it working for you?

And for anyone else, it’d be great to have others beat on the code from various devices. A good tap/click testing page in the repo is at:
/js/ext/angular/test/clickTests.html

(at the root of the repo, fire up a local web server, like using python -m SimpleHTTPServer)

@adam Thanks for all the work. There is still a problem with clicking.

See this : http://cdpn.io/sBDlh

Have the console open and rapidly click on any “Person”. It will only register a click every few seconds.

Compare to : http://codepen.io/calendee/pen/wxHvC
This one with 0.9.20 will register clicks as fast as you can make them.

I see what you mean now. Part of that is by design because of “ghostclicks” that happen by touch devices. There are scenarios where you tap a button and the action fires off, and then the touch device’s browser will also fire a click immediately afterwards in the same location. Android is pretty bad at doing this, especially if you click “near” an element, it still assumes that you meant to click that element.

So there’s a short delay that if the same area is clicked immediately again then the click be ignored. So if you click every 400ms they’ll all register. If you click really fast they won’t.

But now that you pointed this issue out I might be able to rework it a bit so many clicks still register at least every 400ms.

How’s everything else working?

Also, thanks for helping us track down these issues. You’ve been a great help!

I just finished testing across a few devices. Wow! Smooth as butter. All of the weird back button issues are gone. The strange “sometimes 2 clicks” or “sometimes 4 clicks” problem has vanished.

There are a few breaking CSS changes for me. I haven’t dug into them, but I imagine they are due to my own hacks.

Short of that issue above about really fast clicks not registering, this cleared up quite a few issues for me. However, that is pretty unrealistic for most types of apps.

Thanks so much.

It seems like Toggles stopped working on desktops/chrome with the new code. On iOS they still work. (Could be something wrong with my code, but just wanted to throw it out there and see if anyone else has the same problem). The new code did solve the problem of having to click the back-button several times though :slight_smile:

@jorgtron You are right. @adam There’s a problem with toggles when Emulate Touch Screen is turned off. They only work for touch devices.

Also, have a new issue because $ionicPlatform.ready() no longer works in Chrome. https://github.com/driftyco/ionic/issues/494

@adam Here’s an example of Radios not working unless Touch is emulated :