Javascript - Can't get postion of cursor on Touchend event - javascript

im trying to create a small script for simulating a swipe on a web page, here is the code :
var startX;
var endX;
document.body.addEventListener('touchstart', function(e) {
startX = e.targetTouches[0].pageX;
console.log(startX);
}, false);
document.body.addEventListener('touchend', function(e) {
endX = e.targetTouches[0].pageX;
console.log(endX);
});
But when im swiping on the browser it says >TypeError: e.targetTouches[0] is undefined
I tried to only display the event with :
console.log(e);
and it seems there is no position for the touchend event.
I need help please :(

Related

Scrolling with mouse drag inside a container

I have a scrollable list. It has the overflow-y property set to scroll and I can scroll with the scroll bar but I would like to be able to use my mouse to drag up and down.
Here is what I have so far
//Allows the user to scroll by dragging the mouse
element.on({
'mousemove': function(e)
{
if (self.get('clicked'))
{
const difference = self.get('clickY') - element.scrollTop();
const scrollY = difference + (e.pageY - element.offset().top);
element.scrollTop(scrollY);
}
},
'mousedown': function (e)
{
const clickY = (e.pageY - element.offset().top) + element.scrollTop();
self.set('clicked', true);
self.set('clickY', clickY);
},
'mouseup': function (e)
{
self.set('clicked', false);
},
'mouseleave': function(e)
{
self.set('clicked', false);
}
});
I have been playing around with it but just cant get it right, because I have to take into consideration the y position of the element, how much its already scrolled, the mouseY etc. Would someone be able to help me out with this please?

How to catch mouse left button on mouseover event?

I am trying to catch when user press left button on mouse while hovering over cells in a html table using vanilla javascript. The purpose is to paint a cell in black when user is clicking with mouse while dragging (drawing like in MsPaint, when you draw a line for example)
I added an "over" event listener on each td of my table and used buttons property to check if left button is pressed or not:
celle = document.getElementsByTagName("td");
for (i=0;i<celle.length;i++)
celle[i].addEventListener("mouseover", function(e){
if(e.buttons == 1 ){
e.target.style.backgroundColor="black";
}
})
This code works but not always and not perfectly. First it starts setting the background color of the next element, not the one on which I pressed the mouse. Moreover, sometimes it doesn't set any color at all (there is a small icon like "accessed denied" in Chrome's window). It appears to work quite randomly and unpredicatably.
I tried also with jQuery, but I found similar problems. Anyone can help me?
Thanks a lot
Split the problem into several parts. I would add a mousedown and mouseup eventlistener to the whole window and set a global state if you're currently drawing:
var drawState=false
window.addEventListener("mousedown",function(e){
if(e.button===1){
drawState = true;
}});
window.addEventListener("mouseup",function(e){
if(e.button===1){
drawState = false;
}});
You can improve the window listeners with some checks, if the mouse is over a cell.
After this you can add a mouseenter listener to all your cells. Mouseenter is only fired once you enter a cell and not on every move inside the element:
celle[i].addEventListener("mouseenter", function(e){
if(drawState){
e.target.style.backgroundColor="black";
}
})
Instead of tracking mouseover, track three events:
mousemove - to constantly get the mouse position
mousedown - to set the mouse state as currently clicked down
mouseup - to set the mouse state as currently released
It works this way:
handleMousemove constantly updates the mouse position and check mouse state
When the mouse is clicked down, handleMousedown is fired
handleMousedown set the state as 'down'
When handleMousemove sees that mouse state is 'down', it fires click event at the current mouse position
When the mouse is released, handleMouseup is fired
handleMouseup set the state as 'released' and everything returns to normal
Repeat
var mouseIsDown = false;
var mousePosition = { x:-1, y:-1 };
let handleMousemove = (event) => {
// get the mouse position
mousePosition.x = event.x;
mousePosition.y = event.y;
if(mouseIsDown) // if mouse state is currently down, fire click at mouse position
{
let elem = document.elementFromPoint(mousePosition.x, mousePosition.y);
// you can add some conditions before clicking
if(something)
{
elem.click();
}
}
};
let handleMousedown = (event) => {
mouseIsDown = true;
// set the mouse state as 'down'
};
let handleMouseup = (event) => {
mouseIsDown = false;
// set the mouse state as 'release'
};
document.addEventListener('mousemove', handleMousemove);
document.addEventListener('mousedown', handleMousedown);
document.addEventListener('mouseup', handleMouseup);
Working fiddle: https://jsfiddle.net/Black3800/9wvh8bzg/5/
Thanks to everybody for your kind answers. Proposed codes work almost ok. The only problem is that sometimes browser shows the NO SYMBOL cursor. Unfortunately I can't post an image but you can find it here:
NO Symbol
and the only way to keep on drawing is clicking outside the table and then clicking again inside.
This is my code:
var mouseIsDown = false;
var mousePosition = { x:-1, y:-1 };
let handleMousemove = (event) => {
// get the mouse position
mousePosition.x = event.x;
mousePosition.y = event.y;
if(mouseIsDown) // if mouse state is currently down, fire click at mouse position
{
let elem = document.elementFromPoint(mousePosition.x, mousePosition.y);
// you can add some conditions before clicking
if (event.buttons==1)
{
elem.click();
}
}
};
let handleMousedown = (event) => {
mouseIsDown = true;
// set the mouse state as 'down'
};
let handleMouseup = (event) => {
mouseIsDown = false;
// set the mouse state as 'release'
};
document.addEventListener('mousemove', handleMousemove);
document.addEventListener('mousedown', handleMousedown);
document.addEventListener('mouseup', handleMouseup);
celle = document.getElementsByTagName("td");
for (i=0;i<celle.length;i++)
celle[i].addEventListener("click", function(e){
e.target.style.backgroundColor="black";
}
)
Isn't it easier to just add a listener for "click" ? If the element is clicked it also over the cell.
celle[i].addEventListener("click", function(e){
e.target.style.backgroundColor="black";
}

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

Javascript/jQuery focusout event that changes layout causes click event to not fire

I have a field that when you leave focus on it, it changes the layout of the page. I also have buttons on the page that submit my form.
If I go into my field and type a value, then click the button, the button click event never fires. This seems to happen because the layout is changing before the click event gets fired, which means the button changes places. By the time the click event fires, it's firing on an empty area, not the button.
Here is a jsfiddle of the issue: http://jsfiddle.net/xM88p/
I figured out a way to solve this for IE but after extensive research I can't find/access the same object in FF/Chrome:
//only works in IE
if(event.originalEvent.toElement){
$("#"+event.originalEvent.toElement.id).click();
}
http://jsfiddle.net/xM88p/2/
Use mousedown instead of click:
$("#btn_test").on('mousedown', function (event){
alert("clicked!");
});
$('#test').focusout(function (event){
$('<p>Test</p>').insertAfter(this);
});
Edit
Okay, I got a little more creative with the event handlers. The new solution keeps track of mousedown/mouseup events as well as the position of the click. It uses these values to check whether mouse up should execute an alert.
var testClicked = false;
var lastX, lastY;
$(document).on('mouseup', function (event) {
if (testClicked === true && lastX === event.clientX && lastY === event.clientY) {
alert("clicked!");
}
testClicked = false;
lastX = null;
lastY = null;
});
$("#btn_test").on('mousedown', function (event){
testClicked = true;
lastX = event.clientX;
lastY = event.clientY;
});
$('#test').focusout(function (event){
$('<p>Test</p>').insertAfter(this);
});

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