Is there a way to prevent scrolling of a rendered HTML page in the Android browser? The following does not appear to have any impact on page scrolling in the Android browser:
var preventDefault = function(e) {
e.preventDefault();
return false;
};
document.addEventListener('touchmove',preventDefault,false);
document.body.addEventListener('touchmove',preventDefault,true);
window.addEventListener('touchmove',preventDefault,true);
(I've tried with bubbling on and off.)
It looks to me like Android Webkit makes the "window" the same length as the document, so scrolling is being done on the browser itself, not on the document body or DOM window object. What's weird is that that's exactly what webkit on iOS does, but the code above still works.
Answering my own question.
The problem ended up being that you need to capture and suppress ontouchstart as well as ontouchmove on document to stop the browser from scrolling. This is definitely different in iOS, but it still works identically on both platforms.
The actual code I ended up using looks sort of like this:
var preventDefault = function(e){
e.preventDefault();
};
var touchstart = function(e) {
document.addEventListener('touchstart', preventDefault,false);
document.addEventListener('touchmove', preventDefault,false);
/*do other stuff*/
};
var touchend = function(e) {
document.removeEventListener('touchstart', preventDefault,false);
document.removeEventListener('touchmove', preventDefault,false);
};
element.addEventListener('touchstart', touchstart, false);
element.addEventListener('touchend', touchend, false);
Not works on Chrome on Android though
But preventing event on window and stopping immediate propagation helps!
Handler should be not passive to do that.
MAY BE adding an event handler on capturing phase would help too
But this snippet below is tested by me
window.addEventListener("touchmove", function(event) {
event.preventDefault();
event.stopImmediatePropagation();
}, { passive: false });
DEMO https://codepen.io/ColCh/full/qvLqoe
Related
I am having some trouble with a mobile menu. At first i had my code written like this which worked fine on desktop
$('#nav-icon3').click(function(){
$(this).toggleClass('open');
});
$('.menu-item').click(function(){
$('#nav-icon3').toggleClass('open');
});
after uploading and checking on my mobile device via android chrome the click function was not working, so i tried using the touchstart.
$('#nav-icon3').on('touchstart', function(){
$(this).toggleClass('open');
});
$('.menu-item').on('click touchstart', function(){
$('#nav-icon3').toggleClass('open');
});
when touching the menu-item element, the toggle fires twice. Anyway I can prevent this from happening and let it only fire once?
I think what is going on based on your question is that your event is propagating. What you can do is the stop the event from propagating by doing something like this.
$('.menu-item').on('click touchstart', function(e) {
e.stopPropagation();
$('#nav-icon3').toggleClass('open');
});
More reference here:
https://api.jquery.com/event.stoppropagation/
Using e.preventDefault(); worked for me when I had the same issue as you.
$('#nav-icon3').on('touchstart', function(e){
e.preventDefault();
$(this).toggleClass('open');
});
I'm trying to get Hammer update the event.target element while using the "pan" event on mobile Android (so far only checked 4.2).
The problem is if I attach Hammer to the ul and pan along the li elements the event.target is returned correctly on desktop browsers (Chrome in particular - check the console), but mobile Android event.target is always the first one clicked.
The code:
$(document).ready(function() {
console.log('fired up!');
var myParent = document.getElementsByClassName('test')[0];
var mc = new Hammer.Manager(myParent, {domEvents: true});
mc.add( new Hammer.Pan({ direction: Hammer.DIRECTION_ALL, threshold: 0 }) );
mc.on("pan", function(ev) {
console.log(ev.target);
});
});
An example can be found here:
http://designingreen.net/tests/test-hammer.html (just check the source)
Am I doing something wrong or is it a matter of Hammer?
Edit: Apparently this is an issue in Android and "touchmove" event. A good workaround solution is availible here:
How to find out the actual event.target of touchmove javascript event?
Apparently this is an issue in Android with "touchmove" event. A good workaround solution is availible here:
How to find out the actual event.target of touchmove javascript event?
I can't get all of these to work together. I have phonegap/JQM running with fastclick.js perfectly on iOS. It's a dream. But for some reason I still get a 300ms delay on android. I put some alerts in and the code is being called. It's baffling really. I am testing on a motorola droid razor maxx.
In my index.html file:
<!DOCTYPE html>
<html>
<head>
...
<script type='application/javascript' src='js/fastclick.js'></script>
</head>
<script>
$(document).on("pagebeforechange", function (e, data) {
FastClick.attach(document.body);
alert('fastclick attached');
var to_page = data.toPage[0].id;
// skip showing #myPage if condition is true
if (to_page == "index") {
$.mobile.pageContainer.pagecontainer('change', 'event-list.html');
e.preventDefault();
}
});
</script>
<body>
<div id="index" data-role="page">
This is the index page.
</div>
</body>
</html>
But it doesn't seem to work. I've also tried attaching it like:
window.addEventListener('load', function() {
new FastClick(document.body);
}, false);
Which both work on iOS but don't seem to have any effect on android. Any suggestions?
edit: It seems if I remove the JQuery libraries it works fine. There has got to be a conflict somewhere. Any idea what it may be? I am using JQM 1.4.
edit: I have also tried using vclick to no avail
$("#test-element").bind('vclick',function() {
$.mobile.pageContainer.pagecontainer('change', 'description.html?lunch_pk=2133',{
transition: "slide",
});
});
...
<h1 id='test-element'> CLICK HERE FOR TEST </h1>
I am also using gestures to change pages which are also being delayed by 300ms, so I don't think that even if vclick was working that it would be a complete solution.
edit: ok, so after some further testing, I am pretty sure the delay is coming from inside JQM pagechange functions. I did a console.log inside the vclick and I the log is pretty responsive when hitting the button. I am trying to dig through JQM but not being very successful, I mean why would it be seemless on iOS and not work on android? And maybe I just need to find a better mobile library.
Answer
I was never able to solve this problem. My solution was to switch libraries. I went to Intel's mobile app framework which was able to do everything I was doing with JQM only more successfully.
You can try vclick without trying onclicks. These built-in jQuery Mobile vclick omits the 300ms delay. I do this by doing this.
$("#element").bind('vclick',function(event) {
yourFunction(this.id);
event.preventDefault();// this prevents the default click event
});
Have you tried opening jQueryMobile library file? There are some functions like:
setTimeout(function() {
$link.removeClass( $.mobile.activeBtnClass );
}, 300 );
I am sure, that searching this file for "delay" or "timeout" and changing it would give a good result. Line above is from
.mobile.popup.handleLink = function( $link ) {
...
}
Try registering FastClick inside the deviceready event handler:
document.addEventListener('deviceready', function() {
FastClick.attach(document.body);
}, false);
Have you tryed to juse the tap event?
$("#test-element").off('tap').on('tap', function(event) {...do your stuff});
Note that .bind is deprecated - better use on / off
Note that depending upon, where you're attaching your eventhandler the eventhandler might get bound multiple times on pagechange and revisit.
You best bind your tap-event-handler in ther pageinit event in order to make sure, that you attaching to the event only once and not every time, you revisit a certain page.
In case you're attaching on pageshow use the "off" first (see above)
I had the same problem with some menu buttons. My solution works for all platforms without a helper library, however, I wish there was a better way like setting a JQM variable:
$('#button').unbind('touchstart click').bind('touchstart click', function(event) {
$('#button').addClass('ui-btn-active');
//doSomethingHere();
setTimeout(function() {
$('#button').removeClass('ui-btn-active');
}, 300); //this 300ms is just the delay for styling the button
event.preventDefault(); //if touchstart is supported, do not let the event propagate to the click handler. Having this here avoids a double trigger.
});
The key is binding to touchstart which triggers immediately.
NOTE- I have this code within a pageshow handler which is why I unbind and then bind it. Otherwise, you'd end up with the same event bound multiple times as the user navigates to and from this page.
There are durations associated with the page transition animations in JQM. Here is some of the CSS for the default 'fade' transition (from jquery.mobile.structure.css v1.4.2):
.fade.out {
opacity: 0;
-webkit-animation-duration: 125ms;
-webkit-animation-name: fadeout;
-moz-animation-duration: 125ms;
-moz-animation-name: fadeout;
animation-duration: 125ms;
animation-name: fadeout;
}
.fade.in {
opacity: 1;
-webkit-animation-duration: 225ms;
-webkit-animation-name: fadein;
-moz-animation-duration: 225ms;
-moz-animation-name: fadein;
animation-duration: 225ms;
animation-name: fadein;
}
JQM changes classes on the to and from pages when transitions start and complete, so in the case of the 'fade' transition, the page being changed to will become the active page 225ms after the from page has completed fading out (125ms) i.e. after 350ms.
You could try disabling the transition by specifying {transition: 'none'} in your call to $.mobile.pageContainer.pagecontainer('change' or by setting $.mobile.defaultPageTransition = "none"; in your mobileinit event handler to rule it as the cause of the delay.
I've always disabled page transitions in my Phonegap JQM apps because of the poor performance and flickering (Android), but I still have responsiveness issues, especially on Android. I think it is down to how the webview prioritises rendering the DOM. I've found that a strategically positioned setTimeout can make page changes seem more responsive by allowing the webview to postpone my application logic until after it has rendered the DOM.
In my (android) phonegap app, I've added the code below to show a loading-spinner while logging in on facebook.
At first start-up, it works beautifuly. However, when I press the back-button and launch the app again, the spinner is out of control: it spins way too fast and with short pauzes. Also, the spinner appears not in the center of the screen, but slightly higher (it looks like it's centered with my text that follows afterwards).
Anyone familiar with this issue and knows how to fix it?
function DeviceReadyListener() {
$.mobile.loading( 'show' );
console.log("Device ready");
try {
FB.init({ appId:"xxx", nativeInterface:CDV.FB, useCachedDialogs:false });
navigator.splashscreen.hide();
getLoginStatus();
document.addEventListener("offline", onOffline, false);
} catch (e) {
alert(e);
}
}
I have a small phonegap application with jquery mobile and backbone.
I'm trying to show popup to user by manually calling .popup() method.
Everything works fine on iOS but on android I got strange issue: popup is showing for few moments and than disappear.
Here the actual code:
var PostView = Backbone.View.extend({
events: {
'touchend .add-comment-button': 'addComment'
},
addComment: function() {
this.$(".comment-popup").popup('open', { history: false });
return false; // Stop bubbling.
}
});
I'm using history: false because this popup is actualy part of subpage.
The code looks very simple, I'm just can't understand why it can disappear, and why this happen only on android devices.
Thanks, and sorry for my bad english.
I spent hours trying to fix this problem.
Finally I ended up doing the following two things that seemed to fix the problem.
1 - Use the uncompressed jqm file. i.e jquery.mobile.1.2.0.js
2 - I was triggering the popup programatically using the 'tap' option - once changed to the 'click' option it worked.
$('.option').live('click', function() {
$('#popup-div').popup('open');
});
I spent hours trying to fix this problem.
Finally I ended up doing the following two things that seemed to fix the problem.
this code snippet may help you ->
$('#testBtn').on('tap',function(e){
console.log("button clicked");
e.preventDefault();
$('#testPOPUP').popup("open");
});
Please note i have used e.perventDefault().
I didn't feel like changing my .tap() events to the click event and I didn't have a case where I could use preventDefault()so I just added a timeout to the popup('open') line. My hoverdelay in jqm is set to 150 so I set this timeout to 600 just to be on the safe side. Works fine, doesn't feel sluggish for the user.
One way to 'fix' it is by setting data-history="false" on the popup div
See also this question
JQuery Mobile popup with history=false autocloses
I have the exact same problem when trying to use popup('open') on an android 2.3 device (both in native browser and in firefox) and it works just fine on browsers on other devices. I'm also using backbone event management to open my popup (used the tap event and no aditionnal options to popup).
What I did to 'correct' the problem is that I removed the backbone event management for this event and added a listener in the render function. In your case this would look something like this :
events: {
// 'touchend .add-comment-button': 'addComment'
},
render: function() {
$(this.el).html(this.template(this.model));
$(this.el).find('.add-comment-button').tap(function(el){
this.addComment(el);
return false;
}.bind(this));
}
I have no idea where the problem comes from (must be some incompatibility between backbone and jquery mobile) and why we only see it on android but for the moment with this workaround my app seems to work fine on any device.
Edit: oops, it turns out that in my case the problem was I was missing "return false;" in the function dealing with the event.
Now that I added it, it works correctly with the backbone event management.
Sadly that doesn't explain why you have the issue and why I was seeing it only on android.
In case it helps anyone, I had the same problem occurring with Bing Maps, with the Microsoft.Maps.Events.addHandler(pin, 'click', callback) method.
Not particularly nice, but instead I stored an ID in pushpin._id and did the following:
$("#page").on('vclick', function (event) {
if (event.target.parentElement.className === "MapPushpinBase") {
$("#stopPopup").popup('open');
}
});
One brute force option is to check whether popup was hidden and reopen it.
In a loop, because the exact time the popup becomes hidden seems to be varied.
var hidden = $('#' + id + '-popup') .hasClass ('ui-popup-hidden')
if (hidden) $('#' + id) .popup ('open')
A working example: http://jsfiddle.net/ArtemGr/hgbdv9s7/
Another option could be to bind to popupafterclose:
var reopener = function() {$('#' + id) .popup ('open')}
$('#' + id) .on ('popupafterclose', reopener)
$('#' + id) .popup ('open')
Like here: http://jsfiddle.net/ArtemGr/gmpczrdm/
But for some reason the popupafterclose binding fails to fire on iPhone 4 half of the time.