JavaScript on iOS: preventDefault on touchstart without disabling scrolling - javascript

I am working with JavaScript and jQuery in an UIWevView on iOS.
I'v added some javascript event handler that allow me to capture a touch-and-hold event to show a message when someone taps an img for some time:
$(document).ready(function() {
var timeoutId = 0;
var messageAppeared = false;
$('img').on('touchstart', function(event) {
event.preventDefault();
timeoutId = setTimeout(function() {
/* Show message ... */
messageAppeared = true;
}, 1000);
}).on('touchend touchcancel', function(event) {
if (messageAppeared) {
event.preventDefault();
} else {
clearTimeout(timeoutId);
}
messageAppeared = false;
});
});
This works well to show the message. I added the two "event.preventDefault();" lines to stop imgs inside links to trigger the link.
The problem is: This also seems to prevent drag events to scroll the page from happen normally, so that the user wouldn't be able to scroll when his swipe happens to begin on an img.
How could I disable the default link action without interfering with scrolling?

You put me on the right track Stefan, having me think the other way around. For anyone still scratching their head over this, here's my solution.
I was trying to allow visitors to scroll through images horizontally, without breaking vertical scrolling. But I was executing custom functionality and waiting for a vertical scroll to happen. Instead, we should allow regular behavior first and wait for a specific gesture to happen like Stefan did.
For example:
$("img").on("touchstart", function(e) {
var touchStart = touchEnd = e.originalEvent.touches[0].pageX;
var touchExceeded = false;
$(this).on("touchmove", function(e) {
touchEnd = e.originalEvent.touches[0].pageX;
if(touchExceeded || touchStart - touchEnd > 50 || touchEnd - touchStart > 50) {
e.preventDefault();
touchExceeded = true;
// Execute your custom function.
}
});
$(this).on("touchend", function(e) {
$(this).off("touchmove touchend");
});
});
So basically we allow default behavior until the horizontal movement exceeds 50 pixels.
The touchExceeded variable makes sure our function still runs if we re-enter the initial < 50 pixel area.
(Note this is example code, e.originalEvent.touches[0].pageX is NOT cross browser compatible.)

Sometimes you have to ask a question on stack overflow to find the answer yourself. There is indeed a solution to my problem, and it's as follows:
$(document).ready(function() {
var timeoutId = 0;
$('img').on('touchstart', function(event) {
var imgElement = this;
timeoutId = setTimeout(function() {
$(imgElement).one('click', function(event) {
event.preventDefault();
});
/* Show message ... */
}, 1000);
}).on('touchend touchcancel', function(event) {
clearTimeout(timeoutId);
});
});
Explanation
No preventDefault() in the touch event handlers. This brings back scrolling behavior (of course).
Handle a normal click event once if the message appeared, and prevent it's default action.

You could look at a gesture library like hammer.js which covers all of the main gesture events across devices.

Related

How to stop page scroll on mouse wheel interaction with input type=text [duplicate]

