Instantly share code, notes, and snippets.
Created
September 20, 2012 15:35
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save iaincarsberg/3756647 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
/** | |
* From: http://code.this.com/mobile/articles/fast_buttons.html | |
* Also see: http://stackoverflow.com/questions/6300136/trying-to-implement-googles-fast-button | |
*/ | |
/** For IE8 and earlier compatibility: https://developer.mozilla.org/en/DOM/element.addEventListener */ | |
function addListener(el, type, listener, useCapture) { | |
if (el.addEventListener) { | |
el.addEventListener(type, listener, useCapture); | |
return { | |
destroy: function() { el.removeEventListener(type, listener, useCapture); } | |
}; | |
} else { | |
// see: http://stackoverflow.com/questions/5198845/javascript-this-losing-context-in-ie | |
var handler = function(e) { listener.handleEvent(window.event, listener); } | |
el.attachEvent('on' + type, handler); | |
return { | |
destroy: function() { el.detachEvent('on' + type, handler); } | |
}; | |
} | |
} | |
var isTouch = "ontouchstart" in window; | |
/* Construct the FastButton with a reference to the element and click handler. */ | |
this.FastButton = function(element, handler, useCapture) { | |
// collect functions to call to cleanup events | |
this.events = []; | |
this.touchEvents = []; | |
this.element = element; | |
this.handler = handler; | |
this.useCapture = useCapture; | |
if (isTouch) | |
this.events.push(addListener(element, 'touchstart', this, this.useCapture)); | |
this.events.push(addListener(element, 'click', this, this.useCapture)); | |
}; | |
/* Remove event handling when no longer needed for this button */ | |
this.FastButton.prototype.destroy = function() { | |
for (i = this.events.length - 1; i >= 0; i -= 1) | |
this.events[i].destroy(); | |
this.events = this.touchEvents = this.element = this.handler = this.fastButton = null; | |
}; | |
/* acts as an event dispatcher */ | |
this.FastButton.prototype.handleEvent = function(event) { | |
switch (event.type) { | |
case 'touchstart': this.onTouchStart(event); break; | |
case 'touchmove': this.onTouchMove(event); break; | |
case 'touchend': this.onClick(event); break; | |
case 'click': this.onClick(event); break; | |
} | |
}; | |
/* Save a reference to the touchstart coordinate and start listening to touchmove and | |
touchend events. Calling stopPropagation guarantees that other behaviors don’t get a | |
chance to handle the same click event. This is executed at the beginning of touch. */ | |
this.FastButton.prototype.onTouchStart = function(event) { | |
event.stopPropagation ? event.stopPropagation() : (event.cancelBubble=true); | |
this.touchEvents.push(addListener(this.element, 'touchend', this, this.useCapture)); | |
this.touchEvents.push(addListener(document.body, 'touchmove', this, this.useCapture)); | |
this.startX = event.touches[0].clientX; | |
this.startY = event.touches[0].clientY; | |
}; | |
/* When /if touchmove event is invoked, check if the user has dragged past the threshold of 10px. */ | |
this.FastButton.prototype.onTouchMove = function(event) { | |
if (Math.abs(event.touches[0].clientX - this.startX) > 10 || Math.abs(event.touches[0].clientY - this.startY) > 10) { | |
this.reset(); //if he did, then cancel the touch event | |
} | |
}; | |
/* Invoke the actual click handler and prevent ghost clicks if this was a touchend event. */ | |
this.FastButton.prototype.onClick = function(event) { | |
event.stopPropagation ? event.stopPropagation() : (event.cancelBubble=true); | |
this.reset(); | |
// Use .call to call the method so that we have the correct "this": https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call | |
var result = this.handler.call(this.element, event); | |
if (event.type == 'touchend') | |
clickbuster.preventGhostClick(this.startX, this.startY); | |
return result; | |
}; | |
this.FastButton.prototype.reset = function() { | |
for (i = this.touchEvents.length - 1; i >= 0; i -= 1) | |
this.touchEvents[i].destroy(); | |
this.touchEvents = []; | |
}; | |
this.clickbuster = function() {} | |
/* Call preventGhostClick to bust all click events that happen within 25px of | |
the provided x, y coordinates in the next 2.5s. */ | |
this.clickbuster.preventGhostClick = function(x, y) { | |
clickbuster.coordinates.push(x, y); | |
window.setTimeout(clickbuster.pop, 2500); | |
}; | |
this.clickbuster.pop = function() { | |
clickbuster.coordinates.splice(0, 2); | |
}; | |
/* If we catch a click event inside the given radius and time threshold then we call | |
stopPropagation and preventDefault. Calling preventDefault will stop links | |
from being activated. */ | |
this.clickbuster.onClick = function(event) { | |
for (var i = 0; i < clickbuster.coordinates.length; i += 2) { | |
var x = clickbuster.coordinates[i]; | |
var y = clickbuster.coordinates[i + 1]; | |
if (Math.abs(event.clientX - x) < 25 && Math.abs(event.clientY - y) < 25) { | |
event.stopPropagation ? event.stopPropagation() : (event.cancelBubble=true); | |
event.preventDefault ? event.preventDefault() : (event.returnValue=false); | |
} | |
} | |
}; | |
if (isTouch) { | |
// Don't need to use our custom addListener function since we only bust clicks on touch devices | |
document.addEventListener('click', clickbuster.onClick, true); | |
clickbuster.coordinates = []; | |
} | |
})(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment