-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfastbutton.js
138 lines (122 loc) · 5.54 KB
/
fastbutton.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
(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; // Is this a touch-based browser?
/* 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, 'touchmove', this, this.useCapture));
this.events.push(addListener(element, 'touchend', this, this.useCapture));
}
else {
this.events.push(addListener(element, 'click', this, this.useCapture)); // Needed for desktop compatibility
}
};
/* 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) {
if (typeof me != 'undefined'){ try { clicks.add(); } catch(err) {} }
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) {
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) > 30 || Math.abs(event.touches[0].clientY - this.startY) > 15) {
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);
// jQuery
(function($) {
$.fn.fastClick = function(handler) {
return $(this).each(function() {
new FastButton($(this)[0], handler);
});
};
}(jQuery));