From 2132d292e7bbf368e1c21be10b3ddf67a1b4e496 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Mon, 10 Feb 2014 13:36:37 -0600 Subject: [PATCH] fix(click): Clicks firing twice, closes #573 --- js/ext/angular/test/clickTests.html | 49 +++++++++++++++++------------ js/utils/poly.js | 17 ++++++++-- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/js/ext/angular/test/clickTests.html b/js/ext/angular/test/clickTests.html index 574f46cc66..5a10274f2e 100644 --- a/js/ext/angular/test/clickTests.html +++ b/js/ext/angular/test/clickTests.html @@ -112,6 +112,13 @@ +
+ + Checkbox {{ htmlCheckboxData.checkedValue }} +
+ @@ -130,6 +137,8 @@ .controller('MyCtrl', function($scope) { + $scope.htmlCheckboxData = {}; + $scope.otherTests = function() { if($scope.testBtnText == 'HTML') { $scope.testBtnText = 'AngJS'; @@ -140,18 +149,18 @@ $scope.otherTests(); $scope.htmlToggleClick = function() { - console.log('htmlToggleClick', 'click'); + console.debug('htmlToggleClick', 'click'); $scope.buttonValue = 'html' + Math.floor(Math.random() * 999); }; $scope.stringClick = 'stringMethod()'; $scope.stringMethod = function() { - console.log('toggle {{ stringClick }]', 'click'); + console.debug('toggle {{ stringClick }]', 'click'); $scope.buttonValue = 'tg' + Math.floor(Math.random() * 999); }; $scope.itemClick = function() { - console.log('itemClick()', 'click'); + console.debug('itemClick()', 'click'); $scope.buttonValue = 'itm' + Math.floor(Math.random() * 999); }; @@ -159,28 +168,28 @@ { label: 'Item 1', actionItem: function() { - console.log('item.actionItem()', 'click') + console.debug('item.actionItem()', 'click') $scope.buttonValue = 'itm' + Math.floor(Math.random() * 999); } } ]; $scope.buttonClick = function() { - console.log('button ng-click', 'click') + console.debug('button ng-click', 'click') $scope.buttonValue = 'btn' + Math.floor(Math.random() * 999); }; $scope.divClick = function() { - console.log('div ng-click', 'click') + console.debug('div ng-click', 'click') $scope.buttonValue = 'div' + Math.floor(Math.random() * 999); }; $scope.radioModel = {} $scope.radioChange = function() { - console.log('radio ng-change', 'change', $scope.radioModel.data); + console.debug('radio ng-change', 'change', $scope.radioModel.data); }; $scope.radioClick = function(val) { - console.log('radio ng-click', 'click', val); + console.debug('radio ng-click', 'click', val); $scope.buttonValue = 'rd' + Math.floor(Math.random() * 999); }; @@ -192,23 +201,23 @@ } document.getElementById('radio1').addEventListener('change', function(){ - console.log('radio 1', 'change'); + console.debug('radio 1', 'change'); }); document.getElementById('radio1').addEventListener('click', function(){ - console.log('radio 1', 'click'); + console.debug('radio 1', 'click'); }); document.getElementById('radio2').addEventListener('change', function(){ - console.log('radio 2', 'change'); + console.debug('radio 2', 'change'); }); document.getElementById('radio2').addEventListener('click', function(){ - console.log('radio 2', 'click'); + console.debug('radio 2', 'click'); }); document.addEventListener('touchstart', function(e){ - console.log('touchstart'); + console.debug('touchstart'); }); document.addEventListener('touchend', function(e){ - console.log('touchend'); + console.debug('touchend'); if(!e.changedTouches || !e.changedTouches.length) return; var dot = document.createElement('div'); @@ -223,16 +232,16 @@ }, 3000); }); document.addEventListener('mousedown', function(){ - console.log('mousedown'); + console.debug('mousedown'); }); document.addEventListener('mouseup', function(){ - console.log('mouseup'); + console.debug('mouseup'); }); // document.addEventListener('mousemove', function(event){ - // console.log('mousemove', 'clientX: ' + event.clientX, 'clientY: ' + event.clientY); + // console.debug('mousemove', 'clientX: ' + event.clientX, 'clientY: ' + event.clientY); // }); document.addEventListener('click', function(event){ - console.log('click', 'clientX: ' + event.clientX, 'clientY: ' + event.clientY); + console.debug('click', 'clientX: ' + event.clientX, 'clientY: ' + event.clientY); }); document.getElementById('clear').addEventListener('click', function(){ setTimeout(clearMsgs, 200); @@ -258,10 +267,10 @@ for (var i = 0, j = arguments.length; i < j; i++){ args.push(arguments[i]); } - console.log.apply(this, args); + console.debug.apply(this, args); }; - console.log = function() { + console.debug = function() { //return; if(stopped) return; diff --git a/js/utils/poly.js b/js/utils/poly.js index fbe74f7640..8d0b094afd 100644 --- a/js/utils/poly.js +++ b/js/utils/poly.js @@ -33,6 +33,8 @@ // simulate a normal click by running the element's click method then focus on it if(ele.disabled) return; + console.debug('tapElement', ele.tagName, ele.className); + var c = getCoordinates(e); // using initMouseEvent instead of MouseEvent for our Android friends @@ -50,7 +52,10 @@ } // remember the coordinates of this tap so if it happens again we can ignore it - recordCoordinates(e); + // but only if the coordinates are not already being actively disabled + if( !isRecentTap(e) ) { + recordCoordinates(e); + } // set the last tap time so if a click event quickly happens it knows to ignore it ele.lastTap = Date.now(); @@ -65,12 +70,14 @@ if( isRecentTap(e) ) { // if a tap in the same area just happened, don't continue + console.debug('tapPolyfill', 'isRecentTap', ele.tagName); return; } - if(e.target.lastClick && e.target.lastClick + CLICK_PREVENT_DURATION > Date.now()) { + if(ele.lastClick && ele.lastClick + CLICK_PREVENT_DURATION > Date.now()) { // if a click recently happend on this element, don't continue // (yes on some devices it's possible for a click to happen before a touchend) + console.debug('tapPolyfill', 'recent lastClick', ele.tagName); return; } @@ -111,6 +118,7 @@ if(e.target.control.labelLastTap && e.target.control.labelLastTap + CLICK_PREVENT_DURATION > Date.now()) { // Android will fire a click for the label, and a click for the input which it is associated to // this stops the second ghost click from the label from continuing + console.debug('preventGhostClick', 'labelLastTap'); e.stopPropagation(); e.preventDefault(); return false; @@ -122,12 +130,14 @@ // The input's click event will propagate so don't bother letting this label's click // propagate cuz it causes double clicks. However, do NOT e.preventDefault(), because // the label still needs to click the input + console.debug('preventGhostClick', 'label stopPropagation'); e.stopPropagation(); return; } if( isRecentTap(e) ) { // a tap has already happened at these coordinates recently, ignore this event + console.debug('preventGhostClick', 'isRecentTap', e.target.tagName); e.stopPropagation(); e.preventDefault(); return false; @@ -135,6 +145,7 @@ if(e.target.lastTap && e.target.lastTap + CLICK_PREVENT_DURATION > Date.now()) { // this element has already had the tap poly fill run on it recently, ignore this event + console.debug('preventGhostClick', 'e.target.lastTap', e.target.tagName); e.stopPropagation(); e.preventDefault(); return false; @@ -201,7 +212,7 @@ } var tapCoordinates = {}; // used to remember coordinates to ignore if they happen again quickly - var CLICK_PREVENT_DURATION = 350; // amount of milliseconds to check for ghostclicks + var CLICK_PREVENT_DURATION = 450; // amount of milliseconds to check for ghostclicks // set global click handler and check if the event should stop or not document.addEventListener('click', preventGhostClick, true);