How to tell if only one finger touching in Microsoft IE10 - javascript

We are emulating scrolling of an infinite list and we wish to detect the difference between a single finger scrolling, or the user starting a gesture.
In theory one can keep a count of fingers down in IE10 by +1 for every MSPointerDown and -1 for MSPointerUp events (and/or matching fingers using the event.msPointerId).
In practice, there is at least one bug where IE10 will generate an MSPointerDown event but never ever send the matching MSPointerUp event. (Sorry, I haven't been able to create a simple test case to show this, but I did spend a lot of time checking that the MSPointerUp event is definitely missing. Maybe due to removal of child elements during the touch).
Perhaps use the MSGesture events to see if multiple fingers are down? (I tried this with little success, but maybe someone else has solved it).
Any ideas?
PS: In webkit the equivalent is to check event.touches.length === 1 in the touchstart event (beware that you need an unobvious trick to get this working: document.ontouchstart must have an event registered, and then event.touches.length will be correct for touchstart events registered on other elements).

Make sure you are also keeping track of MSPointerOut. I've found that MSPointerUp will not be called if you let go of the screen whilst outside of the trackable area.
If it helps, I've got a WinJS class I've been using to track multitouch state.
var TouchState = WinJS.Class.define(
function () {
this.pointers = [];
this.primaryPointerId = 0;
this.touchzones = [];
}, {
touchHandler: function (eventType, e) {
if (eventType == "MSPointerDown") {
if (!this.pointers[this.primaryPointerId] || !this.pointers[this.primaryPointerId].touching) {
this.primaryPointerId = e.pointerId;
}
e.target.msSetPointerCapture(e.pointerId);
this.pointers[e.pointerId] = {
touching: true,
coords: {
x: e.currentPoint.rawPosition.x,
y: e.currentPoint.rawPosition.y
}
};
this.checkTouchZones(this.pointers[e.pointerId].coords.x, this.pointers[e.pointerId].coords.y, e);
}
else if (eventType == "MSPointerMove") {
if (this.pointers[e.pointerId]) {
this.pointers[e.pointerId].coords.x = e.currentPoint.rawPosition.x;
this.pointers[e.pointerId].coords.y = e.currentPoint.rawPosition.y;
}
}
else if (eventType == "MSPointerUp") {
if (this.pointers[e.pointerId]) {
this.pointers[e.pointerId].touching = false;
this.pointers[e.pointerId].coords.x = e.currentPoint.rawPosition.x;
this.pointers[e.pointerId].coords.y = e.currentPoint.rawPosition.y;
}
}
else if (eventType == "MSPointerOut") {
if (this.pointers[e.pointerId]) {
this.pointers[e.pointerId].touching = false;
this.pointers[e.pointerId].coords.x = e.currentPoint.rawPosition.x;
this.pointers[e.pointerId].coords.y = e.currentPoint.rawPosition.y;
}
}
},
checkTouchZones: function (x, y, e) {
for (var zoneIndex in this.touchzones) {
var zone = this.touchzones[zoneIndex];
if (x >= zone.hitzone.x1 && x < zone.hitzone.x2 && y > zone.hitzone.y1 && y < zone.hitzone.y2) {
zone.callback(e);
}
}
},
addTouchZone: function (id, hitzone, callback) {
this.touchzones[id] = {
hitzone: hitzone,
callback: callback
};
},
removeTouchZone: function (id) {
this.touchzones[id] = null;
}
});

Related

Prevent default single click event on double click on a link in HTML

My code is pretty simple:
var clickCount = 0, clickEl = [];
var manualClick = false;
$(document).on('click', 'a', function (e) {
if (e.altKey || e.ctrlKey || e.shiftKey) {
return;
}
clickCount = clickCount + 1;
clickEl[clickCount] = this;
var that = this;
if (1 === clickCount) {
setTimeout(function () {
if (2 === clickCount && clickEl[1] === clickEl[2]) {
window.stop();
embed_anchor(that);
}
clickCount = 0;
}, 250);
}
});
It basically checks if there is double click. If yes, it cancel the single click redirect using window.stop(). It used to work great, but I don't know if it's Chrome or my new PC, window.stop() failing 9.5/10 times.
Even a simple code like:
setInterval(function () {
window.stop();
}, 1);
is not able to prevent redirect these days. Is there any alternative solution for me. I ideally don't want to use e.preventDefault() because this script is part of TamperMonkey and I feel e.preventDefault() will break single click on ton of sites.
Is there any way to hold the event for 250 ms, or cancel and raise the same event (It must behave like last one so no issues with any site). I am open too pretty much everything. I would prefer if my script don't work on some sites rather than breaking any site.
I think you're looking for the dblclick javascript event. It's usable on all updated browsers currently.
There's already a post here: Detect if browser/device supports double click events to detect if it's supported by using a timeout to check if there is an another click after the first click.
Here is the piece of code I wrote to solve my problem:
$.fn.on2 = function(type, sel, handler) {
this[0].addEventListener(type, function(event) {
var t = event.target;
while (t && t !== this) {
if (t.matches(sel)) {
handler.call(t, $.event.fix(event));
}
t = t.parentNode;
}
}, true);
}
var clickEvents = [];
$(document).on2('click', 'a', function (event) {
if (event.altKey || event.ctrlKey || event.shiftKey || this.text.length == 0) {
return;
}
clickEvents.push(event);
if (event.originalEvent.isTrusted) {
event.preventDefault();
event.stopImmediatePropagation();
}
var target = this;
if (1 === clickEvents.length) {
setTimeout(function () {
if (2 === clickEvents.length && clickEvents[0].target == clickEvents[1].target) {
doWhatever(clickEvents[0].target);
} else {
clickEvents[clickEvents.length-1].target.dispatchEvent(new MouseEvent("click", clickEvents[clickEvents.length-1].originalEvent));
}
clickEvents = [];
}, 250);
}
});

Keypress Gestures using Tampermonkey for Chrome

I'm working on a tampermonkey userscript to replace a feature that existed with FireGestures back in Pre-Quantum Firefox. The ability to open all hovered links into new background tabs. So having a combination keypress, which in FG was Ctrl + Right Click and drawing a gesture trail though every link you wanted open. Everything I have so far has been written for me by somebody else so I'm not trying to take credit and I in am way over my head. I don't have the know-how to edit and fix what is needed. This is what I have so far.
(function(delay, t, lnk, clicked) {
//config: delay before click. mouse movement will reset the delay timer.
delay = 1000; //in milliseconds. 1sec = 1000ms
t = 0;
function mousemove() {
clearTimeout(t);
if (lnk) t = setTimeout(clickLink, delay);
}
function clickLink() {
removeEventListener("mousemove", mousemove);
clearTimeout(t);
if (lnk) {
lnk.target = "_blank";
lnk.click();
lnk.target = "";
clicked = true;
}
}
addEventListener("mouseover", function(ev, el, el2) {
el = ev.target;
removeEventListener("mousemove", mousemove);
clearTimeout(t);
while (el) {
if (el.tagName === "A") {
el2 = el;
if (el !== lnk) {
lnk = el;
clicked = false;
addEventListener("mousemove", mousemove);
clearTimeout(t);
t = setTimeout(clickLink, delay);
}
return;
}
el = el.parentNode;
}
if (!el2) {
lnk = null;
clicked = false;
removeEventListener("mousemove", mousemove);
clearTimeout(t);
}
});
})();
There is a couple issues I face.
1. This doesn't require any sort of button combination. It is continually active and will click any link that is hovered over for the specified length of time. I would prefer it to only function when a button combination is pressed, ideally Ctrl + Rightclick. I found a thread dealing with combination keypresses but wouldn't know how to edit it and insert it into the existing script to fit my needs.
document.addEventListener ("keydown", function (zEvent) {
if (zEvent.ctrlKey && zEvent.altKey && zEvent.code === "KeyE") {
// DO YOUR STUFF HERE
}
} );
2. The pop-up blocker in chrome actually prevents these tabs from opening. I don't know if there is any way of remedying this other than turning off the pop-up blocker, but if there was I'd appreciate the help
3. This script opens up tabs in the foreground rather than the background. So opening up a bunch of links on a page wouldn't be possible because it would navigate to the new tab as soon as the first link is clicked. My original idea for fixing this was to just have the script just do a middle-click mouse event over every link it passed over, but I don't even know if that is something that is possible or practical.
I know I am asking a lot but I was just hoping that someone out there that knows what they are doing could help me out by either editing what I already have or writing something out themselves. I appreciate any help provided.
here's my spin on it. This is toggled rather than going on while you are holding onto the keys.
you could had the following to your TamperMonkey script, and when you press "Ctrl + Alt + S", the links on the page are modified and appended a onmouseover event. when you hit the key combination again, the event gets removed from the link. Short and simple.
document.addEventListener('keydown', function (zEvent) {
if (zEvent.ctrlKey && zEvent.altKey && zEvent.code === 'KeyS') {
var links = document.getElementsByTagName('a');
console.log(links.length);//how many links have been grabbed
for (var i = 0; i < links.length; i++) {
if (links[i].onmouseover !== null && links[i].onmouseover.toString().indexOf('function openit') > -1)
{
//toggling the funcitonality off
//remove it
links[i].setAttribute('target', '');
links[i].setAttribute('onmouseover', '');
}
else
{
//toggling the funcitonality on
//add it
links[i].setAttribute('target', 'blank');
links[i].setAttribute('onmouseover', 'function openit(elem){console.log(\'userScript will click on the link\');elem.click();};openit(this);');
}
}
}
}
);
As for popup blocking... I don't know.

js mousedown with touch

is there a way to detect whether a 'mousedown' is a touch right click (hold the finger about 1 sec in place) or just a normal right click?
I think chrome can do this with "ev.originalEvent.sourceCapabilities.firesTouchEvents". But only chrome.
$('#container').mousedown(function(ev) {
if (ev.button === 2 && ev.comesFromTouch) return false
//...
}
edit:
current situation: after about one second after I pressed down my left mouse button, the browser automaticly triggers a 'mousedown' event with button = 2 (tested in the 'device toolbar' mode in chrome). I want to cancel this.
SOLUTION
If a right mousedown appears between a touchstart and touchend it is a right click on a touch screen.
it works something like this.
function onPcRight() { console.log(1);}
function onTouchRight() { console.log(2);}
$('#container').mousedown(function(ev) {
if (ev.button === BUTTON_RIGHT) {
if ($(this).prop('touchdown')) onTouchRight();
else onPcRight();
}
})
.on('touchstart', function() {
$(this).prop('touchdown', true);
})
.on('touchend', function() {
$(this).prop('touchdown', false);
});
I think you can use setTimeout/clearTimeout to count 1s. The pseudo code:
var global_timer = null;
$('#container').mousedown(function(ev) {
if (ev.button === 2) {
global_timer = setTimeout(fireTouchRightClick, 1000);
}
});
$('#container').mousemove(function(ev) {
cancelTouchRightClick();
});
$('#container').mouseleave(function(ev) {
cancelTouchRightClick();
});
$('#container').mouseup(function(ev) {
if (cancelTouchRightClick() && ev.button === 2) {
fireNormalRightClick();
}
});
function cancelTouchRightClick () {
if (global_timer) {
clearTimeout(global_timer);
global_timer = null;
return true;
}
return false;
}
function fireTouchRightClick () {
global_timer = null;
// TODO touch right click
}
I also found a repo to do mouse holding on Github: https://github.com/dna2github/dna2petal/tree/master/visualization
https://github.com/dna2github/dna2petal/blob/master/samples/visualization.html
Maybe you need to pass button type to the mousehold event callback

Detect single tap in UIWebView, but still support text selection and links

I'm using JavaScript to detect taps in a page I'm showing in a UIWebView, like so:
<div id="wrapper">
Apple
</div>
<script>
document.getElementById("wrapper").addEventListener('click', function() {
document.location = 'internal://tap';
}, false);
</script>
I'm intercepting links with my web view delegate, and look for "internal://tap". When I get that, I prevent the web view from navigating, and respond to the tap. However doing this I lose the ability to select text. Tapping the link does still work correctly.
In fact, just adding an event listener for 'click' removes the ability to select text, even if the handler doesn't attempt to change the document location.
Any idea what I'm doing wrong?
Apparently if you put a click listener on an element, you can no longer select text within that element on iOS. My solution was to detect taps using a combination of touchstart, touchmove, and touchend events, along with a timer to ignore multi-taps, and checking the current document selection to make sure a selection event is not going on.
Here's the JS code I used:
SingleTapDetector = function(element, handler) {
this.element = element;
this.handler = handler;
element.addEventListener('touchstart', this, false);
};
SingleTapDetector.prototype.handleEvent = function(event) {
switch (event.type) {
case 'touchstart': this.onTouchStart(event); break;
case 'touchmove': this.onTouchMove(event); break;
case 'touchend': this.onTouchEnd(event); break;
}
};
SingleTapDetector.prototype.onTouchStart = function(event) {
this.element.addEventListener('touchend', this, false);
document.body.addEventListener('touchmove', this, false);
this.startX = this.currentX = event.touches[0].clientX;
this.startY = this.currentY = event.touches[0].clientY;
this.startTime = new Date().getTime();
};
SingleTapDetector.prototype.onTouchMove = function(event) {
this.currentX = event.touches[0].clientX;
this.currentY = event.touches[0].clientY;
};
SingleTapDetector.prototype.onTouchEnd = function(event) {
var that = this;
// Has there been one or more taps in this sequence already?
if (this.tapTimer) {
// Reset the timer to catch any additional taps in this sequence
clearTimeout(this.tapTimer);
this.tapTimer = setTimeout(function() {
that.tapTimer = null;
}, 300);
} else {
// Make sure the user didn't move too much
if (Math.abs(this.currentX - this.startX) < 4 &&
Math.abs(this.currentY - this.startY) < 4) {
// Make sure this isn't a long press
if (new Date().getTime() - this.startTime <= 300) {
// Make sure this tap wasn't part of a selection event
if (window.getSelection() + '' == '') {
// Make sure this tap is in fact a single tap
this.tapTimer = setTimeout(function() {
that.tapTimer = null;
// This is a single tap
that.handler(event);
}, 300);
}
}
}
}
};
new SingleTapDetector(document.body, function(event) {
document.location = "internal://tap";
});
There is no need to use Javascript for this, it's overkill when the UIGestureRecognizerDelegate has adequate methods. All you need to do is make sure that when text selection is taking place, the tap recogniser isn't triggered.
- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
BOOL hasTap = ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] ||
[otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]);
BOOL hasLongTouch = ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] ||
[otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]);
if (hasTap && hasLongTouch) {
// user is selecting text
return NO;
}
return YES;
}
That takes care of text selection, and links should work fine anyway (at least they do for me).

