changePage "jumps" back to old page - jquery-mobile

I've a big problem with a jQuery Mobile Application:
I'm using custom functions (they are triggered by onClick) to switch the page with currentPage.
It only happens on Android-Devices on sites in which has changed (due to ajax requests) with the integrated browser. iOS and Chrome works nice.
After clicking on an element, the animation started but just before it ends, it switches back to the old page. After a half second, it switches back to the new.
I made a movie of the bug here: http://www.youtube.com/watch?v=sXxvVUxniNg
Thank you very much
Code (CoffeeScript):
class Guide
#categoriesLoaded = false
#loadSearch: ->
$.mobile.changePage $("#guide"),
transition: 'slide'
changeHash: false
if !#categoriesLoaded
#categoriesLoaded = true
GuideApi.getCategories (data) ->
output = Mustache.render $("#tmpl-guide-categories-select").html(),
categories: data
$("#guide-search-category").append output
$("#guide-search-category").val($("#guide-search-category option:first").val());
window.WgSwitchGuide = ->
Guide.loadSearch

I was having the same issue. And I tried everything, I finally end with the solution. What I found was the error was principally within the browser. So I set the configuration of the pushStateEnabled as false. I did it by doing the following, adding this script.
<script type="text/javascript">
$(document).bind("mobileinit", function(){
$.mobile.pushStateEnabled = false;
});
</script>
It should be add before the jquery-mobile script is call, for more information you could see it on JQuery description
And it solved the problem no more jumping back.

I was having the exact same issue on both android and ios. For me, it was happening for heavy pages, i.e., pages with complex elements etc. Looks like you are using "slide" transition, which was what I was using as well. Taking out the page transitions (i.e., $.mobile.changePage("page.html", { transition: "none" })) for those pages resolved this issue for me. Hope this helps.
If you want to retain the transition, you can try preloading the page first when the previous page is being shown, by using the $.mobile.loadPage, and then show the transition. I am myself exploring this route, but it is probably worth trying.
Edit: OK - I explored the last suggestion and this doesn't seem to be working. Will stick with the first option.

Would you try to add the event stopPropagation and preventDefault methods on the first page's click event? This way the default action of the click event will not be triggered. Moreover the stopPropagation prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
event.stopPropagation();
event.preventDefault();
Example:
$("p").click(function(event){
event.stopPropagation();
event.preventDefault();
// change page
});

