Skip to content
This repository
Browse code

Fixes for issue 1464 - No way to stop a link from being followed with…

… some custom event (tap, taphold)

jquery.mobile.vmouse.js:

	- Modified triggerVirtualEvent() so that it returns the virtual event instead of the isDefaultPrevented() result of the virtual event.

		- Updated all references to triggerVirtualEvent() that relied on the boolean return value to instead check the isDefaultPrevented() call on the event now returned.

			- Updated mouseEventCallback() to propagate the iDefaultPrevented(), isPropagationStopped(), and stopImmediatePropagation() values from the virtual event on to the  original mouse event.

			jquery.mobile.event.js

				- Modified the "taphold" trigger code to create a new $.Event() instead of passing the stale vmousedown event.

					- Added clearTapTimer() which is called from a new vmouseup binding, to prevent the timer from firing between the tie the finger/mouse goes up and the click event is dispatched.

					- Added some propagation tests for the "tap" event. Tests for "taphold" will have to wait until we fix the problem where multiple taphold timers are fired off when an element and one of its ancestors is bound to taphold.
  • Loading branch information...
commit 7cce0c5573ad593b17302082e936b10f568551a4 1 parent 0b41fe8
Kin Blas authored September 02, 2011
16  js/jquery.mobile.event.js
@@ -78,16 +78,19 @@ $.event.special.tap = {
78 78
 				return false;
79 79
 			}
80 80
 
81  
-			var touching = true,
82  
-				origTarget = event.target,
  81
+			var origTarget = event.target,
83 82
 				origEvent = event.originalEvent,
84 83
 				timer;
85 84
 
  85
+			function clearTapTimer() {
  86
+				clearTimeout( timer );
  87
+			}
  88
+
86 89
 			function clearTapHandlers() {
87  
-				touching = false;
88  
-				clearTimeout(timer);
  90
+				clearTapTimer();
89 91
 
90 92
 				$this.unbind( "vclick", clickHandler )
  93
+					.unbind( "vmouseup", clearTapTimer )
91 94
 					.unbind( "vmousecancel", clearTapHandlers );
92 95
 			}
93 96
 
@@ -102,12 +105,11 @@ $.event.special.tap = {
102 105
 			}
103 106
 
104 107
 			$this.bind( "vmousecancel", clearTapHandlers )
  108
+				.bind( "vmouseup", clearTapTimer )
105 109
 				.bind( "vclick", clickHandler );
106 110
 
107 111
 			timer = setTimeout(function() {
108  
-				if ( touching ) {
109  
-					triggerCustomEvent( thisObject, "taphold", event );
110  
-				}
  112
+					triggerCustomEvent( thisObject, "taphold", $.Event( "taphold" ) );
111 113
 			}, 750 );
112 114
 		});
113 115
 	}
23  js/jquery.mobile.vmouse.js
@@ -164,8 +164,7 @@ function clearResetTimer() {
164 164
 }
165 165
 