Javascript function works not as intended when I move mouse too quickly. Ideas on how to improve?

I have a drag-and-droppable control on my page. It has a client-side event 'OnClientDragging' which I am using to provide highlighting to my page. The idea is that there are various zones on the page and, as the user drags the control over parts of the page, the page gets highlighted accordingly.
The problem is that I see the highlighting flicker slightly when moving fast. I do not see any exceptions being thrown, but if I move the mouse rapidly while staying inside of one 'zone' I still see the highlighting being removed and re-added. I can only assume this is because the method is not completing in time -- and fires again due to being dragged more.. which causes unintended issues?
function RemoveHighlighting(dockZone) {
if (dockZone.get_docks().length == 0) {
dockZone.removeCssClass("zoneDropOk");
}
}
function AddHighlighting(dockZone) {
if (dockZone.get_docks().length == 0) {
dockZone.addCssClass("zoneDropOk");
}
}
var previousZone = null;
//Evaluates whether the currently moused-over item is a RadDockZone.
function TryGetZoneFromTarget(target) {
while (target != null && target.id) {
if (target.id.indexOf("RadDockZone") != -1) {
return $find(target.id);
}
target = target.parentNode;
}
return null;
}
//Adds highlighting to the dockZones when the user is dragging objects to the screen.
//Clear the old dockZone as the user moves out of it, and color new ones as they move into it.
function OnClientDragging(sender, eventArgs) {
var target = eventArgs.get_htmlElement();
var zone = TryGetZoneFromTarget(target);
if (zone) {
var currentZone = zone;
dockZoneDroppedOnID = zone.get_id();
if (previousZone == null) {
previousZone = currentZone;
$.queue(AddHighlighting(currentZone));
}
else if (previousZone != currentZone) {
$.queue(RemoveHighlighting(previousZone));
previousZone = currentZone;
$.queue(AddHighlighting(currentZone));
}
}
else {
dockZoneDroppedOnID = "";
if (previousZone != null && $.queue.length == 0) {
RemoveHighlighting(previousZone);
previousZone = null;
}
}
}
Should I be trying to make this script more efficient, or maybe setting a variable outside the local scope to indicate that the previous event is still running and cancel the current event?
EDIT: I've edited in a queue solution which worked well! I'm not sure if it's a perfect solution, but it solved my issue.
I think setting variables outside this scope will be a more effective approach, in particular, you can either make use of or emulate the way jQuery's fx queue works.

Categories