After trying for weeks to find a solution to this, I ended up doctoring the JQM library to disable page transitions one right after another. It's not a good solution, but it's the only thing I could get to work.
I was getting pages jumping back on both $.mobile.changePage and on anchor links. I used the slide transition, but removing it did not fix the problem. Setting pushStateEnabled to false did not work either. The jumps were happening on all devices and browsers (that I tested, anyway).
So here's what I did to the JQM library (v1.3.2).
Before the $.mobile.changePage function is defined, I added:
var justChangedPage = false;
Then within the function there's a line that goes:
if ( pbcEvent.isDefaultPrevented()) {
return;
}
which I changed to:
if ( pbcEvent.isDefaultPrevented() || justChangedPage) {
return;
}
Then right after this part of the $.mobile.changePage function:
if ( toPage[ 0 ] === $.mobile.firstPage[ 0 ] && !settings.dataUrl ) {
settings.dataUrl = documentUrl.hrefNoHash;
}
I added:
justChangedPage = true;
setTimeout(function() {
justChangedPage = false;
}, 500);
(Putting that earlier in the function didn't work -- all that stuff executes more than once within a single page transition. And half a second seemed to be the minimum timeout that prevented the page jumps.)
I hope this helps someone even if it is a hack...

What is your JQM and Android version?
I'm not sure If I understand correctly. I think transition flicker maybe come from the following assumption.
Heavy page DOM transition.
Using "translate3d" somewhere in css file.
Not using "H/W Acceleration" feature. Enable by add this line to your AndroidManifest.xml in <application>
android:hardwareAccelerated="true"

I encountered exactly the same behaviour and it seems that few people are having the same issue. At first I thought it is caused by jQuery mobile library. Later on, I manage to find where the problem came from and it is a bug in my own code.
I made a demo to explain the issue.
http://jsfiddle.net/pengyanb/6zvpgd4p/10/
Hopefully, this can be hint for people having the same problem.
$(document).on('pagebeforeshow', '#page2', function(){
console.log('Page2 before show');
var htmlGeneratedOnTheFly = '<ul data-role="listview" data-inset="true">';
for(var i=0; i<4; i++)
{
htmlGeneratedOnTheFly += '<li><a>Random html element</a></li><li data-role="list-divider"></li>';
}
htmlGeneratedOnTheFly += '</div>';
$('#page2UiContent').empty();
$('#page2UiContent').append(htmlGeneratedOnTheFly);
$('#page2UiContent').trigger('create');
//////////////////////////////////////////////////
//The following section is where the bug is generated.
//Each on "page2 before show event" will add a OK Button click handler.
//The handlers never get cleared.
//More and more handler is added to the Page2 OK button as pages going back and forth.
//Open the browser's console window to see multiple "Page 2 OK Button clicked!!!" lines on one button click.
//To fix the bug, move the following section out of the $(document).on('pagebeforeshow', '#page2', function(){});
//////////////////////////////////////////////////
$('#page2OkButton').click(function(){
console.log("Page 2 OK Button clicked!!!");
$.mobile.changePage('#page1', {transition:"flip"});
});
//////////////////////////////////////////////
//////////////////////////////////////////////
});
<link href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.css" rel="stylesheet"/>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div data-role="page" id="page1" data-theme="a">
<div data-role="header" data-position="fixed">
<h5>Demo Page 1</h5>
</div>
<div data-role="main" class="ui-content">
<h2>jQuery mobile changepage jumps back to old page demo</h2>
<p>Click "Go To Page 2" button to go to page2</p>
<p>On Page2 click Ok Button to come back to page1</p>
<p>Keeping going back forth between two pages for few times.</p>
<p>Eventually, you will find that clicked on "Go To Page2" button to flip to Page2 but it soon jumps back to page1 automatically. </p>
<h2>Please read the comments in the javascript for explaination</h2>
Go To Page 2
</div>
</div>
<div data-role="page" id="page2" data-theme="a">
<div data-role="header" data-position="fixed">
<h5>Demo Page 2</h5>
</div>
<div id="page2UiContent" data-role="main" class="ui-content">
</div>
<div data-role="footer" data-position="fixed" style="text-align:center;">
<div data-role="navbar">
<ul>
<li><a id="page2OkButton" class="ui-btn ui-icon-check ui-btn-icon-left">OK</a></li>
</ul>
</div>
</div>
</div>

Related

Making first mobile Safari tap a 'clickable action'

I have seen variations on the theme, but no clear answer. Basically I want an AngularJS Directive that registers a click and inserts extra content into a page, and then scrolls down a bit to make the new content visible. Here is the HTML
<li class="btn btn-default" ng-show="resto.link !== 'none'">
<p scroll-down onclick="void(0)">
Full review
<span class="glyphicon glyphicon-chevron-right right"></span>
</p>
</li>
</ul>
<div class="row">
<div class="col-xs-12" ng-bind-html="fullReview">
</div>
</div>
The onlick part is an Apple suggestion, that seems not to work.
And then I have:
.directive 'scrollDown', () ->
restrict: 'A'
link: (scope, $elm, attrs) ->
$elm.on 'click', (e) ->
e.preventDefault()
scope.getFullReview () ->
console.log "Scrolling'
$("body").animate
scrollTop: $elm.offset().top - 100
, "slow"
getFullReview() updates the model after an AJAX call, and then runs the callback.
This works fine in Chrome but not on the iOS simulator - basically a first tap makes the page move (but without triggering the console log - I think this is the URL bar regrowing) and a second is needed to trigger the Directive's link function. I have also installed fastclick as that was mentioned in some posts, but it did not help.
Need ideas :-) (Even some code that would tell me what event is being triggered by safari)
OK, Wow, I do not understand why, but the html above was the last markup on the page, so all of this was happening at the bottom of the screen. When I added some extra margin to the <ul> element, everything started working perfectly!! Bug in Safari?

Binding lost (after click) in angular js small example

