Swiping between html pages for Android app dev - jquery-mobile

I know this has been asked before but I can't get any of he examples to work.
Getting the slide transition to work where you have all the pages as separate html files seems very difficult to do? How does the next/prev part of the script know which of the other files is next?
For example, index.html should slide to 01_welcome.html - but how does it know that it's not 02_funds.html?
Thanks for any enlightenment you can give. Below is the script ( courtesy of a previous answer) I've been trying to implement.
$('div.ui-page').live("swipeleft", function () {
var nextpage = $(this).next('div[data-role="page"]');
if (nextpage.length > 0) {
$.mobile.changePage(nextpage, "slide", false, true);
}
});
$('div.ui-page').live("swiperight", function () {
var prevpage = $(this).prev('div[data-role="page"]');
if (prevpage.length > 0) {
$.mobile.changePage(prevpage, {
transition: "slide",
reverse: true
}, true, true);
}
});

The code in your OP works well in Multi-Page Model environment, since all pages (div's) are present in DOM. For Single Page Model, you will need to tweak the code a bit as each page is an individual file. Another note, .live() is deprecated, use .on() instead.
The simplest solution is to add custom attributes to each page div, e.g.
<div data-role="page" data-next-page="services" data-prev-page="about">
Retrieve the values of the custom attributes on swipe and then load the target page.
$(document).on("swipeleft swiperight", function (event) {
var activePage = $.mobile.pageContainer.pagecontainer("getActivePage"),
nextPage = activePage.data("next-page"),
prevPage = activePage.data("prev-page");
/* move to next page */
if (event.type == "swipeleft" && nextPage) {
$.mobile.pageContainer.pagecontainer("change", nextPage + ".html");
}
/* move to previous page */
if (event.type == "swiperight" && prevPage) {
$.mobile.pageContainer.pagecontainer("change", prevPage + ".html", {
reverse: true
});
}
});

Related

jQuery Mobile: Prevent node being deleted on history back

I have this code
$(function() {
$(window).on("swipeleft", jqmForward).on("swiperight", jqmBack);
});
function jqmBack(e) {
var prevpage = $('div.ui-page-active').prevAll('div[data-role="page"]');
if (prevpage.length > 0)
$.mobile.changePage($(prevpage[0]), { transition: "slide", reverse: true }, true, true);
}
function jqmForward(e) {
var nextpage = $('div.ui-page-active').nextAll('div[data-role="page"]');
if (nextpage.length > 0)
$.mobile.changePage($(nextpage[0]), "slide", false, true);
}
But the forward function never works because apparently jqm deletes the latest div.ui-page when you go back.
Is there a way to keep the div.ui-page to let you go forward after you accidentally swipe right (ie. go back)?
I am using jQuery Mobile 1.4.0
The solution is to have
$(function () {
$.mobile.page.prototype.options.domCache = true;
});
(see Caching pages in DOM). But that rises another problem.

How to call Modal Dialog from Datatables row - seem to have conflict with Jquery UI

I want to create "CRUD" functions by calling a modal form by clicking on a row in Datatables.
I've been at this for hours traversing through each step of my code and it seems I'm getting a conflict between my JQ-UI and Datatables. I found several examples, including the Datatables example for "live" functions, where you can initialize a table and call a simple jquery function.
I'm using:
code.jquery.com/jquery-1.9.1.js
code.jquery.com/ui/1.10.2/jquery-ui.js
../DataTables-1.9.4/media/js/jquery.dataTables.js
This example will give me the cursor, then makes the table "jump" across the page.
Does anyone have a working example or a fiddle I can experiment with?
function openDialog() {
$("#dialog-modal").dialog({
height: 140,
modal: true
});
}
/* Init DataTables */
$('#example').dataTable();
/* Add events */
$('#example tbody tr').on('click', function () {
$('#example tbody tr').css('cursor', 'pointer');
var sTitle;
var nTds = $('td', this);
var sBrowser = $(nTds[1]).text();
var sGrade = $(nTds[4]).text();
/*
if (sGrade == "A")
sTitle = sBrowser + ' will provide a first class (A) level of CSS support.';
else if (sGrade == "C")
sTitle = sBrowser + ' will provide a core (C) level of CSS support.';
else if (sGrade == "X")
sTitle = sBrowser + ' does not provide CSS support or has a broken implementation. Block CSS.';
else
sTitle = sBrowser + ' will provide an undefined level of CSS support.';
*/
openDialog();
//alert( sTitle )
});
A little sleep and another stab at this yielded a solution that at least solves the Datatable Dialog issue, I'll have to assume that any other issues I was having lies the other add-ins that I included. So to me this is solved.
The answer was 99% in this post - thanks to the author for the great working example.
I modified their link solution, combined with Datatables "live" solution example with variables, and was able to successfully pass data to a working dialog that works with pagination as the previous link explains.
This set up would allow me to create JQuery-UI Modal Forms, pass the ID from mySQL table column, and execute the form that's handing the Server Side PHP CRUD functions I needed.
(I can't take credit for any part of this, other than time spent making sure it worked).
The working example is taken straight from Datatables "live events" example, should be easy to drop in if you remove the sAjaxsource and go with a plain Datatable..
$('#example').dataTable( {
"bProcessing": true,
"bServerSide": true,
"bJQueryUI": true,
"bStateSave": true,
"sPaginationType": "full_numbers",
"sAjaxSource": " /*your data source page here*/ "
} );
/* Add events */
$("body").on("click", "#example tbody tr", function (e) {
e.preventDefault();
var nTds = $('td', this);
//example to show any cell data can be gathered, I used to get my ID from the first coumn in my final code
var sBrowser = $(nTds[1]).text();
var sGrade = $(nTds[4]).text();
var dialogText="The info cell I need was in (col2) as:"+sBrowser+" and in (col5) as:"+sGrade+"" ;
var targetUrl = $(this).attr("href");
$('#table-dialog').dialog({
buttons: {
"Delete": function() {
window.location.href = targetUrl;
},
"Cancel": function() {
$(this).dialog("close");
}
}
});
//simple dialog example here
$('#table-dialog').text(dialogText ).dialog("open");
});

jQuery Mobile - Loading External HTML

I'm loading some external html into a div in a jquery mobile app. Everything works fine, however I'm trying to make it a little bit smoother.
Here is my code:
$(document).bind('pagebeforecreate', function (event, ui) {
if (event.target.id == 'pageViewOrder') {
//get the page
$.getJSON(root_url + '/orders/view/' + window.viewOrderReference + '/?callback=?', null, function (d) {
$("#viewOrder_content").html(d.html).trigger("create");
$.mobile.loading('hide');
});
}
What's happening is the page is being displayed prior to the ajax call finishing. Is there a way of halting jquery mobile from proceeding to display the page before this call is finished? At the moment it shows the page then the content pops in.
EDIT: This is loading in single pages
Cheers,
Ben
Halting the display process is easy, you just need to call event.preventDefault().
The problem is then to make sure that you will go with the process once you retrieve your content. What I would actually do is bind to pagechange, check if you have already retrieved the data, if not, then interrupt the process, retrieve the data and start over. If yes, then proceed as planned.
var contentRetrieved = false; //will indicate wether the JSON call has already been executed
var contentToDisplay; //data from the JSON call
$(document).live('pagebeforechange', function (event, data) {
if (( typeof data.toPage === "string" ) && ($.mobile.path.parseUrl(data.toPage).hash == '#pageViewOrder')) {
if (contentRetrieved) {
contentRetrieved = false; //content is already retrieved, we proceed with the pagechange
} else {
event.preventDefault(); //prevent further page change operations
$.getJSON(root_url + '/orders/view/' + window.viewOrderReference + '/?callback=?', null, function (d) {
contentToDisplay = {"html":d.html};
contentRetrieved = true;
$.mobile.changePage("#pageViewOrder");
});
}
}
});
$(document).bind('pagebeforecreate', function (event, ui) {
if (event.target.id == 'pageViewOrder') {
$("#viewOrder_content").html(contentToDisplay.html).trigger("create");
$.mobile.loading('hide');
}
});​

Latency issue with Primefaces overlayPanel - loads to lazy

I am using Primefaces 3.2 with jsf 2 and glassfish 3.1.2.
I have a p:dataTable of users containing avatars of the user. Whenever the user moves the mouse over the avatar a p:overlayPanel appears with more information (lazy loaded) on the user, and disappears when the user moves the cursor away - like this:
<p:overlayPanel for="avatar" dynamic="true" showEvent="mouseover" hideEvent="mouseout" ...>
This works very well - as long as the user is "slowhanded". Whenever an user moves the cursor fast above many avatars many of the overlayPanels stay visible.
For example when the user has the cursor over the position where user avatars are displayed and uses the scroll wheel of his mouse to scroll the usertable down or up.
I believe that the overlaypanel starts to load the information dynamically (dynamic="true") from the server when showEvent="mouseover" is dispatched and displays the overlaypanel after the response from the server arrives.
This way it is not possible to detect whether the cursor is already away when the overlaypanel becomes visible - so the hideEvent="mouseout" is never dispatched.
Is there a way to make the primefaces overlaypanel appear directly on mousover, showing a loading gif and update the content into the overlaypanel when the response comes from the server.
Is this a good appraoch or does anyone know any other way to solve this nasty problem?
Thanks Pete
As my first answer is already very long and contains valid information, I decided to open a new answer presenting my final approach.
Im now using Primefaces inheritance pattern making the code alot cleaner. Also I noticed that replacing/overwriting the whole bindEvents function isnt necessary, as we can remove the old event handlers. Finally this code fixs the latest issue experienced: A hide event before ajax arrival.
PrimeFaces.widget.OverlayPanel = PrimeFaces.widget.OverlayPanel
.extend({
bindEvents : function() {
this._super();
var showEvent = this.cfg.showEvent + '.ui-overlay', hideEvent = this.cfg.hideEvent
+ '.ui-overlay';
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(
showEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.timer = setTimeout(function() {
_self.hidden = false;
_self.show();
}, 300);
}).on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.hidden = true;
_self.hide();
});
},
_show : function() {
if (!this.cfg.dynamic || !this.hidden) {
this._super();
}
}
});
Im sorry for the poor formatting: Eclipses fault ;)
Wow, finally after a long debuging session and testing various approaches i recognized that the problem isnt the ajax request but the event handlers itself:
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
if(_self.isVisible()) {
_self.hide();
}
});
As you can see, the widget is just hidden if its visible before. If your moving your mouse out too fast, now two things can happen:
The widget isnt visible at all
The animation is still going on
In this case the event is discarded and the panel stays visible. As animations are queued, one simply has to remove the if statement to fix the issue. I did this by replacing the whole bindEvents method:
PrimeFaces.widget.OverlayPanel.prototype.bindEvents = function() {
//mark target and descandants of target as a trigger for a primefaces overlay
this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
//show and hide events for target
if(this.cfg.showEvent == this.cfg.hideEvent) {
var event = this.cfg.showEvent;
$(document).off(event, this.targetId).on(event, this.targetId, this, function(e) {
e.data.toggle();
});
}
else {
var showEvent = this.cfg.showEvent + '.ui-overlay',
hideEvent = this.cfg.hideEvent + '.ui-overlay';
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
var _self = e.data;
if(!_self.isVisible()) {
_self.show();
}
})
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
_self.hide();
});
}
//enter key support for mousedown event
this.bindKeyEvents();
var _self = this;
//hide overlay when mousedown is at outside of overlay
$(document.body).bind('mousedown.ui-overlay', function (e) {
if(_self.jq.hasClass('ui-overlay-hidden')) {
return;
}
//do nothing on target mousedown
var target = $(e.target);
if(_self.target.is(target)||_self.target.has(target).length > 0) {
return;
}
//hide overlay if mousedown is on outside
var offset = _self.jq.offset();
if(e.pageX < offset.left ||
e.pageX > offset.left + _self.jq.outerWidth() ||
e.pageY < offset.top ||
e.pageY > offset.top + _self.jq.outerHeight()) {
_self.hide();
}
});
//Hide overlay on resize
var resizeNS = 'resize.' + this.id;
$(window).unbind(resizeNS).bind(resizeNS, function() {
if(_self.jq.hasClass('ui-overlay-visible')) {
_self.hide();
}
});
};
Execute this code on load and the issue should be gone.
As your replacing the js code nevertheless, you can use this oppurtunity to implement quite a nice feature. By using timeouts in the event handlers one can easily implement a little delay not just improving usability (no more thousands of popups appear) but also reducing network traffic:
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
var _self = e.data;
_self.timer = setTimeout( function(){
if(!_self.isVisible()) {
_self.show();
}
}, 300);
})
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.hide();
});
Ofcourse you can use a global variable to control the delay time. If you want a more flexible approach youll have to overwrite the encodeScript method in the OverlayPanelRender to transmit an additional property. You could access it then with _self.cfg.delay. Notice though that youll have to replace the component model OverlayPanel too providing it with an extra attribute.
At the same time I thank you for this brilliant solution I take the opportunity to update it for Primefaces 5.2. In our application the code broke after that upgrade.
Follows the updated code for Primefaces 5.2:
PrimeFaces.widget.OverlayPanel.prototype.bindTargetEvents = function() {
var $this = this;
//mark target and descandants of target as a trigger for a primefaces overlay
this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
//show and hide events for target
if(this.cfg.showEvent === this.cfg.hideEvent) {
var event = this.cfg.showEvent;
this.target.on(event, function(e) {
$this.toggle();
});
}
else {
var showEvent = this.cfg.showEvent + '.ui-overlaypanel',
hideEvent = this.cfg.hideEvent + '.ui-overlaypanel';
this.target
.off(showEvent + ' ' + hideEvent)
.on(showEvent, function(e) {
clearTimeout($this.timer);
$this.timer = setTimeout(function() {
$('.ui-overlaypanel').hide();
$this.hidden = false;
$this.show();
}, 500);
})
.on(hideEvent, function(e) {
clearTimeout($this.timer);
$this.timer = setTimeout(function() {
// don't hide if hovering overlay
if(! $this.jq.is(":hover")) {
$this.hide();
}
}, 100);
});
}
$this.target.off('keydown.ui-overlaypanel keyup.ui-overlaypanel').on('keydown.ui-overlaypanel', function(e) {
var keyCode = $.ui.keyCode, key = e.which;
if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
e.preventDefault();
}
})
.on('keyup.ui-overlaypanel', function(e) {
var keyCode = $.ui.keyCode, key = e.which;
if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
$this.toggle();
e.preventDefault();
}
});
};
I also added an extra feature which allows the user to move the mouse over the overlay without hiding it. It should hide when you move the mouse out of it then which I accomplished through:
<p:overlayPanel .... onShow="onShowOverlayPanel(this)" ...>
function onShowOverlayPanel(ovr) {
ovr.jq.on("mouseleave", function(e) {
ovr.jq.hide();
});
}
Hope you enjoy!
It's been a long time, but in case anyone bumps into this problem, a showDelay attribute was added to the overlayPanel to solve this problem starting from Primefaces 6.2. However, it is not in the official documentation for some reason.

