I'm working on the plugin flot.touch.js which add touch interactivity (pan and zoom) on the chart for webkit browsers.
I want to make it work on IE10 too but I don't know how to retrieve the space between my touch points (I need this for calculate the scale).
On webkit browsers, we can do this by using these variables :
evt.originalEvent.touches[0].pageX
evt.originalEvent.touches[0].pagey
evt.originalEvent.touches[1].pageX
evt.originalEvent.touches[1].pageY
With IE's pointer events, a separate event is fired for each touch point. Unlike iOS touch events (as implemented by other browsers too), the state of each "pointer" is tracked separately. Consider it a more generic event that groups several pointer-based input devices.
Each event object is given a pointerId property that can be used to track its state. To track multiple touches, you'll need to store that pointerId along with any other variables in an object outside the scope of the event handler's function, along with any other data you might need. For example:
var pointers = {};
function pointerDown(evt) {
if (evt.preventManipulation)
evt.preventManipulation();
pointers[evt.pointerId] = [evt.PageX, evt.PageY];
for (var k in pointers) {
// loop over your other active pointers
}
}
function pointerUp(evt) {
delete pointers[evt.pointerId];
}
Further reading:
IEBlog - Handling Multi-touch and Mouse Input in All Browsers
Like Andy E said. Track how many pointers you have on your screen and store properties from each of them (in your case x and y coordinates, acquired form event)
A nice example of multitouch can be found here: http://ie.microsoft.com/testdrive/Graphics/TouchEffects/ and you can go into the code with F12 tools and get all the code under the Script tag: http://ie.microsoft.com/testdrive/Graphics/TouchEffects/Demo.js
There you can find this part of code:
function addTouchPoint(e) {
if(touchCount == 0) {
document.addEventListener(moveevent, moveTouchPoint, false);
}
var pID = e.pointerId || 0;
if(!touchPoints[pID]) {
touchCount++;
touchPoints[pID] = {x : e.clientX, y : e.clientY};
}
}
Hope it helps
If you're on a Windows 8 platform, IE10 supports the MSGesture object. This object can be used like the iOS version of gesture events. To initialize the object, we have to set the target object as well as add the MSPointer on MSPointerDown. For example:
var myGesture = new MSGesture();
var myElement = document.getElementById("MyCanvas");
myGesture.target = myElement; //sets target
myElement.addEventListener("MSPointerDown", function (event){
redGesture.addPointer(evt.pointerId); // adds pointer to the MSGesture object
}, false);
From here, you can add an event listener for the MSGestureChange function to process the event.scale property. Note that if the target is not set, an InvalidStateError exception will occur.
Related
I'm trying to write a browser add-on that can create artificial TouchEvents. So far I have it working in Chrome but Firefox does not seem to support TouchEvents, as trying to create one with new TouchEvent(...) produces:
ReferenceError: TouchEvent is not defined
I'm wondering if there's some way I can either implement TouchEvent myself in the add-on or otherwise dispatch an event that has all the necessary properties to be usable like a TouchEvent.
However if I create a CustomEvent and assign it the extra properties necessary for it to effectively be a TouchEvent (i.e. touches, targetTouches, and changedTouches) those properties don't persist by the time the webpage receives the event.
Is there any way to accomplish what I'm after, or am I out of luck trying to get this to work in Firefox?
In case anyone else has this question, here is the solution I found. You can create your own class that extends UIEvent like so:
class TouchEvent extends UIEvent {
constructor(name, initDict) {
super(name, initDict);
this.touches = initDict.touches;
this.targetTouches = initDict.targetTouches;
this.changedTouches = initDict.changedTouches;
}
}
The one caveat is that these events have to be constructed by a script running in the same domain as the handler that will receive them, otherwise you'll get a Permission denied to access property error. My solution for that was to use my extension's content script to add a script element in the current document's body and set its textContent property to my javascript code that creates and dispatches the touch events.
I couldn't get the accepted answer to work because I couldn't figure out how to construct the touches that need to be placed inside initDict. Here is what worked for me:
function dispatchTouchEvent(eventName, details) {
const uiEvent = document.createEvent('UIEvent');
uiEvent.initUIEvent(eventName, true, true, window, 1);
const event = Object.assign(uiEvent, {touches: [details]});
details.target.dispatchEvent(event);
}
And here's how to invoke it with a touchstart event on the body element at coordinates (50, 100):
dispatchTouchEvent('touchstart', {identifier: 0, target: document.body, pageX: 50, pageY: 100});
For my purposes I only needed to set the pageX and pageY properties, but depending on what you're doing it may be necessary to set clientX, clientY, screenX, or screenY.
Apple has announced that with its new iPhone 6S and 6S+, users can now use pressure to interact with their apps. Is there a JavaScript API for web developers to take advantage of this new functionality on standard websites as well? If so, how can it be implemented?
Yes, according to the W3C, a new force property has been recently added to the spec of the Touch interface, which ranges from 0 (min force) to 1 (max force). This is already available for Safari on iOS9 (not sure if Chrome, or other browsers have implemented it yet).
Quick answer:
To access this property, simply invoke
touchForce = evt.originalEvent.touches[0].force;
within a touchstart or touchmove event listener.
There are a few problems with this implementation:
On touchstart, the force will be very close to 0, as it's called as soon as the first sign of pressure is detected.
The touchstart event won't fire again if pressure is increased or decreased.
touchmove isn't reliable because you have to wait for position to change before the new force is read, which won't always be the case.
Solution:
To remedy this, you can set a timeout that evaluates this force every few milliseconds after touchstart and then clear the timeout on touchend.
var tO = 0;
$("#div1").on("touchstart", startTouch);
$("#div1").on("touchend", endTouch);
function startTouch(evt){
evt.preventDefault();
touchForce = evt.originalEvent.touches[0].force;
tO = window.setTimeout(function(){startTouch(evt)}, 100);
// Do something with touchForce
}
function endTouch(){
touchForce = 0;
window.clearTimeout(tO);
// Do something else
}
Try it out in this CodePen I created
It's a bit hacky, but it works. Maybe in the future they'll create a new forcechange event, but this is all we have for now.
Excuse the JQuery, I used it to illustrate the point more quickly.
You can use the the JS library Pressure.js. It makes it really easy to get Force and 3D Touch values on iOS and OSX if you have an iPhone 6s or new Macbook with the new Force Touch trackpads.
The syntax is really simple too, here is an example:
Pressure.set('#element', {
start: function(){
// this is called on force start
},
end: function(){
// this is called on force end
},
startDeepPress: function(){
// this is called on "force click" / "deep press", aka once the force is greater than 0.5
},
endDeepPress: function(){
// this is called when the "force click" / "deep press" end
},
change: function(force, event){
// this is called every time there is a change in pressure
// here the 'force' variable passed in container the current pressure force
},
unsupported: function(){
// this is called once there is a touch on the element and the device or browser does not support Force or 3D touch
}
});
Famo.us' GestureHandler doesn't seem to be catching on mobile devices. Even with the very simple test:
var FamousEngine = require('famous/core/FamousEngine');
var DOMElement = require('famous/dom-renderables/DOMElement');
var GestureHandler = require('famous/components/GestureHandler');
FamousEngine.init();
var scene = FamousEngine.createScene();
var rootNode = scene.addChild();
var backgroundNode = rootNode.addChild();
var backgroundElement = new DOMElement(rootNode, {
classes: ['background'],
});
var gestures = new GestureHandler(rootNode);
gestures.on('drag', function(e) {
console.log(e);
.
.
.
});
the drag gesture callback is firing on desktop when you drag with the mouse, but in a mobile browser dragging just scrolls the document around.
The event passed to the callback, e in my example, is a custom famous thing and doesn't have the usual .stopPropagation method.
What gives?
Turns out the answer had nothing to do with the event handling. Inside my event handler I was creating a few variables using ES6's new destructuring syntax:
var [x, y] = node.getPosition();
and while my code is being "babelified" and this works perfectly on desktop browsers, it was failing on iOS.
The issue also described here:
https://github.com/babel/babelify/issues/22
The solution is just to do it the old fashioned way...
:(
I'm trying to get around the way iOS handles javascript and scrolling with a js function I'm calling through window.onscroll. The problem is that in iOS, the javascript is not called until after scrolling has stopped. To work around this, I was hoping to add an event listener that prevents the default behavior, calls my function, then dispatches the original touch event. Something like this:
// Helper function to clone event
function clone_obj(obj) {
if(obj == null || typeof(obj) !== 'object') {
return obj;
}
var temp = document.createEvent("TouchEvent");
for (var key in obj) {
temp[key] = obj[key];
}
return temp;
}
document.body.addEventListener("touchstart", function(event) {
event.preventDefault();
// clone the event using helper function
var clonedEvent = clone_obj(event);
// run my javascript function
animateNav();
// initialize and dispatch clonedEvent
clonedEvent.initTouchEvent();
event.target.dispatchEvent(clonedEvent);
});
The clone_obj helper function is derived from an SO post, but it doesn't seem to provide a deep clone. If I try to clone using $.extend or event.constructor, I get TypeError: Illegal constructor.
Anyone know of another workaround, or if it's possible to clone a touch event?
As a side note, I tried the HammerJS library to call my function on swipe and drag events, but it still wasn't calling my function until after scrolling stopped.
This is intended behavior by iOS and other mobile operating systems. Since this is not a bug, it is not wise to circumvent this limitation; hacking around such limitations can cause inconsistent and buggy results across devices.
If you would like scroll events to occur on mobile, the best approach is to use a custom scollbar system. From the browser perspective, the webpage is never scrolling, rather the contents of the webpage are scrolling within the body. iScroll (https://github.com/cubiq/iscroll) is a popular library to create custom scrolling. It depends on the circumstance, but custom scrolling should be used sparingly and carefully. Remember, you are completely circumventing the native scrolling; native scrollers know more about the user, their preferences, and the device than you could know.
I am trying to figure out how to programmatically add an event handler to a JavaScript object. More specifically, I am trying to add an event handler to the onunload event of a window object. I am trying it like so with out any luck:
var url = "www.somelocation.com";
var specs = "location=0,menubar=0,status=0,titlebar=0,toolbar=0";
var dialogWindow = window.open(url, "dialog", specs, true);
dialogWindow.onunload += myEventHandler;
function myEventHandler(e)
{
// Do stuff
}
I'm guessing my syntax is incorrect. However, I cannot seem to find any documentation regarding this. Can someone please help me?
dialogWindow.onunload += myEventHandler is incorrect. This should really be:
dialogWindow.unload = myEventHandler;
Or to preserve the existing handlers:
var oldHandler = dialogWindow.unload;
dialogWindow.unload = function (e)
{
if (oldHandler) { oldHandler(e); }
myEventHandler(e);
}
And, of course, use JQuery.
Javascript only manages objects. The HTML DOM elements including HTML5 are objects, we can categorize them as follows:
The Window object that has all the events
The IFRAME object which is as complete as Window (this is why it is used by Youtube)
DOM objects that only have management events, click, mouseup, mouseDown ... as well as their own events (audio, video, DIV BLOCK), etc.
Building JAVASCRIPT objects is a bit like Visual Basic or C ++.
Events are easy to manage, we can mix the events of smartphones and computers. To ensure support for old browsers SAFARI, IE, just avoid using certain keywords like (LET, =>, or values in the parameters of functions like X = 1).
Event Management:
var mouseup = (!('ontouchstart' in document.documentElement))? 'mouseup':'touchend';
var winresize = (!('ontouchstart' in document.documentElement))? 'resize':'orientationchange';
var mouseover = (!('ontouchstart' in document.documentElement))? 'mouseover':'touchstart';
var mouseout = (!('ontouchstart' in document.documentElement))? 'mouseout':'touchend';
To ensure compatibility:
var Event_mouseup = function (e)(
Code mouseup.......
};
if(object.addEventListener){
object.addEventListener(mouseup,Event_mouseup,{passive:true}); //passive true not return event
}else if(object.attachEvent){
object.attachEvent(mouseup,Event_mouseup);
}else{
object['on'+mouseup]=Event_mouseup;
};
The Window object works exactly the same.
The event load is really special in an HTML page.
The objects that handle this event are objects that load data like IMG, VIDEO, AUDIO, etc.
In general, when an object has a load event it also has an error loading event.
To understand the DOM and the browser, you have to compare it to a C ++ window for example.
Window
----------------------- event Load
-------- Object 1 HTML DOM
--------- event1 Object
--------- event2 Object
-------- Object 2 HTML DOM
--------- event1 Object
--------- event2 Object
---------------------- event unload
Window