Ionic-framework: bug: ng-click handlers called twice on IE

Created on 7 Jan 2015  路  23Comments  路  Source: ionic-team/ionic-framework

Type: bug

Platform: windows

To reproduce the duplicate handlers on Internet Explorer, open http://codepen.io/vjrantal/pen/GgNdVj with IE11 and click the show/alert/confirm buttons. The actual (problematic) behavior is that the popups gets triggered twice, while the expected behavior is that they get triggered once.

The same actual behavior can be reproduced on desktop IE11 and the IE mobile on Windows Phone 8.1. Tip: If you don't have Windows and IE installed, you can use https://remote.modern.ie to test on OS X, iOS or Android.

The code in the the codepen logs the events the handlers get an you see something like this in the IE F12 developer tools console:

[object MouseEvent]
[object PointerEvent]

Most helpful comment

@jdnichollsc try to add type="button" to the button element. As @cverb pointed in this SO issue the default behaviour is submit and apparently that messes with the code.
At least my issue was fixed.

All 23 comments

For debugging purposes - the issue seems to go away if you add this piece of JavaScript to the codepen at http://codepen.io/vjrantal/pen/GgNdVj :

window.addEventListener('click', function(event) {
  if (Object.prototype.toString.call(event) == '[object PointerEvent]') {
    event.stopPropagation();
  }
}
, true);

As I think stopping the propagation of the PointerEvent might create unexpected behaviors, one alternative solution is to replace the tag "button" by the tag "a" with the class 'button'.

My solution is to overwrite the ng-click handling and simply drop the second event:

ionic.Platform.isIE = function() {
  return ionic.Platform.ua.toLowerCase().indexOf('trident') > -1;
}

if (ionic.Platform.isIE()) {
  angular.module('ionic')
    .factory('$ionicNgClick', ['$parse', '$timeout', function($parse, $timeout) {
      return function(scope, element, clickExpr) {
        var clickHandler = angular.isFunction(clickExpr) ? clickExpr : $parse(clickExpr);

        element.on('click', function(event) {
          scope.$apply(function() {
            if (scope.clicktimer) return; // Second call
            clickHandler(scope, {$event: (event) });
            scope.clicktimer = $timeout(function() { delete scope.clicktimer; }, 1, false);
          });
        });

        // Hack for iOS Safari's benefit. It goes searching for onclick handlers and is liable to click
        // something else nearby.
        element.onclick = function(event) {};
      };
    }]);
}

The magic is around the clicktimer part.

Tested that this bug is not reproducible on the very latest browser included in the Windows 10 technical preview when experimental features are enabled. More about the release and how to enable those features can be found at http://blogs.msdn.com/b/ie/archive/2015/03/18/rendering-engine-updates-in-march-for-the-windows-10-technical-preview.aspx.

Thanks for the leg work on this @vjrantal. I'd like to fix this immediately after the 1.0, but since tap.js can be such a can of obscure-device-specific-worms, I'd like to wait until then before rocking the boat.

Thanks for the update.

I was actually recently looking into http://patrickhlauke.github.io/touch/tests/results/, which shows that there is some amount of variation in the exact behavior even between different versions of the same browser.

One challenge is that regressions might not be easy to catch with unit tests (because a true finger tap might result into different events than a JavaScript-triggered click). However, Ionic seems to have a fairly comprehensive manual test list at https://github.com/driftyco/ionic/blob/master/test/unit/utils/tap.unit.js which should be helpful when testing.

Using an external library to achieve a reasonable tap behavior might be one option.

It's a tricky area. We started with FastClick and implemented most of Hammer.js which are great, but both had issues we had to fix to the point they don't really resemble their original form. We'll reevaluate for v2, but for v1, there just isn't/wasn't anything as comprehensive. A particular problem is form elements and preventing ghost clicks when an element animates to a new position when clicked. That combined with a myriad of different behavior, between devices, manufacturers, and OSes... it gets ugly. We're not aware of anyone who's solved it better than tap.js, but we'd gladly adopt any open source project that has.

Same problem here, no matter if you're running IE11 on your desktop or on your phone.
An 'a' tag instead of 'button' works just fine.
They have the almost same call tree, so that's very tricky.

@corebonts thanks, that fix works for now--for ng-click. ng-submit is still firing twice--though I'm sure if I understood what your workaround was doing I could write a similar one for ng-submit.

Thanks @corebonts! Hopefully the Ionic Team can learn from your fix in future versions.

Also effects