click/tap refactor

This commit is contained in:
Adam Bradley
2014-02-18 09:03:09 -06:00
parent 7cf4eef961
commit 62fefc3c2d
2 changed files with 51 additions and 68 deletions

View File

@@ -231,9 +231,9 @@
oldDot.parentElement.removeChild(oldDot)
}, 3000);
});
document.addEventListener('mousedown', function(){
console.debug('mousedown');
});
// document.addEventListener('mousedown', function(){
// console.debug('mousedown');
// });
document.addEventListener('mouseup', function(){
console.debug('mouseup');
});

View File

@@ -46,9 +46,6 @@
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();
};
function tapPolyfill(orgEvent) {
@@ -64,13 +61,6 @@
return stopEvent(e);
}
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 stopEvent(e);
}
while(ele) {
// climb up the DOM looking to see if the tapped element is, or has a parent, of one of these
if( ele.tagName === "INPUT" ||
@@ -97,25 +87,11 @@
}
function preventGhostClick(e) {
if(e.target.tagName === "LABEL" && e.target.control) {
if(e.target.control) {
// this is a label that has an associated input
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');
return stopEvent(e);
}
// remember the last time this label was clicked to it can prevent a second label ghostclick
e.target.control.labelLastTap = Date.now();
// 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 native layer still needs to click the input which the label controls
console.debug('preventGhostClick', 'label stopPropagation');
e.stopPropagation();
return;
// the native layer will send the actual event, so stop this one
console.debug('preventGhostClick', 'label');
return stopEvent(e);
}
if( isRecentTap(e) ) {
@@ -124,53 +100,25 @@
return stopEvent(e);
}
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);
return stopEvent(e);
}
// remember the last time this element was clicked
e.target.lastClick = Date.now();
// remember the coordinates of this click so if a tap or click in the
// same area quickly happened again we can ignore it
recordCoordinates(e);
}
function stopEvent(e){
e.stopPropagation();
e.preventDefault();
return false;
}
function blurActive() {
var ele = document.activeElement;
if(ele && (ele.tagName === "INPUT" ||
ele.tagName === "TEXTAREA" ||
ele.tagName === "SELECT")) {
// using a timeout to prevent funky scrolling while a keyboard hides
setTimeout(function(){
ele.blur();
}, 400);
}
}
function isRecentTap(event) {
// loop through the tap coordinates and see if the same area has been tapped recently
var tapId, existingCoordinates, currentCoordinates,
hitRadius = 15;
var tapId, existingCoordinates, currentCoordinates;
for(tapId in tapCoordinates) {
existingCoordinates = tapCoordinates[tapId];
if(!currentCoordinates) currentCoordinates = getCoordinates(event); // lazy load it when needed
if(currentCoordinates.x > existingCoordinates.x - hitRadius &&
currentCoordinates.x < existingCoordinates.x + hitRadius &&
currentCoordinates.y > existingCoordinates.y - hitRadius &&
currentCoordinates.y < existingCoordinates.y + hitRadius) {
if(currentCoordinates.x > existingCoordinates.x - HIT_RADIUS &&
currentCoordinates.x < existingCoordinates.x + HIT_RADIUS &&
currentCoordinates.y > existingCoordinates.y - HIT_RADIUS &&
currentCoordinates.y < existingCoordinates.y + HIT_RADIUS) {
// the current tap coordinates are in the same area as a recent tap
return true;
return existingCoordinates;
}
}
}
@@ -178,10 +126,10 @@
function recordCoordinates(event) {
var c = getCoordinates(event);
if(c.x && c.y) {
var tapId = 't' + Date.now();
var tapId = Date.now();
// only record tap coordinates if we have valid ones
tapCoordinates[tapId] = { x: c.x, y:c.y };
tapCoordinates[tapId] = { x: c.x, y: c.y, id: tapId };
setTimeout(function() {
// delete the tap coordinates after X milliseconds, basically allowing
@@ -208,8 +156,39 @@
return { x:0, y:0 };
}
function removeClickPrevent(e) {
setTimeout(function(){
if(e.target && e.target.control && e.target.control.labelLastTap) {
e.target.control.labelLastTap = null;
}
var c = isRecentTap(e);
if(c) delete tapCoordinates[c.id];
}, REMOVE_PREVENT_DELAY);
return stopEvent(e);
}
function stopEvent(e){
e.stopPropagation();
e.preventDefault();
return false;
}
function blurActive() {
var ele = document.activeElement;
if(ele && (ele.tagName === "INPUT" ||
ele.tagName === "TEXTAREA" ||
ele.tagName === "SELECT")) {
// using a timeout to prevent funky scrolling while a keyboard hides
setTimeout(function(){
ele.blur();
}, 400);
}
}
var tapCoordinates = {}; // used to remember coordinates to ignore if they happen again quickly
var CLICK_PREVENT_DURATION = 450; // amount of milliseconds to check for ghostclicks
var CLICK_PREVENT_DURATION = 1500; // max milliseconds ghostclicks in the same area should be prevented
var REMOVE_PREVENT_DELAY = 325; // delay after a touchend/mouseup before removing the ghostclick prevent
var HIT_RADIUS = 15;
// set global click handler and check if the event should stop or not
document.addEventListener('click', preventGhostClick, true);
@@ -217,4 +196,8 @@
// global tap event listener polyfill for HTML elements that were "tapped" by the user
ionic.on("tap", tapPolyfill, document);
// listeners used to remove ghostclick prevention
document.addEventListener('touchend', removeClickPrevent, false);
document.addEventListener('mouseup', removeClickPrevent, false);
})(this, document, ionic);