166 166
 function triggerVirtualEvent( eventType, event, flags ) {
167  
-	var defaultPrevented = false,
168  
-		ve;
  167
+	var ve;
169 168
 
170 169
 	if ( ( flags && flags[ eventType ] ) ||
171 170
 				( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
@@ -173,18 +172,27 @@ function triggerVirtualEvent( eventType, event, flags ) {
173 172
 		ve = createVirtualEvent( event, eventType );
174 173
 
175 174
 		$( event.target).trigger( ve );
176  
-
177  
-		defaultPrevented = ve.isDefaultPrevented();
178 175
 	}
179 176
 
180  
-	return defaultPrevented;
  177
+	return ve;
181 178
 }
182 179
 
183 180
 function mouseEventCallback( event ) {
184 181
 	var touchID = $.data(event.target, touchTargetPropertyName);
185 182
 
186 183
 	if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ){
187  
-		triggerVirtualEvent( "v" + event.type, event );
  184
+		var ve = triggerVirtualEvent( "v" + event.type, event );
  185
+		if ( ve ) {
  186
+			if ( ve.isDefaultPrevented() ) {
  187
+				event.preventDefault();
  188
+			}
  189
+			if ( ve.isPropagationStopped() ) {
  190
+				event.stopPropagation();
  191
+			}
  192
+			if ( ve.isImmediatePropagationStopped() ) {
  193
+				event.stopImmediatePropagation();
  194
+			}
  195
+		}
188 196
 	}
189 197
 }
190 198
 
@@ -264,7 +272,8 @@ function handleTouchEnd( event ) {
264 272
 	triggerVirtualEvent( "vmouseup", event, flags );
265 273
 
266 274
 	if ( !didScroll ) {
267  
-		if ( triggerVirtualEvent( "vclick", event, flags ) ) {
  275
+		var ve = triggerVirtualEvent( "vclick", event, flags );
  276
+		if ( ve && ve.isDefaultPrevented() ) {
268 277
 			// The target of the mouse events that follow the touchend
269 278
 			// event don't necessarily match the target used during the
270 279
 			// touch. This means we need to rely on coordinates for blocking
89  tests/unit/event/event_core.js
@@ -241,6 +241,95 @@
241 241
 		}, 40);
242 242
 	});
243 243
 
  244
+	asyncTest( "tap event propagates up DOM tree", function(){
  245
+		var tap = 0,
  246
+			$qf = $( "#qunit-fixture" ),
  247
+			$doc = $( document ),
  248
+			docTapCB = function(){
  249
+				same(++tap, 2, "document tap callback called once after #qunit-fixture callback");
  250
+			};
  251
+
  252
+		$qf.bind( "tap", function() {
  253
+			same(++tap, 1, "#qunit-fixture tap callback called once");
  254
+		});
  255
+
  256
+		$doc.bind( "tap", docTapCB );
  257
+
  258
+		$qf.trigger( "vmousedown" )
  259
+			.trigger( "vmouseup" )
  260
+			.trigger( "vclick" );
  261
+
  262
+		// tap binding should be triggered twice, once for
  263
+		// #qunit-fixture, and a second time for document.
  264
+		same( tap, 2, "final tap callback count is 2" );
  265
+
  266
+		$doc.unbind( "tap", docTapCB );
  267
+
  268
+		start();
  269
+	});
  270
+
  271
+	asyncTest( "stopPropagation() prevents tap from propagating up DOM tree", function(){
  272
+		var tap = 0,
  273
+			$qf = $( "#qunit-fixture" ),
  274
+			$doc = $( document ),
  275
+			docTapCB = function(){
  276
+				ok(false, "tap should NOT be triggered on document");
  277
+			};
  278
+
  279
+		$qf.bind( "tap", function(e) {
  280
+			same(++tap, 1, "tap callback 1 triggered once on #qunit-fixture");
  281
+			e.stopPropagation();
  282
+		})
  283
+		.bind( "tap", function(e) {
  284
+			same(++tap, 2, "tap callback 2 triggered once on #qunit-fixture");
  285
+		});
  286
+
  287
+		$doc.bind( "tap", docTapCB);
  288
+
  289
+		$qf.trigger( "vmousedown" )
  290
+			.trigger( "vmouseup" )
  291
+			.trigger( "vclick" );
  292
+
  293
+		// tap binding should be triggered twice.
  294
+		same( tap, 2, "final tap count is 2" );
  295
+
  296
+		$doc.unbind( "tap", docTapCB );
  297
+
  298
+		start();
  299
+	});
  300
+
  301
+	asyncTest( "stopImmediatePropagation() prevents tap propagation and execution of 2nd handler", function(){
  302
+		var tap = 0,
  303
+			$cf = $( "#qunit-fixture" );
  304
+			$doc = $( document ),
  305
+			docTapCB = function(){
  306
+				ok(false, "tap should NOT be triggered on document");
  307
+			};
  308
+
  309
+		// Bind 2 tap callbacks on qunit-fixture. Only the first
  310
+		// one should ever be called.
  311
+		$cf.bind( "tap", function(e) {
  312
+			same(++tap, 1, "tap callback 1 triggered once on #qunit-fixture");
  313
+			e.stopImmediatePropagation();
  314
+		})
  315
+		.bind( "tap", function(e) {
  316
+			ok(false, "tap callback 2 should NOT be triggered on #qunit-fixture");
  317
+		});
  318
+
  319
+		$doc.bind( "tap", docTapCB);
  320
+
  321
+		$cf.trigger( "vmousedown" )
  322
+			.trigger( "vmouseup" )
  323
+			.trigger( "vclick" );
  324
+
  325
+		// tap binding should be triggered once.
  326
+		same( tap, 1, "final tap count is 1" );
  327
+
  328
+		$doc.unbind( "tap", docTapCB );
  329
+
  330
+		start();
  331
+	});
  332
+
244 333
 	var swipeTimedTest = function(opts){
245 334
 		var swipe = false;
246 335
 

0 notes on commit 7cce0c5

Please sign in to comment.
Something went wrong with that request. Please try again.