[483] | 1 | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> |
---|
| 2 | <html> |
---|
| 3 | <head> |
---|
| 4 | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
---|
| 5 | <meta name="viewport" content="width=device-width,maximum-scale=1,minimum-scale=1,user-scalable=no"/> |
---|
| 6 | <title>Dojo Touch Testing</title> |
---|
| 7 | <style type="text/css"> |
---|
| 8 | #test { |
---|
| 9 | width: 300px; |
---|
| 10 | height: 150px; |
---|
| 11 | border: 1px solid #7FB0DB; |
---|
| 12 | background-color: #7FB0DB; |
---|
| 13 | } |
---|
| 14 | #innertest { |
---|
| 15 | border: 1px solid white; |
---|
| 16 | width: 100px; |
---|
| 17 | height: 75px; |
---|
| 18 | background-color: white; |
---|
| 19 | } |
---|
| 20 | #test2 { |
---|
| 21 | /* for testing touch.move */ |
---|
| 22 | width: 200px; |
---|
| 23 | height: 50px; |
---|
| 24 | border: 1px solid yellow; |
---|
| 25 | background-color: yellow; |
---|
| 26 | } |
---|
| 27 | #test3 { |
---|
| 28 | width: 300px; |
---|
| 29 | height: 150px; |
---|
| 30 | border: 1px solid red; |
---|
| 31 | background-color: red; |
---|
| 32 | } |
---|
| 33 | #innertest3 { |
---|
| 34 | border: 1px solid white; |
---|
| 35 | width: 250px; |
---|
| 36 | height: 75px; |
---|
| 37 | background-color: white; |
---|
| 38 | } |
---|
| 39 | #current, #log { |
---|
| 40 | width: 300px; |
---|
| 41 | height: 200px; |
---|
| 42 | float: left; |
---|
| 43 | } |
---|
| 44 | #dohDiv { |
---|
| 45 | display: none; |
---|
| 46 | background-color:#800300; |
---|
| 47 | color: wheat; |
---|
| 48 | } |
---|
| 49 | </style> |
---|
| 50 | <script type="text/javascript" src="../dojo.js" data-dojo-config="async: true"></script> |
---|
| 51 | <script> |
---|
| 52 | require([ |
---|
| 53 | "dojo/_base/array", |
---|
| 54 | "dojo/dom", |
---|
| 55 | "dojo/_base/lang", |
---|
| 56 | "dojo/on", |
---|
| 57 | "dojo/has", |
---|
| 58 | "doh/runner", |
---|
| 59 | "dojo/dom-geometry", |
---|
| 60 | "dojo/dom-style", |
---|
| 61 | "dojo/domReady", |
---|
| 62 | "dojo/touch" |
---|
| 63 | ], function(array, dom, lang, on, has, doh, domGeom, domStyle, domReady, touch){ |
---|
| 64 | |
---|
| 65 | var action = function(comment, e){ |
---|
| 66 | // summary: |
---|
| 67 | // Callback to display into when events fire |
---|
| 68 | // Detailed log of the most recent event: |
---|
| 69 | |
---|
| 70 | dom.byId("current").innerHTML = "Most recent non touch.move event:"; |
---|
| 71 | var info = "[Touch Event]: " + e.type + " on " + comment + |
---|
| 72 | "<br/> ------ Event Properties: ------<br/>"; |
---|
| 73 | for(var i in e){ |
---|
| 74 | if(i == "touches" || i == "targetTouches" || i == "changedTouches"){ |
---|
| 75 | info += i + ": " + e[i].length + "<br/>"; |
---|
| 76 | }else{ |
---|
| 77 | if(typeof e[i] != "function"){ |
---|
| 78 | info += " " + i + ": " + e[i] + "<br/>"; |
---|
| 79 | } |
---|
| 80 | } |
---|
| 81 | } |
---|
| 82 | dom.byId("current").innerHTML += info + "<br/>"; |
---|
| 83 | |
---|
| 84 | // This is a log of all events, most recent first: |
---|
| 85 | dom.byId("log").innerHTML = comment + "{type:" + |
---|
| 86 | e.type + ", target:" + (e.target.id||e.target.tagName) + |
---|
| 87 | "}<br/>" + dom.byId("log").innerHTML; |
---|
| 88 | |
---|
| 89 | e.preventDefault(); |
---|
| 90 | }; |
---|
| 91 | |
---|
| 92 | var node = dom.byId("test"), |
---|
| 93 | innerNode = dom.byId("innertest"); |
---|
| 94 | |
---|
| 95 | //1. should work well on PC and touch devices |
---|
| 96 | array.forEach(["test", "innertest"], function(name){ |
---|
| 97 | for(var event in touch){ |
---|
| 98 | if(event != "move"){ |
---|
| 99 | on(dom.byId(name), touch[event], lang.hitch(null, action, "on("+name+", touch."+event+")-->")); |
---|
| 100 | } |
---|
| 101 | } |
---|
| 102 | }); |
---|
| 103 | |
---|
| 104 | on(dom.byId("test2"), touch.move, function(e){ |
---|
| 105 | dom.byId("log").innerHTML = "on(touch2, touch.move)--> {type:" + |
---|
| 106 | e.type + ", target:" + (e.target.id||e.target.tagName) + |
---|
| 107 | ", pageX = " + e.pageX + ", pageY = " + e.pageY + |
---|
| 108 | "}<br/>" + dom.byId("log").innerHTML; |
---|
| 109 | |
---|
| 110 | |
---|
| 111 | // this should stop scrolling |
---|
| 112 | e.preventDefault(); |
---|
| 113 | |
---|
| 114 | // stopPropagation() should work too |
---|
| 115 | e.stopPropagation(); |
---|
| 116 | }); |
---|
| 117 | |
---|
| 118 | // //2. should work well across touch devices |
---|
| 119 | // on(node, "touchstart", action); |
---|
| 120 | // on(node, "touchmove", action); |
---|
| 121 | // on(node, "touchend", action); |
---|
| 122 | // on(node, "touchcancel", action); |
---|
| 123 | // on(node, "orientationchange", action); |
---|
| 124 | |
---|
| 125 | dom.byId("test3").dojoClick = true; |
---|
| 126 | on(dom.byId("test3"), "click", function(e){ |
---|
| 127 | if(has("touch")){ |
---|
| 128 | // click should be a synthetic click |
---|
| 129 | dom.byId("log").innerHTML = (e._dojo_click ? "Synthetic click received in test3" : "ERROR: native click received in test3") + "<br/>" + dom.byId("log").innerHTML; |
---|
| 130 | }else{ |
---|
| 131 | // click should be a native click |
---|
| 132 | dom.byId("log").innerHTML = (e._dojo_click ? "ERROR: Synthetic click received in test3 (non touch device)" : "Native click received in test3 (non touch device)") + "<br/>" + dom.byId("log").innerHTML; |
---|
| 133 | } |
---|
| 134 | }); |
---|
| 135 | dom.byId("innertest3").dojoClick = false; |
---|
| 136 | on(dom.byId("innertest3"), "click", function(e){ |
---|
| 137 | // click should be a native click |
---|
| 138 | dom.byId("log").innerHTML = (e._dojo_click ? "ERROR: Synthetic click received in innertest3" : "Native click received in innertest3") + "<br/>" + dom.byId("log").innerHTML;; |
---|
| 139 | e.stopPropagation(); |
---|
| 140 | }); |
---|
| 141 | |
---|
| 142 | /** |
---|
| 143 | * Fires an event. All coordinates are defaulted to zero. |
---|
| 144 | * @param target the target element. |
---|
| 145 | * @param eventType touchstart, touchmove or touchend, or mouse or MSPointer equivalents. |
---|
| 146 | */ |
---|
| 147 | function emit(target, eventType, options){ |
---|
| 148 | var targetPos = domGeom.position(target); |
---|
| 149 | |
---|
| 150 | options = lang.mixin({ |
---|
| 151 | pageX: targetPos.x + targetPos.w / 2, |
---|
| 152 | pageY: targetPos.y + targetPos.h / 2 |
---|
| 153 | }, options || {}); |
---|
| 154 | |
---|
| 155 | if(document.createEvent && target.dispatchEvent){ |
---|
| 156 | var touch = { |
---|
| 157 | identifier: (new Date()).getTime(), |
---|
| 158 | target: target, |
---|
| 159 | screenX: 0, |
---|
| 160 | screenY: 0, |
---|
| 161 | clientX: 0, |
---|
| 162 | clientY: 0, |
---|
| 163 | pageX: 0, |
---|
| 164 | pageY: 0 |
---|
| 165 | }; |
---|
| 166 | lang.mixin(touch, options || {}); |
---|
| 167 | |
---|
| 168 | var mouseEvent = document.createEvent("MouseEvent"); |
---|
| 169 | mouseEvent.initMouseEvent( |
---|
| 170 | eventType, |
---|
| 171 | true, |
---|
| 172 | true, |
---|
| 173 | target.ownerDocument.defaultView, |
---|
| 174 | 0, |
---|
| 175 | 0, |
---|
| 176 | 0, |
---|
| 177 | 0, |
---|
| 178 | 0, |
---|
| 179 | false, |
---|
| 180 | false, |
---|
| 181 | false, |
---|
| 182 | false, |
---|
| 183 | 0, |
---|
| 184 | document.body // relatedTarget, for mouseout events |
---|
| 185 | ); |
---|
| 186 | |
---|
| 187 | if(/touch/.test(eventType)){ |
---|
| 188 | mouseEvent['touches'] = [touch]; |
---|
| 189 | mouseEvent['targetTouches'] = [touch]; |
---|
| 190 | mouseEvent['changedTouches'] = [touch]; |
---|
| 191 | } |
---|
| 192 | |
---|
| 193 | target.dispatchEvent(mouseEvent); |
---|
| 194 | }else{ |
---|
| 195 | options.bubbles = true; |
---|
| 196 | options.cancelable = true; |
---|
| 197 | on.emit(target, eventType, options); |
---|
| 198 | } |
---|
| 199 | } |
---|
| 200 | |
---|
| 201 | // if we declare "dojo/domReady!" touch.js will register its handler *after* the test execution |
---|
| 202 | // so here we explicitly call domReady. |
---|
| 203 | domReady(function(){ |
---|
| 204 | var dohDiv = dom.byId('dohDiv'); |
---|
| 205 | domStyle.set(dohDiv, {display: 'block'}); |
---|
| 206 | |
---|
| 207 | // Function to register tests on mouse events, touch events, or Microsoft's pointer-ish events |
---|
| 208 | function register(testName, downEvent, moveEvent, upEvent, cancelEvent){ |
---|
| 209 | doh.register(testName, [ |
---|
| 210 | function press(){ |
---|
| 211 | var executed, target; |
---|
| 212 | on.once(dohDiv, touch.press, function(e){ |
---|
| 213 | //console.log(e.type); |
---|
| 214 | executed = true; |
---|
| 215 | target = e.target; |
---|
| 216 | }); |
---|
| 217 | emit(dohDiv, downEvent); |
---|
| 218 | doh.t(executed, 'dojo.touch.press fired'); |
---|
| 219 | doh.is(dohDiv, target, "target"); |
---|
| 220 | }, |
---|
| 221 | function move(){ |
---|
| 222 | var executed, target; |
---|
| 223 | on.once(dohDiv, touch.move, function(e){ |
---|
| 224 | //console.log(e.type); |
---|
| 225 | executed = true; |
---|
| 226 | target = e.target; |
---|
| 227 | }); |
---|
| 228 | emit(dohDiv, moveEvent); |
---|
| 229 | doh.t(executed, 'dojo.touch.move fired'); |
---|
| 230 | doh.is(dohDiv, target, "target"); |
---|
| 231 | }, |
---|
| 232 | function release(){ |
---|
| 233 | var executed, target; |
---|
| 234 | on.once(dohDiv, touch.release, function(e){ |
---|
| 235 | //console.log(e.type); |
---|
| 236 | executed = true; |
---|
| 237 | target = e.target; |
---|
| 238 | }); |
---|
| 239 | emit(dohDiv, upEvent, {screenX: 0, screenY: 50}); |
---|
| 240 | doh.t(executed, 'dojo.touch.release fired'); |
---|
| 241 | doh.is(dohDiv, target, "target"); |
---|
| 242 | }, |
---|
| 243 | function cancel(){ |
---|
| 244 | var executed, target; |
---|
| 245 | on.once(dohDiv, touch.cancel, function(e){ |
---|
| 246 | executed = true; |
---|
| 247 | target = e.target; |
---|
| 248 | }); |
---|
| 249 | emit(dohDiv, cancelEvent, { |
---|
| 250 | relatedTarget: document.body, // needed for mouseout event |
---|
| 251 | screenX: 0, |
---|
| 252 | screenY: 50, |
---|
| 253 | pageX: 0, |
---|
| 254 | pageY: 0 |
---|
| 255 | }); |
---|
| 256 | doh.t(executed, 'dojo.touch.cancel fired'); |
---|
| 257 | doh.is(dohDiv, target, "target"); |
---|
| 258 | }, |
---|
| 259 | function movePreventDefault(){ |
---|
| 260 | /** |
---|
| 261 | * checks that calling preventDefault() on dojo "touch.move" event |
---|
| 262 | * prevents default behavior on the underlying native event |
---|
| 263 | * Ref. #17220 |
---|
| 264 | * window |
---|
| 265 | * | => (1) listen and prevent default on dojo "touch.move" event |
---|
| 266 | * | => (2) listen and check default behavior status on native "touchmove" event |
---|
| 267 | * | |
---|
| 268 | * +--dohDiv => (3) dispatch native "touchmove" event |
---|
| 269 | **/ |
---|
| 270 | var prevented = false; |
---|
| 271 | |
---|
| 272 | // (1) register dojo "touch.move" listener |
---|
| 273 | on.once(dohDiv, touch.move, function(e){ |
---|
| 274 | e.preventDefault(); |
---|
| 275 | //console.log("prevent default on event " + e.type); |
---|
| 276 | }); |
---|
| 277 | |
---|
| 278 | // (2) register listener for native event (touchmove, mousemove, or MSPointerMove) |
---|
| 279 | // Call addEventListener() except on IE6-8 where that's not supported |
---|
| 280 | function listener(e){ |
---|
| 281 | prevented = e.defaultPrevented; |
---|
| 282 | //console.log("native listener, prevented = " + prevented); |
---|
| 283 | } |
---|
| 284 | if(window.addEventListener){ |
---|
| 285 | window.addEventListener(moveEvent, listener); |
---|
| 286 | }else{ |
---|
| 287 | // IE6-8 code path |
---|
| 288 | on.once(document.body, moveEvent, listener); |
---|
| 289 | } |
---|
| 290 | |
---|
| 291 | // (3) dispatch native touchmove/mousemove/MSPointerMove event |
---|
| 292 | emit(dohDiv, moveEvent); |
---|
| 293 | doh.t(prevented, 'prevented'); |
---|
| 294 | |
---|
| 295 | if(window.addEventListener){ |
---|
| 296 | window.removeEventListener(moveEvent, listener); |
---|
| 297 | } |
---|
| 298 | } |
---|
| 299 | ]); |
---|
| 300 | } |
---|
| 301 | |
---|
| 302 | // Setup tests relevant for this platform |
---|
| 303 | if(navigator.pointerEnabled){ |
---|
| 304 | register('Pointer events', 'pointerdown', 'pointermove', 'pointerup', |
---|
| 305 | has('touch') ? 'pointercancel' : 'mouseout'); |
---|
| 306 | }else if(navigator.msPointerEnabled){ |
---|
| 307 | register('MSPointer events', 'MSPointerDown', 'MSPointerMove', 'MSPointerUp', |
---|
| 308 | has('touch') ? 'MSPointerCancel' : 'mouseout'); |
---|
| 309 | }else{ |
---|
| 310 | // Mouse is always supported, even on devices with touch capability. |
---|
| 311 | // Test mouse first because once we start emitting touch events, dojo/touch ignores mouse events |
---|
| 312 | // for 1000ms. |
---|
| 313 | register('mouse events', 'mousedown', 'mousemove', 'mouseup', 'mouseout'); |
---|
| 314 | |
---|
| 315 | if(has("touch")){ |
---|
| 316 | register('touch events', 'touchstart', 'touchmove', 'touchend', 'touchcancel'); |
---|
| 317 | } |
---|
| 318 | } |
---|
| 319 | |
---|
| 320 | if(has("touch")){ |
---|
| 321 | doh.registerTest("synthetic clicks", { |
---|
| 322 | name: "Synthetic click disabled", |
---|
| 323 | timeout: 1000, |
---|
| 324 | runTest: function(){ |
---|
| 325 | /** |
---|
| 326 | * check that synthetic click is NOT fired when dojoClick value is set |
---|
| 327 | * to falsy on a dom element and touch is released inside the element. |
---|
| 328 | * Note that synthetic click is fired ONLY on touch enabled devices (touch or MSPointer). |
---|
| 329 | */ |
---|
| 330 | var domEltWithDisabledSyntheticClick = dom.byId("innertest3"); |
---|
| 331 | var box = domEltWithDisabledSyntheticClick.getBoundingClientRect(); // position in viewport coordinates |
---|
| 332 | var coordInsideEltWithDisabledSyntheticClick = {x: box.right - box.left, y:box.bottom - box.top}; |
---|
| 333 | var clicked = false; |
---|
| 334 | // register the click handler |
---|
| 335 | function listener(e){ |
---|
| 336 | alert("click fired ??"); |
---|
| 337 | clicked = true; |
---|
| 338 | } |
---|
| 339 | domEltWithDisabledSyntheticClick.addEventListener("click", listener); |
---|
| 340 | // emit "touchstart" and "touchend" events inside the element |
---|
| 341 | emit(domEltWithDisabledSyntheticClick, "touchstart", {screenX: coordInsideEltWithDisabledSyntheticClick.x, screenY: coordInsideEltWithDisabledSyntheticClick.y}); |
---|
| 342 | emit(domEltWithDisabledSyntheticClick, "touchend", {screenX: coordInsideEltWithDisabledSyntheticClick.x, screenY: coordInsideEltWithDisabledSyntheticClick.y}); |
---|
| 343 | var deferred = new doh.Deferred(); |
---|
| 344 | // without deferred the click handler is called after the doh method ends. |
---|
| 345 | setTimeout(deferred.getTestCallback(function(){ |
---|
| 346 | doh.t(!clicked, 'synthetic click fired'); |
---|
| 347 | }), 0); |
---|
| 348 | return deferred; |
---|
| 349 | } |
---|
| 350 | }); |
---|
| 351 | } |
---|
| 352 | |
---|
| 353 | doh.run(); |
---|
| 354 | }); |
---|
| 355 | }); |
---|
| 356 | </script> |
---|
| 357 | </head> |
---|
| 358 | <body> |
---|
| 359 | <div id="test"> |
---|
| 360 | test |
---|
| 361 | <div id="innertest"> |
---|
| 362 | inner test |
---|
| 363 | </div> |
---|
| 364 | </div> |
---|
| 365 | <div id="test2"> |
---|
| 366 | touch.move |
---|
| 367 | </div> |
---|
| 368 | <div id="test3"> |
---|
| 369 | On touch devices, synthetic clicks are enabled in this area. |
---|
| 370 | <div id="innertest3"> |
---|
| 371 | On touch devices, synthetic clicks disabled in this area. |
---|
| 372 | </div> |
---|
| 373 | </div> |
---|
| 374 | <div id="dohDiv">doh</div> |
---|
| 375 | <div id="current"></div> |
---|
| 376 | <div id="log"></div> |
---|
| 377 | <br style="clear:both"/> |
---|
| 378 | </body> |
---|
| 379 | </html> |
---|