I have a very small application in Angular JS. It's placed inside a bigger rails application, but I don't see too much interaction. The angular application, allows the user to interact with a group of categories. As easy as:
var angular_app = angular.module('angular_app', []);
angular_app.config(['$httpProvider', function($httpProvider, $cookieStore) {
//Protection
}]);
angular_app.controller('CategoriesController', function ($scope, $http) {
$scope.isEditing = false;
$scope.categoryName = '';
$http.get('/api/categories').success(function(data) {
//We use this to data-bind with the HTML placed below
$scope.categories = data;
});
$scope.addNewCategory = function() {
...
}
$scope.editCategory = function(index) {
if (!index)
return;
var selectedCategory = $scope.categories[index];
// With ng-show, we make visible the part of the UI
// that should be used for editing
$scope.isEditing = true;
}
$scope.cancelEditCategory = function() {
$scope.isEditing = false;
}
$scope.deleteCategory = function(index) {
...
}
});
angular.element(document).ready(function() {
angular.bootstrap(document, ['angular_app']);
});
The idea is that the information is shown in a list, and we have an 'edit' button that allows the user to see other part of the UI that will let him perform changes.
<div ng-controller="CategoriesController">
<div ng-show='isEditing' class="popup_menu">
DIV FOR EDITING
</div>
<ul>
<li ng-repeat="category in categories">
<a href="#" ng-click='deleteCategory($index)'>[X]</a>
<a href="#" ng-click='editCategory($index)'>[E]</a>{{ category.name }}
</li>
</ul>
<input type="text" id="categoryTextBox" ng-model="categoryName"/>
<button id="submit" ng-click='addNewCategory()'>New category</button>
</div>
When I'm clicking the edit button, the corresponding part of the UI gets visible, but just after that, something happens, and the ul that should render the list, looses completely the binding, just showing something like:
[X] [E]{{ category.name }}
When it must be showing:
[X] [E]computer science
[X] [E]politics
[X] [E]news
(Which is what I have in the scope). It happens a few after the click (and works for a sec). No errors on the console, no interactions with other libraries (as far as I can see).
Thanks!
Turbolinks
I have no experience with Angular, but perhaps your problem could be to do with Turbolinks - this is a way of Rails loading the <body> tag of a page only - keeping the <head> intact.
Turbolinks is notorious for Javascript on Rails, as each time you reload your <body> without reloading the <head> part of your page, all your JS bindings are going to disappear. The solution to this, in normal JS, is to use JQuery / Javascript delegation, and delegate from the document object:
$(document).on("action", "delegated_object", function(){
...
});
Apologies if this does not work - it's a common issue for us, but as I have no experience with Angular, I don't know if it's going to help you or not.
It seems that I should have been more careful with the links:
<a href="#" ng-click='deleteCategory($index)'>[X]</a>
<a href="#" ng-click='editCategory($index)'>[E]</a>{{ category.name }}
Don't know exactly how this works, but seems that if the link has his href attribute, a GET request is made against 127.0.0.1, breaking in some way the angular code. If you put them like:
<a ng-click='deleteCategory($index)'>[X]</a>
<a ng-click='editCategory($index)'>[E]</a>{{ category.name }}
The problem will be solved. Thanks all for reading and helping!

Using data-direction with dynamically loaded pages