jQuery Mobile + backbone.js: navbar issue

I load pages created from templates dynamically with a function from the Router (as seen on some tutorials):
changePage: function(page) { // page is a View object
$(page.el).attr('data-role', 'page');
page.render();
$('body').append($(page.el));
var transition = $.mobile.defaultPageTransition;
if (this.firstPage) {
transition = 'none';
this.firstPage = false;
}
$.mobile.changePage($(page.el), {changeHash:false, transition: transition});
}
The thing is when pages contain a JQ Mobile navbar, the active item is not highlighted. Actually it is, like 1 ms, then it's not, I feel like it's because the navbar is "reloaded".
When I click 2 times on the same item, it works the second time.
Is there anybody who is able to have working navbars with jQuery Mobile and backbone.js?
I ended up doing that:
var activeTab = null;
$('[data-role=page]').live('pageshow', function (event, ui) {
$.each($('[data-role=navbar] ul li').children(), function (i, val) {
if (typeof activeTab !== "undefined" && activeTab != null && $(val).attr('id') == 'navTab' + activeTab)
$(val).addClass($.mobile.activeBtnClass);
else
$(val).removeClass($.mobile.activeBtnClass);
});
activeTab = null;
});
And for each route that requires an active tab I just do for instance:
r_search: function() { // Search page (form)
activeTab = "Search";
this.changePage(new SearchView());
},

Resources