How to change threshold of "touchmove" in js - javascript

I just want to ask if there is a way to change the threshold of the event touchmove? In my PhoneGap App, an image will appear. If touchstart is triggered, another image will appear. If touchend or touchmove is triggered, all images must disappear. Here is my code:
$('.greenApple').on('touchend', function (e){
$('body').find('.active').removeClass('active');
$('body').find('.greenApple').addClass('hidden');
flag = true;
return;
});
$('.greenApple').on('touchmove', function (e){
$('body').find('.active').removeClass('active');
$('body').find('.greenApple').addClass('hidden');
flag = true;
return;
However, the threshold of the number of pixels that is considered a touchmove is too little. Frequently, as soon as I press the image (not releasing it, touchend is not being triggered), the image disappears because the touchmove event is triggered. Is there a way to change the number of pixels of moved that is considered as touchmove? Or there are other workarounds?

You need to modify this property
$.vmouse.moveDistanceThreshold (default: 10px) – More than this, then it is a scroll event. The vmousecancel event is called and the TouchMove event is cancelled.
Try the code below:
<script src="jquery.js"></script>
<script>
$(document).bind("mobileinit", function(){
$.vmouse.moveDistanceThreshold (default: 20px)
});
</script>
<script src="jquery-mobile.js"></script>
Take a look at this Official Documentation

You can't change the default behavior of the browser, but you can use the event data to filter out small movements that you want to suppress. The touch attribute of the event parameter gives position information. See the docs for full details.
Save the position on start and compare the position on touchmove. Remove the items from the page only if greater than the threshold that you set.
var flag, x,y, distance = 25;
$('.greenApple').on('touchstart', function (e){
x = e.originalEvent.changedTouches[0].pageX
y = e.originalEvent.changedTouches[0].pageY
});
$('.greenApple').on('touchmove', function (e){
var deltaX = e.originalEvent.changedTouches[0].pageX - x;
var deltaY = e.originalEvent.changedTouches[0].pageY - y;
var difference = (deltaX * deltaX) + (deltaY * deltaY)
if(Math.sqrt(difference) > distance) {
$('body').find('.active').removeClass('active');
$('body').find('.greenApple').addClass('hidden');
flag = true;
});
Here's a Working fiddle

need not find the class, if there is not and will not do anything wrong
$('button').on('touchend', function (e){
/*no function whatsoever*/
console.log($("body").find('.active').first().html());
console.log($("body").find('.active').html());
/*only here*/
console.log($("body").html());
/*just do it this way*/
$('body').removeClass('active');
$('body').addClass('hidden');
flag = true;
return;
});

Store last touch coordinates in element data and trigger touchmove handler only when their change is significant.
var treshold = 12345; // set whatever treshold you like
function storeLastTouch (element, event) {
element.data('lastTouch', event.originalEvent.changedTouches);
}
$('.greenApple').on('touchstart', function (event) {
storeLastTouch($(this), event);
});
$('.greenApple').on('touchmove', function (event) {
var lastTouch = $(this).data('lastTouch'),
thisTouch = event.originalEvent.changedTouches,
delta = ...; // calculate difference in any metric you like
if (delta > treshold) {
storeLastTouch($(this), event);
// ... (perform your handler logic)
}
});

Related

Have one button with two different behaviors for click and mouse down

I wish to have the same button behave different for left click and left mouse down.
On click it should add 15 to the current number, on mouse down it adds 1 for as long as the mouse is down
var value = 0;
document.getElementById("Addbtn").addEventListener("mousedown", mouseDown);
document.getElementById("Addbtn").addEventListener("click", mouseClick);
function mouseDown() {
this.value++;
document.getElementById("demo").innerHTML = this.value.tosSring();
}
function mouseClick() {
this.value += 15;
document.getElementById("demo").innerHTML = this.value.tosSring();
}
The problem both events gets fired and I wish to distinguish them so when mouse is hold down click won't fire and vise versa.
Is there a way to achieve this?
Cheers
As I said in the comment, since click and mousedown are related and there is no way to disconnect them from each other, the solution can come with a bit of a trick, adding a timeout to determine if it was a "long hold" of the mouse down.
Added a little bonus to disable the click if mouse down has been triggered, you can remove it if not needed.
Also notice, its on a 1 second waiting (1000 ms), change it according to how much you can wait before saying its a mousedown event. 1 second seems long to a user sometime ( about 500 ms is a sweet spot for me)
var value = 0;
var mouseHoldTimeout;
var mouseDownDone = false;
document.getElementById("Addbtn").addEventListener("mousedown", mouseDown);
document.getElementById("Addbtn").addEventListener("click", mouseClick);
function mouseDown() {
mouseHoldTimeout = setTimeout(() => {
value++;
mouseDownDone = true;
document.getElementById("demo").innerHTML = value.toString();
}, 1000);
}
function mouseClick() {
if (mouseHoldTimeout) {
clearTimeout(mouseHoldTimeout);
mouseHoldTimeout = null;
}
if (mouseDownDone) {
mouseDownDone = false;
return;
}
value += 15;
document.getElementById("demo").innerHTML = value.toString();
}
mousedown event will trigger when you click either the left or right or even the middle.It does not matter which one do you click it runs the function. In contrast click event will trigger when you click the left.So it seems when you click the left you trigger mousedown(for left) and click event.
As the document provides the click event triggers after mousedown and mouseup.
js mouse basics

Define moving direction javascript (Android device)

I am writing PhoneGap application, and use swipe and scroll on one view. For scroll I use native scroll (I have tried to use iScroll, but there are many inputs in my view, so the better solution that I found it is a native scroll). For defining direction of moving I am gathering first 10 touchmove events in array and check clientX and clienY values and than define average value. And all should be good, but in Android touchmove event does not work is scroll view without preventDefault(). So, according this issue, I use preventDefault() for first ten touchmove events and than depending on direction continue use preventDefault() and make swipe, or don't use preventDefault() and make scroll. But when I make scroll nothing happens(. There is an example:
var list = document.querySelector('ul');
var arr = [];
var ev = document.createEvent('UIEvent');
var evEnd = document.createEvent('UIEvent');
var evCancel = document.createEvent('UIEvent');
var flag = false;
ev.initUIEvent("touchstart", true, true);
evEnd.initUIEvent("touchend", true, true);
evCancel.initUIEvent("touchcancel", true, true);
list.addEventListener('touchstart', startMove, false)
list.addEventListener('touchend', endMove, false)
function startMove(e){
list.addEventListener('touchmove', move, false)
}
function move(e){
if(flag){
return;
}
e.preventDefault();
arr.push(e);
if(arr.length>10){
flag = true;
list.dispatchEvent(evEnd);
list.dispatchEvent(evCancel);
list.dispatchEvent(ev);
return;
}
}
function endMove(e){
list.removeEventListener('touchmove', move, false)
list.removeEventListener('touchend', endMove, false)
}
http://jsfiddle.net/tnqHQ/17/

Javascript: Any workarounds for getting Chrome for Android to fire off touchmove and touchend event listeners other than using event.preventDefault()?

When using event listeners with the touchmove and touchend events, I can't get Chrome for Android to acknowledge those events unless I first use event.preventDefault(); earlier in the code. If I'm not wanting to block the default scroll functionality, is there any other workaround I can use to get Chrome for Android to acknowledge these events?
Sample code:
$(document).ready(function () {
// Bind touch event listeners.
var elem = $('html').get(0);
elem.addEventListener('touchstart', function (e) { console.info('"touchstart" detected. Coordinates - ' + getCoord(e)); });
elem.addEventListener('touchmove', function (e) { console.info('"touchmove" detected. Coordinates - ' + getCoord(e)); });
elem.addEventListener('touchend', function (e) { console.info('"touchend" detected. Coordinates - ' + getCoord(e)); });
function getCoord(e) {
var touch = false;
if (e.touches.length > 0) {
touch = e.touches[0];
} else {
touch = e.changedTouches[0];
}
if (touch) {
return 'x: ' + touch.pageX + ', y: ' + touch.pageY;
}
}
Example fiddle here: http://jsfiddle.net/jQ2VS/1/
Google Chrome will fire a touchcancel event about 200 milliseconds after touchstart if it thinks the user is panning/scrolling and you do not call event.preventDefault().
Assuming that you want to intercept horizontal touch events and let vertical touch events cause panning/scrolling, a workaround would be:
On touchstart, store the coordinates in a variable, and set iteration to 0.
For each touchmove event, set iteration to iteration+1.
When iteration is equal to 4 (just a "magic number" I found to be reliable on my set-up), calculate the total touch offset deltas for x- and y- axes.
EDIT: on mobile devices you'll only receive one touchmove without event.preventDefault()
If x-axis offset > y-axis offset * 3 then fire event.preventDefault(). (This ensures the the gesture is pretty much horizontal)
The down-side for this is that user can only either swipe left/right or scroll up/down.
Finally I found the solution (pure js) even in case you might want use it for swipe:
var swipe = function() {
var touchX, touchY, movX, movY, go;
function prevent(e){
e.preventDefault();
}
function start(e) {
go = false;
document.addEventListener("touchmove", prevent, false);
touchX = e.touches[0].pageX;
touchY = e.touches[0].pageY;
}
function move(e) {
movX = e.touches[0].pageX - touchX;
movY = e.touches[0].pageY - touchY;
if(!go) {
(Math.abs(movY) < Math.abs(movX)) ? go = true : stop(e);
} else {
/* *************** */
// cast your spell
/* *************** */
}
}
function stop(e) {
document.removeEventListener("touchmove", prevent, false);
}
document.addEventListener("touchstart", start, true);
document.addEventListener("touchmove", move, true);
document.addEventListener("touchend", stop, true);
document.addEventListener("touchleave", stop, true);
document.addEventListener("touchcancel", stop, true);
}
Hope this help.
The simplest answer is that you have to preventDefault on the first touchmove event otherwise they will be cancelled.
I found that preventing the touchcancel worked fine.
The accepted answer is not correct.
On Android if preventDefault is not set on touchstart the device assumes native scrolling and no more touch events are sent to webview. If preventDefault is set all native scrolling is disabled.
There is a shim to provide swipe events with native scrolling here : https://github.com/TNT-RoX/android-swipe-shim

Make ul list work like select input

I want to be able to use a ul list as an select form element, for styling reasons.
I'm able to populate an hidden input with my code (not included in this jsfiddle), and so far so good.But now I'm trying to let my ul behave like the select input when the keyboard is pressed, or the mouse is used.
In my previous question i had some problems with keyboard controls. They are now fixed. See: Autoscroll on keyboard arrow up/down
The problem that remains is that the mouse is not ignored when the keyboard buttons are pressed. This is causing the "hover effect" to listen to the keyboard input first, but than immediately going to the mouse and select this li item as being selected.
This can be seen in my jsfiddle example: http://jsfiddle.net/JVDXT/3/
My javascript code:
// scrollTo plugin
$.fn.scrollTo = function( target, options, callback ){
if(typeof options == 'function' && arguments.length == 2){ callback = options; options = target; }
var settings = $.extend({
scrollTarget : target,
offsetTop : 100,
duration : 0,
easing : 'linear'
}, options);
return this.each(function(){
var scrollPane = $(this);
var scrollTarget = (typeof settings.scrollTarget == "number") ? settings.scrollTarget : $(settings.scrollTarget);
var scrollY = (typeof scrollTarget == "number") ? scrollTarget : scrollTarget.offset().top + scrollPane.scrollTop() - parseInt(settings.offsetTop);
scrollPane.animate({scrollTop : scrollY }, parseInt(settings.duration), settings.easing, function(){
if (typeof callback == 'function') { callback.call(this); }
});
});
}
//My code
//The function that is listing the the mouse
jQuery(".btn-group .dropdown-menu li").mouseover(function() {
console.log('mousie')
jQuery(".btn-group .dropdown-menu li").removeClass('selected');
jQuery(this).addClass('selected');
})
//What to do when the keyboard is pressed
jQuery(".btn-group").keydown(function(e) {
if (e.keyCode == 38) { // up
console.log('keyup pressed');
var selected = jQuery('.selected');
jQuery(".btn-group .dropdown-menu li").removeClass('selected');
if (selected.prev().length == 0) {
selected.siblings().last().addClass('selected');
} else {
selected.prev().addClass('selected');
jQuery('.btn-group .dropdown-menu').scrollTo('.selected');
}
}
if (e.keyCode == 40) { // down
console.log('keydown');
var selected = jQuery('.selected');
jQuery(".btn-group .dropdown-menu li").removeClass('selected');
if (selected.next().length == 0) {
selected.siblings().first().addClass('selected');
} else {
selected.next().addClass('selected');
jQuery('.btn-group .dropdown-menu').scrollTo('.selected');
}
}
});
So could anyone teach me how to igonore the mouse when the keyboard buttons are pressed, but listing to the mouse when it's touched again by the user. Like the default select input form field.
Update
Here's a new jsfiddle.
Check this out:
http://jsfiddle.net/coma/9KvhL/25/
(function($, undefined) {
$.fn.dropdown = function() {
var widget = $(this);
var label = widget.find('span.valueOfButton');
var list = widget.children('ul');
var selected;
var highlighted;
var select = function(i) {
selected = $(i);
label.text(selected.text());
};
var highlight = function(i) {
highlighted = $(i);
highlighted
.addClass('selected')
.siblings('.selected')
.removeClass('selected');
};
var scroll = function(event) {
list.scrollTo('.selected');
};
var hover = function(event) {
highlight(this);
};
var rebind = function(event) {
bind();
};
var bind = function() {
list.on('mouseover', 'li', hover);
widget.off('mousemove', rebind);
};
var unbind = function() {
list.off('mouseover', 'li', hover);
widget.on('mousemove', rebind);
};
list.on('click', 'li', function(event) {
select(this);
});
widget.keydown(function(event) {
unbind();
switch(event.keyCode) {
case 38:
highlight((highlighted && highlighted.prev().length > 0) ? highlighted.prev() : list.children().last());
scroll();
break;
case 40:
highlight((highlighted && highlighted.next().length > 0) ? highlighted.next() : list.children().first());
scroll();
break;
case 13:
if(highlighted) {
select(highlighted);
}
break;
}
});
bind();
};
$.fn.scrollTo = function(target, options, callback) {
if(typeof options === 'function' && arguments.length === 2) {
callback = options;
options = target;
}
var settings = $.extend({
scrollTarget : target,
offsetTop : 185,
duration : 0,
easing : 'linear'
}, options);
return this.each(function(i) {
var scrollPane = $(this);
var scrollTarget = (typeof settings.scrollTarget === 'number') ? settings.scrollTarget : $(settings.scrollTarget);
var scrollY = (typeof scrollTarget === 'number') ? scrollTarget : scrollTarget.offset().top + scrollPane.scrollTop() - parseInt(settings.offsetTop, 10);
scrollPane.animate({scrollTop: scrollY}, parseInt(settings.duration, 10), settings.easing, function() {
if (typeof callback === 'function') {
callback.call(this);
}
});
});
};
})(jQuery);
$('div.btn-group').dropdown();
The key is to unbind the mouseover and rebind when mouse moves.
I refactored it a little by using a closure function, adding the logic to a jQuery method called dropdown so you can reuse it, using switch instead of a bunch of if's and more things.
Well, there are bazillions of plugins to transform a select to a list:
http://ivaynberg.github.io/select2/
http://harvesthq.github.io/chosen/
http://meetselva.github.io/combobox/
and I have mine too! (ready for touch devices using the same trick as http://uniformjs.com)
https://github.com/coma/jquery.select
But this question is about taking that HTML and make it behave like a select avoiding the hover issue right?
Here's a solution, I'm using mousemove as this will ensure that the right list item is selected as soon as the mouse starts moving again, with mouseover it only starts to select a list item upon entering a new list item:
Take the anonymous function and give it a name:
function mousemove() {
console.log('mousie')
jQuery(".btn-group .dropdown-menu li").removeClass('selected');
jQuery(this).addClass('selected');
}
Declare a global variable mousemoved indicating if the mouse has moved over the document and set it to false, on mousemove over the document, set it to true and attach the mousemove function to the mousemove event on the list items.
var mousemoved = false;
jQuery(document).mousemove(function() {
if(!mousemoved) {
$('.btn-group .dropdown-menu li').mousemove(mousemove);
mousemoved = true;
}
})
As soon as a key is pressed (at the start of the keydown event), use jQuery's .off() method to remove the mousemove event on the list items if it is present, and set mousemoved to false to ensure the mousemove event doesn't get attached again until the mouse is moved again.
jQuery(".btn-group").keydown(function(e) {
$('.btn-group .dropdown-menu li').off('mousemove');
mousemoved = false;
... // Some more of your code
Here's a jsFiddle.
I tried to solve your issue by prevent autoscroll, adding tabindex on the li, setting the focus on active, and using a flag to suppress mouse.
Fixed fiddle: http://jsfiddle.net/8nKJT/ [fixed an issue in Chrome ]
http://jsfiddle.net/RDSEt/
The issue is because of the automatic scroll which is triggered on keydown that again triggers mouseenter messes the selection of the li.
Note: The differences with the other approaches(answers here) I noticed is it scrolls on every keypress instead of scrolling only after reaching the top or bottom(normal behavior). You will feel the difference when you check the demo side-by-side.
Below is the list of change description and a small demo to explain how it was fixed,
Prevented auto scroll that is triggered on pressing up arrow/down arrow using e.preventDefault() http://jsfiddle.net/TRkAb/ [press up/down on the ul li], Now try the same on http://jsfiddle.net/TRkAb/1/ [No more scroll]
Added a flag on keydown to suppress the mouseevents on keydown, this flag is reset onmousemove
Added tabindex to li which would allow you to set focus using .focus function. [More info: https://stackoverflow.com/a/6809236/297641 ]
Calling .focus would automatically scroll to the desired location. (no need for scrollTo plugin) http://jsfiddle.net/39h3J/ - [Check how it scrolls to li that is on focus]
Check out the demo and code changes too (added few improvements) and let me know.
Also thanks to your question, I noticed this issue and bunch of other issue in one of the plugin I wrote.
I wrote a plugin few months back to filter options and also act exactly like a drop down.
DEMO: http://jsfiddle.net/nxmBQ/ [change filterType to '' to turnoff the filtering ]
The original plugin page is http://meetselva.github.io/combobox/
.. more
You could use a global to ignore the mouseover event if a keydown was pressed recently on the widget. For example:
var last_key_event = 0;
jQuery(".btn-group .dropdown-menu li").mouseover(function() {
if ((new Date).getTime() > last_key_event + 1000) {
console.log('mousie')
jQuery(".btn-group .dropdown-menu li").removeClass('selected');
jQuery(this).addClass('selected');
}
});
Then the keydown handler can set when it was handled to avoid interaction with the mouse:
//What to do when the keyboard is pressed
jQuery(".btn-group").keydown(function(e) {
last_key_event = (new Date).getTime();
...
});
May be it could make sense to have the last_key_event variable separate for each widget instead of being a global.
You could try this solution. It ignores the mousemove event if the coordinates have not changed (since the last mousemove event)
//The function that is listing the the mouse
var lastOffsets = "";
jQuery(".btn-group .dropdown-menu li").mouseover(function(e) {
var curOffsets = e.clientX+":"+e.clientY;
if(curOffsets == lastOffsets) {
// mouse did not really move
return false;
}
lastOffsets = curOffsets;
///// rest of your code
}
Updated fiddle to verify if this is what you were after:
http://jsfiddle.net/pdW75/1/
Approach A reasonable solution should imitate the behavior of other UI elements that serve a similar purpose. On all checked systems (Windows, Linux, major browsers), drop-down boxes behave as follows:
Mousing over an item highlights it. Pressing arrow keys change the selected element, and scroll accoringly. Moving the mouse selects the element underneath. If the selection is empty, pressing down selects the first element. Pressing up selects the last element.
Solution This code illustrates my approach to imitating the described behavior. It's kinda cool, try it...
Additional Considerations There would be a number of other options to suppress unwanted mouse movement to change the selected element. These include:
Keeping a state of last input method. If last selection was using the keyboard, hovering over an element will not select it, only clicking will
ignoring the mouseover event if the coordinates have not changed by a specified distance, e.g. 10 pixels
ignoring mouseover if the user has ever used the keyboard
However, at least for an application accessible to the public, it's always best to stick with established UI patterns.
The problem showing up is that when the mouse is left over a part of the expanded list, then selecting using the keys is nullified because the selection made by the keyboard immediately reverts to the item that happens to be under the mouse.
You can solve this problem and retain all functionality without doing any complicated conditional behavior or any removing of event handlers.
Just change your mouseover event handler to be a mousemove event handler. This way any keyboard navigation and selection is listened to and the mouse position is ignored anytime that the user is using the keyboard to select. And anytime the mouse is being used to select, then the mouse is listened to.
This sounds trivial but it seems to make your JS Fiddle behave perfectly and without any conflicting behavior between mouse and keyboard. Like this:
//The function that is listening to the mouse
jQuery(".btn-group .dropdown-menu li").mousemove...
(your code continues unchanged, only replacing mouseover with mousemove)

How do I handle these events and why is my code not working?

I need to do the following. As soon as the user clicks on a div, i want to save the mouse coordinations while the user is moving the cursor over the div and is holding the left mouse button. When the user leaves the div or releases the left button, i want to stop recording the coordinates. I've got the following code:
$(document).ready(function() {
var coordhdl = new coordinateHandler();
$("#test").mousedown(function(e) {
$("#test").mousemove(function(ee) {
$("#test").mouseup(function(e) {
stopIt = true;
});
if(stopIt == false)
{
coordhdl.addCords(ee.pageX - this.offsetLeft, ee.pageY - this.offsetTop);
}
});
});
});
The problems with this code are:
It records coordinate even when the user only clicked the div without pressing the left button.
It doesn't stop recording the coordinates once it has been clicked.
I am new to Javascript/jQuery, so I don't know very much about it.
Something like this should work. It sets a flag to true/false when the mouse is pressed/released respectively. When the mouse moves, if the flag is set, the coordinates are added:
$(document).ready(function() {
var isDown = false,
coordhdl = new coordinateHandler();
$("#test").mousedown(function() {
isDown = true;
}).mouseup(function() {
isDown = false;
}).mousemove(function(e) {
if(isDown) {
coordhdl.addCords(ee.pageX - this.offsetLeft, ee.pageY - this.offsetTop);
}
});
});
Here's a demo of something similar in action (it simply writes the coordinates to a p element instead of using your coordinateHandler object).
Don't attach the event handlers inside the event handlers. On every mouse move you attach a new mouseup event handler. They don't get overridden, they get appended.
Use a "global" flag instead:
$(document).ready(function() {
var coordhdl = new coordinateHandler(),
recording = false;
$("#test").mousedown(function(e) {
recording = true;
}).mousemove(function(e) {
if(recording) {
coordhdl.addCords(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
}
}).mouseup(function(e) {
recording = false;
});
});
Every time there is a mousedown event, you add a mousemove handler, and every time the mouse moves, you add another mouseup handler. I can't see where the stopIt variable is declared so the scope of this variable may also be an issue. You don't need to nest the handlers, so try it this way.
$(document).ready(function() {
var coordhdl = new coordinateHandler();
var isRecording = false;
$("#test").mousedown(function(e) { isRecording = true })
.mouseup(function(e) { isRecording = false })
.mousemove(function(ee) {
if(isRecording)
{
coordhdl.addCords(ee.pageX - this.offsetLeft, ee.pageY - this.offsetTop);
}
});
});

Categories