I'm trying to load dynamic pages with jQuery, following this example.
(EDIT): Updated the code to provide a better view of the context
Here is a code sample
<div data-role="page" id="pageSample">
<div data-role="header">title</div>
<div data-role="content">
<a href="#home" data-role="button" data-transition="slide" data-direction='reverse'>Home</a>
</div>
<div data-role="footer">Footer</div>
</div>
$(document).bind( "pagebeforechange", function( e, data ) {
// Generate dynamic content of targeted pages...
$.mobile.changePage($(page), {
transition:"slide",
dataUrl:url,
reverse:reverse
});
});
The back button may be dynamically generated or not (such as this snippet). In both cases, it does not work reverse, as changePage is triggered through pagebeforechange.
Therefore, I inserted a reverse variable in changePage() options.
I can't find a way to retrieve the data-direction value of the clicked item.
I tried this just before changePage():
reverse = false;
$('a[data-direction="reverse"]').on("click", function(){
reverse = true;
});
But the reverse value is not updated in changePage(). I guess both codes run synchronously.
Is there a way to update the reverse value in changePage() ?
Final update
As per our discussion and your example http://jsfiddle.net/Iris/UZBhx/21/
Change this
$('a').on("click", function()
to
$(document).on("click", 'a', function()
Another update
Binding the $.mobile.changePage to pagebeforechange triggers all your code twice. Thus you lose the value of reverse or it gets neglected when the command executes the first time.
Try binding it to pagebeforehide as below.
$(document).bind( 'pagebeforehide', '[data-role="page"]#PageId', function( e, data ) {
// Generate dynamic content of targeted pages...
$.mobile.changePage($(page), {
transition:"slide",
dataUrl:url,
reverse:reverse
});
});
Update
To use reverse effect on specific buttons, you can follow this method.
First, assign a class for buttons with reverse effect, e.g. ui-reverse and add the below script.
$(document).on('click', '[data-role='button'].ui-reverse', function() {
$.mobile.changePage( url, {
transition:"slide",
reverse: true,
dataUrl:url
});
});
"Back" button links - Jquery Mobile
data-direction="reverse"
Is meant to simply run the backwards version of the transition that will run on that page change, while data-rel="back" makes the link functionally equivalent to the browser's back button and all the standard back button logic applies.
data-rel="back"
This will mimic the back button, going back one history entry and ignoring the anchor's default href.
Adding data-direction="reverse" to a link with data-rel="back" will not reverse the reversed page transition and produce the "normal" version of the transition.
In your case, you want to reverse transition, use the below code.
$.mobile.changePage($(page), {
transition:"slide",
reverse: true, // this will reverse the affect of the transition used in the page.
dataUrl:url
});
Read more about it here.

How do you remove a button's active state with jQuery Mobile?

In my mobile app, using jQuery Mobile...
I would like to make a simple button execute a simple javascript function on click. No page transitions, nothing special like that.
I understood I can eliminate the page transitions by doing return false or preventDefault()
But the problem is the button sticks with the "active" state, i.e. highlighted blue if you use the general theme. I'm wondering how I can remove that after click (or tap, etc).
Thanks.
You can disable the 'highlighted blue'-state in the 'mobileinit'-event before loading jQueryMobile-script:
<head>
<script>
$(document).bind('mobileinit', function () {
$.mobile.activeBtnClass = 'unused';
});
</script>
<script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
</head>
Now, when you click on a link, no class will be added after the click is performed. You will still have the 'hoover' and 'down' classes.
Update:
This question and the hacks suggested are now a bit outdated. jQuery mobile handles buttons quite a bit differently than 3 years ago and also, jQuery mobile now has several different definitions of "button". If you want to do what the OP was looking for, you might now be able to avoid the issue by using this:
Step 1:
<button class="ui-btn myButton">Button</button>
Alternatively, you could also use jQuery mobile input buttons:
<form>
<input value="Button One" type="button" class="myButton">
<input value="Button Two" type="button" class="myButton2">
</form>
Step 2:
Then your standard jquery on callback:
$(".myButton").on("tap", function(e) {
// do your thing
});
If you are using a button or a tab, or whatever, that has the "active" class applied to it (the default is ui-btn-active), the old answer may still be useful to someone. Also, here is a fiddle demonstrating the code below.
Selectively removing active state:
As demonstrated in another answer, you can disable the active state for all buttons on all pages. If that is acceptable for the project in question, that is the appropriate (and simpler) solution. However, if you want to disable the active state for some buttons while preserving active states for others, you can use this method.
Step 1:
$(document).bind('mobileinit', function() {
$(document).on('tap', function(e) {
$('.activeOnce').removeClass($.mobile.activeBtnClass);
});
});
Step 2:
Then add the activeOnce class (or whatever you want to call it - it's a custom class) to the buttons that you don't want to highlight when clicking.
And as is usual when binding anything to mobileinit, be sure you place your bindings - and perhaps better, all your javascript code - below the jQuery script and above the jQuery-mobile script.
<script src="js/jquery.js"></script>
<script src="js/my_script.js"></script>
<script src="js/jquery.mobile.js"></script>
Do NOT set the activeBtnClass to '' as suggested, this will cause errors when closing dialogs and the pageLoading function.
The method described does work, but cannot be set to null, the activeBtnClass variable is used as a selector, so set it to a non-existent class to get the same effect without the error.
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script type="text/javascript">
$(document).bind('mobileinit', function () {
$.mobile.activeBtnClass = 'aBtnSelector';
});
</script>
<script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
</head>
This works well to remove the highlight from the buttons while keeping the active state on other elements.
you can just do it via css instead of java:
eg: (you get the idea)
#cart #item_options .ui-btn-active, #cart #item_options .ui-btn-hover-d, #cart #item_options .ui-btn-up-d, #cart #item_options .ui-link-inherit{
background:inherit;
color:inherit;
text-shadow:inherit;
}
What I do is force the buttons to revert to inactive state before a page changes.
//force menu buttons to revert to inactive state
$( '.menu-btn' ).on('touchend', function() {
$(this).removeClass("ui-btn-active");
});
If you want to support non touch devices you should add timeout.
$('.btn' ).on('touchend click', function() {
var self = this;
setTimeout(function() {
$(self).removeClass("ui-btn-active");
},
0);
});
I have spent the good part of a day and night finding the answer to this problem mainly occurring on an android running phonegap. Instead of the standard JQM buttons I am using custom images with :active state in CSS. After following the link to the next page, then clicking back, the button would just stay in the :active state. I have tried adding classes and removing classes and various other suggestions and nothing has worked.
So I came up with my own little fix which works a treat and may help anyone else that is sitting here stumped. I simply call this snippet of code on 'pagecontainerchange' using data.toPage[0].id to only call it on the page where the active state stuck is occurring. Just make sure to wrap your buttons in a div, in my case called "themenu".
function ResetMenu() {
var menuHtml = $("#themenu").html();
$("#themenu").empty().html(menuHtml).trigger("create");
}
This works for a button in the JqueryMobile headerTab
<style>
.Foo {
color: #FFF !important;
background: #347b68 !important;
}
</style>
<div id="headerTab" data-id="headerTab" data-role="navbar">
<ul id="header_tabs">
<li>name
</li>
</ul>
</div>

jQuery Mobile - back button

I am developing the application using jQuery Mobile 4.1.
In my app, I have two html pages like login.html and home.html. In the home.html have 3 pages. () like menupage, searchpage, resultpage.
The project flow is login.html ---> home.html. In home.html, menupage is displayed as a first page. If I choose the some option in the menupage it will move to searchpage and then resultpage. consider, currently I am in the resultpage. If I press the back button on the mobile browsers (iPhone-safari, Android-chrome) then it moves to the login.html.
But I want to display the searchPage. How to solve this one? is it possible to do this?
[Note : The pages should be in the single html page(home.html).
use the attribute data-rel="back" on the anchor tag instead of the hash navigation, this will take you to the previous page
Look at back linking: Here
Newer versions of JQuery mobile API (I guess its newer than 1.5) require adding 'back' button explicitly in header or bottom of each page.
So, try adding this in your page div tags:
data-add-back-btn="true"
data-back-btn-text="Back"
Example:
<div data-role="page" id="page2" data-add-back-btn="true" data-back-btn-text="Back">
try
$(document).ready(function(){
$('mybutton').click(function(){
parent.history.back();
return false;
});
});
or
$(document).ready(function(){
$('mybutton').click(function(){
parent.history.back();
});
});
You can try this script in the header of HTML code:
<script>
$.extend( $.mobile , {
ajaxEnabled: false,
hashListeningEnabled: false
});
</script>
You can use nonHistorySelectors option from jquery mobile where you do not want to track history. You can find the detailed documentation here http://jquerymobile.com/demos/1.0a4.1/#docs/api/globalconfig.html
This is for version 1.4.4
<div data-role="header" >
<h1>CHANGE HOUSE ANIMATION</h1>
Back
</div>
try to use li can be more even
<ul>
<li>back</li>
</ul>

Resources