I need to make a webpage scrollable only by scrolling bar. I have tried to find how to catch scroll bar event, but as i see it is impossible. Currently i use this functions:
function preventDefault(e) {
e = e || window.event;
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
}
function wheel(e) {
preventDefault(e);
}
function disable_scroll() {
if (window.addEventListener) {
window.addEventListener('DOMMouseScroll', wheel, false);
}
window.onmousewheel = document.onmousewheel = wheel;
}
But they are not very useful in my situation, because they block all scroll events. Do you have any ideas? I am thinking about it 3 days already and i didn't find any answer (and questions also). Thanks!
Prevent the window from scrolling with mouse wheel:
As document level wheel event listeners are treated as Passive, we need to mark this event listener to be treated as Active:
window.addEventListener("wheel", e => e.preventDefault(), { passive:false })
If the content of a <div> (or other element) is scrollable, you can prevent it like this:
document.getElementById('{element-id}').onwheel = function(){ return false; }
More info about scrolling intervention and using passive listeners to improve scrolling performance.
Outdated Method:
window.onwheel = function(){ return false; } // Old Method
more info (thanks #MatthewMorrone)
jQuery solution to prevent window scrolling with mouse wheel:
$(window).bind('mousewheel DOMMouseScroll', function(event){ return false});
If you want to prevent scrolling with mouse wheel in a single DOM element, try this:
$('#{element-id}').bind('mousewheel DOMMouseScroll', function (e) { return false; });
The DOMMouseScroll event is used in Firefox, so you have to listen on both.
I'm currently using this and it works fine. Scrolling using the bar works fine, but mouse wheel won't work.
The reason i'm doing it this way is that I have custom code to scroll the way I want, but if don't add any code it will just don't scroll on wheel.
window.addEventListener('wheel', function(e) {
e.preventDefault();
// add custom scroll code if you want
}

Hammer JS Tap and Press on Mobile

I'm trying to build the following for mobile browsers: I have a row of boxes. Each box holds additional information underneath and a href to another website. When I scroll the page via a certain box or just taphold it, i want to show the additional information. If I tap it, i want to go to the href.
I've built a solution with click, touchstart and touchend, which worked, but quite buggy, because click and touchstart are interfering (I guess). So I'm hoping to build a more solid version with hammer.js. My Idea of the event handling:
On touch start : The additional Information ist Shown.
If I release until 250ms: The Touch is counted as a tap and I'm sent to the href.
After 251ms: It's defenitely a taphold
On scrolling/touch move: It's defenitely a taphold
That's the js i have until now. I changed the starting point of the press time to 1ms and the threshold to 1000px to be able to scroll. pressup doesnt trigger if i press and scroll. I think pan is triggering instead? How can I change the settings of pressup?
var myElement = document.getElementById('myElement');
var mc = new Hammer(myElement);
mc.add(new Hammer.Press({
event: 'press',
pointer: 1,
threshold: 1000,
time: 1,
}));
mc.on('press', function(event) {
$('.skills').addClass( "show" );
});
mc.on('pressup', function(event) {
$('.skills').removeClass( "show" );
});
I've built a codepen (or where should i post it for mobile testing?)
http://codepen.io/Vin-ni/pen/JXYMXm
So I need two things to happen. Pressup needs to trigger and on Pressup i need to check if the time since press is more or less than 250ms.
it would be something like
mc.on('pressup', function(event) {
$('.skills').removeClass( "show" );
if (time since press < 251ms) {
window.location.href = (this).data(link);
}
});
Thanks a lot!!
My Solution (It binds the event to each div in a grid):
$('.class').each(function(){
var $this = $(this);
var mc = new Hammer.Manager(this);
mc.add( new Hammer.Tap() );
//customize Press event to trigger instantly + 1000px scrollable
mc.add(new Hammer.Press({
event: 'press',
pointer: 1,
threshold: 1000,
time: 1,
}));
mc.on('press tap', function(event) {
//do stuff
if (event.type == "tap") {
window.location.href = link;
$('.skills').removeClass( "show" );
}
});
mc.on('pressup', function(event) {
//undo stuff
});
});

Click triggers mousemove in Chrome [duplicate]

I don't know whether it is only Chrome problem (can't check now), however let's try the following piece of code, where we bind two events to some element:
$("div").on({
mousemove: function(e) {
console.log("move");
},
click: function(e) {
console.log("click");
}
});
If we try to click the element, we'll find that for some reason mousemove event fires immediately after click, so in console we have:
>> ...
>> click
>> move
DEMO: http://jsfiddle.net/gKqVt/
Note, that mousedown and mouseup events work by the same scenario.
I saw many questions on SO about the same problem, but none (in my search) gave the straightforward idea what to do in order to fire the click event only.
Mousemove appears to be binded to every mouse action there is in Chrome, so store the mouse position every time the mouse "moves" and check it against the previous mouse position to validate that it has indeed "moved"..
var currentPos=[];
$("div").on({
mousemove: function(e) {
if (e.pageX!==currentPos[0] && e.pageY !==currentPos[1]){
currentPos=[e.pageX,e.pageY];
this.innerHTML = "Event: " + e.type;
console.log("move");
}
},
click: function(e) {
this.innerHTML = "Event: " + e.type;
console.log("click");
}
});
Demo | Source
This appears to be a bug in Chrome that was first reported back in November, and remains open.
Chromium Issue 161464
If you are targeting Chrome specifically then it may be worth comparing the event timestamps to get around it (using some minimum delta time as #ExplosionPills suggested. But if you're looking for general behavior it seems that you're better off treating them as separate events, because in every browser but chrome (and maybe Safari? the bug is labeled as webkit-core) they will in fact be separate events.
This behavior is odd, and it doesn't seem to occur universally (happens in Chrome/IE for me, but not FFX). I think you haven't gotten a straight answer because there isn't one really.
It's possible that the mouse is moved very slightly by the click action, but that's probably not it. Could just be a browser quirk. These don't even seem to be the same event since stopImmediatePropagation in click doesn't stop mousemove from firing. If you focus the element and hit a keyboard button, it will actually trigger click and only click.
Since this is so quirky, it seems like the only way to deal with it is times. As much of a kludge as this is, I do notice that click happens one millisecond before mousemove, so you could get close by comparing the click timestamp + 2 (or 10):
mousemove: function(e) {
if ($(this).data('lastClick') + 10 < e.timeStamp) {
http://jsfiddle.net/gKqVt/3/
This is very specific, though. You should consider not having behavior that occurs immediately on mousemove since it's so frequent.
Why don't just check that did the mouse really move or not like below:
function onMouseDown (e) {
mouseDown = { x: e.clientX, y: e.clientY };
console.log("click");
}
function onMouseMove (e) {
//To check that did mouse really move or not
if ( e.clientX !== mouseDown.x || e.clientY !== mouseDown.y) {
console.log("move");
}
}
FIDDLE DEMO
(I think it's will still correct in all browsers)
var a,b,c,d;
$(".prd img").on({
mousedown: function(e){
a= e.clientX, b= e.clientY;
},
mouseup: function(e){
c= e.clientX, d= e.clientY;
if(a==c&&b==d){
console.log('clicked');
}
}
});
Try this. This one work correct.
I noticed this behavior when I needed to differenciate between mousedown and mouseup without dragging between the two and mousedown and mouseup with dragging between them, the solution that I used is as follows:
var div = $('#clickablediv');
var mouseDown = false;
var isDragging = 0;
div.mousedown(function () {
isDragging = false;
mouseDown = true;
}).mousemove(function () {
if (mouseDown) isDragging++;
}).mouseup(function () {
mouseDown = false;
var wasDragging = isDragging;
isDragging = 0;
if (!wasDragging || wasDragging<=1) {
console.log('there was no dragging');
}
});
when I tried it, I noticed that periodacaly a simple click makes "isDragging" equal to 3 but not very frequently
I added the following to my mouseMove(event) function:
function mouseMove(event)
{
if ((event.movementX == 0) && (event.movementY == 0)) return;
Not clear why it triggers when there is no movement, but this worked for me. Had the issue in Chrome 102.0.5005.61 at least. It did not happen a few years ago.

Prevent mouse wheel scrolling, but not scrollbar event. JavaScript

I need to make a webpage scrollable only by scrolling bar. I have tried to find how to catch scroll bar event, but as i see it is impossible. Currently i use this functions:
function preventDefault(e) {
e = e || window.event;
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
}
function wheel(e) {
preventDefault(e);
}
function disable_scroll() {
if (window.addEventListener) {
window.addEventListener('DOMMouseScroll', wheel, false);
}
window.onmousewheel = document.onmousewheel = wheel;
}
But they are not very useful in my situation, because they block all scroll events. Do you have any ideas? I am thinking about it 3 days already and i didn't find any answer (and questions also). Thanks!
Prevent the window from scrolling with mouse wheel:
As document level wheel event listeners are treated as Passive, we need to mark this event listener to be treated as Active:
window.addEventListener("wheel", e => e.preventDefault(), { passive:false })
If the content of a <div> (or other element) is scrollable, you can prevent it like this:
document.getElementById('{element-id}').onwheel = function(){ return false; }
More info about scrolling intervention and using passive listeners to improve scrolling performance.
Outdated Method:
window.onwheel = function(){ return false; } // Old Method
more info (thanks #MatthewMorrone)
jQuery solution to prevent window scrolling with mouse wheel:
$(window).bind('mousewheel DOMMouseScroll', function(event){ return false});
If you want to prevent scrolling with mouse wheel in a single DOM element, try this:
$('#{element-id}').bind('mousewheel DOMMouseScroll', function (e) { return false; });
The DOMMouseScroll event is used in Firefox, so you have to listen on both.
I'm currently using this and it works fine. Scrolling using the bar works fine, but mouse wheel won't work.
The reason i'm doing it this way is that I have custom code to scroll the way I want, but if don't add any code it will just don't scroll on wheel.
window.addEventListener('wheel', function(e) {
e.preventDefault();
// add custom scroll code if you want
}

how to reverse e.preventDefault() from the body?

I have this:
function dontMove(event) {
// Prevent page from elastic scrolling
event.preventDefault();
}
&
<body ontouchmove="dontMove(event);">
This, on the ipad, stops it from being draggable and does not allow that grey background the ipad has when you drag a whole page around to show up.
I have seen on another website that its possible to reverse that in another div, so that div is completely draggable again.
Does anyone know how to reverse it?
I have also tried using this to prevent it (in the document.ready):
document.ontouchmove = function(e){
e.preventDefault();
}
& this to enable it:
function doTouchMove(state) {
document.ontouchmove = function(e){
return state;
}
}
Then I put this to activate it.
<img ontouchmove="doTouchMove(state);" src="../jpeg/pages/01.jpg" class="touch"/>
This didn't seem to work
Is there anything wrong with this?
Or any other way that might work?
This is exactly why bubbles is slightly better(at least in my opinion).
bubbles is cross browser, so you should be able to replace.
e.preventDefault()
with
e.bubbles = false;
and then latter in your code, you could potentially reset bubbles to true.
If the above isn't an option then just ignore. :D
An alternative(if you are just working with an iPad) is to just reverse how the DOM works.
document.addEventListener('click', function(){}, true );
This will force the event to work in the other direction.
Document click execute
|
|
v
Element click execute
try this post, HTML with event.preventDefault and erase ontouchmove from body tag.
Mine looks like this
<script>
// Get touch move enevt from IOS
document.ontouchmove = function (event) {
if (!event.elementIsEnabled)
event.preventDefault();
};
// Get touch move enevt from IOS
function enableOnTouchMove(event) {
event.elementIsEnabled = true;
};
</script>
then enable ontouchmove on every tag you want. ie:
<div ontouchmove="enableOnTouchMove(event)" id="listing">
I managed to solve it with
$('#form1').unbind('submit').submit();
You can solve it by preventing the event only if it comes from the body:
document.ontouchmove = function(event){
if(event.target.tagName == "BODY"){
event.preventDefault();
}
}

Categories

Resources