I'm used to the older jQuery Mobile interface where I could write:
$("#page").on("pageLoad", function(){
//Do something
});
With the newer pageContainer widget and it's events, I do not know to do detect what page was loaded.
For example, I need to know how to detect that the #settings page was just loaded. So far I can figure out
$("body").on( "pagecontainerload", function( event, ui ){});
but I have no way of knowing what page was loaded. I've tried using the returned event and ui values but without success.
It seems part of my problem is coming from improper use of
$("body").pagecontainer("load", "welcome.html");
I use that in the beginning of my js file to load them all into the DOM. However, when I navigate to that page, then away from it again it is removed from the DOM. For example, I load welcome.html, settings.html, and devices.html with the above code. Then, I have links like
Settings
And when I use that link to go to the settings page, then another of the same kind of link to go to the devices page, the settings link no longer works. Upon inspecting the DOM, #settings has been removed. Infact, so has #welcome. As soon as I navigate away from that page it is removed from the DOM. So either I have done something wrong, or my understanding of the pagecontainer widget is flawed.
Update
Based on your updated OP, external pages are removed once you navigate away from them, this is the default behavior of jQM. If you want to keep those pages, you need to add data-dom-cache="true" to page div of each external page.
You can retrieve page loaded from ui object emitted on pagecontainerload.
$(document).on("pagecontainerload", function (e, ui) {
var loadedPage = $(ui.page),
pageID = loadedPage[0].id;
if (pageID == "settings") {
/* code */
}
});
Note that pagecontainerbeforeload, pagecontainerload and pagecontainerfail, are only emitted on pages loaded externaly. Moreover, they will fire everytime an external page is loaded, unless DOM cache is enabled. Read more about those events here.
Demo
Related
I need something to detect changes on the url but the page doesn't do post back, it changes dynamically adding just the div inside the html, so the URL page is something like this
http://www.examplepage/conversations/44455
and when I click on another section of the page it it is
http://www.examplepage/conversations/44874
it changes not only at the end but like this
http://www.examplepage/settings
without doing post back and reloading the javascript so my question is, is there a way to detect those changes? and event listener but how?
I search and everyone says hash event but I don't have any value after any hash so it doesn't work
EDIT
Just for the record I have no code of the page nor I have access, I'll explain better, I am doing a background google extension and I just added a slide out to an existing page, this page changes its url the way I explained above. the url changes like every page does, but they change the div inside the html so that the page doesn't have to charge everything again
You need to store the URL when the page loads as a starting point and setInterval to check for changes and modify based on that.
The following code does this check twice a second (500ms):
// store url on load
let currentPage = location.href;
// listen for changes
setInterval(function()
{
if (currentPage != location.href)
{
// page has changed, set new page as 'current'
currentPage = location.href;
// do your thing..
}
}, 500);
There is no "clean", event-based way to detect such URL changes from a content script.
They are done with history.pushState API - and using that API doesn't emit any DOM event.
Two possible indirect event-based approaches, besides the already mentioned poll-based one:
An extension can override history.pushState with an injected script to additionally emit a DOM event that can be listened to in a content script.
This approach is described in detail here.
The downside is that, depending on the code of the page in question, the injected script may need to be injected early, needing run_at: document_start which is suboptimal for page load performance.
Use a background page that listens to chrome.webNavigation.onHistoryStateUpdated event.
If you need to detect this in a background page — perfect,
you're done, without ever needing a content script.
If you need to detect this in a content script, you can use details.tabId in the event listener to send a message to the right content script.
JQuery Mobile - Run load () html and navigate () immediately afterwards.
I'm implementing a small application using jQuery Mobile where I have a login screen that gives access to a home with menus that lead to a few pages. My question is how can I dynamically load the pages only when I click on the menu.
I tried to do something like
$('#home').load('home.html');
$.mobile.navigate("#home");
but despite the effect of the load carrying navigate does not work, or does not redirect to the page.
Can anyone give me a hint how to do this?
You just need to use a callback. Because load is asynchronous, you actually end up calling $.mobile.navigate before load has finished. By sending the next step as a callback, you ensure it happens after you have completed the first step.
From jQuery Documentation:
If a "complete" callback is provided, it is executed after
post-processing and HTML insertion has been performed. The callback is
fired once for each element in the jQuery collection, and this is set
to each DOM element in turn.
So your code would look like:
$( "#home" ).load( "home.html", function() {
$.mobile.navigate("#home");
});
I have a mobile single-page web application that is built using jquery-mobile (jqm) and knockout. The application itself has multiple pages but they are all contained within a single HTML document.
Problem: after changing my "create view model for page" from sync to async behavior, I have the problem that jquery-mobile fires its events before the data is ready.
Background: up until recently I had been working with sample data, basically a huge JSON blob, and everything worked smoothly. With the new async composition of view models from various sources, data is not ready immediately and my "buildViewModel" method takes a continuation callback instead of just synchronously returning data.
I'm subscribing to the pagebeforecreate and pagebeforechange events, and fire off the code to populate the viewmodel here. The problem is that after returning from the event handler, jqm triggers the remaining chain of events before the data is available. This causes a page transition to an unprepared page, which is undesirable.
I have tried to call event.preventDefault in all of the before-events and manually calling $.mobile.changePage once the page is ready to be a) enhanced and b) the page transition to occur, but without any luck.
I've scanned the jquery-mobile source, but couldn't spot anything that looked like it would allow me to delay the pagebeforeshow event, which is essentially what I need in order to be able to render the page properly.
How can I ensure that 1) data is available and 2) knockout has been applied to perform initial DOM manipulations, before jquery-mobile attempts to enhance the page and before it executes the in-transition to the page?
I also considered using synchronous ajax to fetch resources, but this will (I think) not work for resources loaded from the device (using PhoneGap/Cordova), and has other negative consequences that I'd like to avoid.
FWIW, I'd like to avoid having to manually handle all navigation events by wiring up click-handlers everywhere, but I'm open to all solutions if need be.
Apologies if this is a duplicate; I've searched and read a ton of questions, but not found an answer or question that was quite the same. It just sounds incredible that I would be the first to hit this problem, as I imagine it is a common scenario..
Update: clarified problem scenario description.
I had this exact same problem.
The only solution I've been able to come up with is to write a custom transition handler that defers starting the transition until the Ajax request completes.
Here's a fiddle showing the technique. The fiddle doesn't use Knockout, but does show how to defer the transition.
Basically, since $.ajax() returns a promise, I can pipe that into the promise returned by the default transition handler and return it from my new handler.
In my pagebeforeshow handler, I attach the Ajax promise to the page so that the transition handler has access to it. Not sure if this is the best way, but I liked it better than using a global variable.
The only thing I didn't like about this is that it delays the start of the transition until the Ajax response arrives so it could feel like the page has "hung" to the user making them click again. Manually showing the loading message makes it feel a bit more responsive.
Hope this helps and please let me know if you find a better solution!
Delaying the transition to a new page until its content is ready is a very common issue when facing dynamic content in jQuery Mobile. The most convenient ways to address this are:
Instead of classic href type navigation, base the links on "click" actions that will first retrieve the content, build a new page in the DOM, and then initiate a transition to this new page through $.mobile.changePage. The advantage of this approach is that it is easy to put in place, the disadvantage is that you do not navigate with classic href links
Bind the pagebeforechange event at the document level to detect if upon navigation the target page is one of your page that should contain dynamic content. In such a case, you can prevent default navigation from happening, take your time to generate the page, and transition upon success. This is described in the JQM docs on dynamically injected content. The advantage is that you can still rely on standard href links navigation, but it requires a bit more code and design upstream to properly detect and act upon navigation to the pages.
$(document).on( "pagebeforechange", function( e, data ) {
if ( typeof data.toPage === "string" ) {
if ( data.toPage === "myDynamicPageName" ) {
e.preventDefault(); //used to stop transition to the page (for now)
/*
Here you can make your ajax call
In you callback, once you have generated the page you can call
$.mobile.changePage
(you can pass the Div of the new page instead of its name as
the changepage parameter to avoid interrupting again the page change)
*/
}
}
});
Set your link to call a "load" function instead of doing a page transition. In your load function, display the "loading message" and make the JSON call. Finally, in the JSON callback function, change page to page2
The load function:
function loadPage2() {
/* show wait page */
$.mobile.loading( 'show', {
text: 'Loading massively huge dataset',
textVisible: true
});
/* perform JSON call then call callback */
}
Callback function
function callback() {
$.mobile.changePage("#page2");
}
Here is a working JSFiddle: http://jsfiddle.net/8w7PM/
Note that if you don't want users to be able to update input fields in Page 1 while waiting, introduce a "wait page" between page 1 and page 2, with the init of "wait page" doing the same as "loadPage2".
I think you have to fire again for all widget which you want to bind the data from response to
For example, you will have to invoke trigger with create or refrestevent for the element
$("#element").trigger('create');
JQuery Mobile will bind all default events to the element as it is
--- EDIT ---
I just created a sample code, I think it's same your issue, please try the link http://jsfiddle.net/ndkhoiits/BneqW/embedded/result/
Before rending the data, we have to invoke to service to get them for displaying, that why all events binded by jqm will be removed then.
I have a workaround for this, don't make jqm fire anything on the element, we'll trigger it after all data is binded by knockoutjs
Let try the fixed version
http://jsfiddle.net/ndkhoiits/c5a2b/embedded/result/
This is the code http://jsfiddle.net/ndkhoiits/c5a2b/
I have a small jQuery Mobile / KnockoutJS application and have struggled with the very same issue. My app contains about 5 pages. All are contained in a single physical HTML document with the standard <div data-role="page"> markup separating individual pages.
I finally went with click based navigation and fire $.mobile.changePage() as the result of $.ajax success.
One of the downsides to this technique is that you will lose your button highlighting when relying on onclick vs href attributes. See my related post: href vs scripted page transitions and button highlighting
I later chose to supply both and rely on the href to perform the navigation while using onclick to invoke my JavaScript logic to load ViewModels etc. The only place that I have found this to be an issue is when there is possible validation required on the source page. If it fails, the transition has already started and the UI then flashes back to the source page. Ugly but this only happens in limited instance within my app.
I don't think any of this is specific to Knockout. My exact solution may present issues for you in that your navigation is likely to complete before your model is fully loaded but if you rely on $.mobile.changePage(), it should all work and hide your page until after it is loaded. The transitions should work fine.
<a href="#MyNewPage" data-bind="click:LoadNewPage" data-role="button">
Load Page
</a>
$.ajax({
url: url,
cache: false,
dataType: "json",
data: requestData,
type: "POST",
async: true,
timeout: 10000,
success: function (data, textStatus, jqXHR) {
// use either href or changePage but not both
$.mobile.changePage("#NewPage");
},
error: function (jqXHR, textStatus, errorThrown) {
alert("AJAX Error. Status: " + textStatus);
// jqXHR.status
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
}
});
You should place the code for the page transition in a success function on the AJAX call.
$.ajax({
url:"request-url",
data: data,
type: "POST",
success: function(response){
// Add Transition Code Here
}
});
I am creating a jQuery Mobile web app, which loads some pages.
For example, a.html is my main page. It may call b1.html,b2.html,...,b100.html (User may click on one of buttons). (The pages are loading with ajax navigation feature of jQuery Mobile)
And there is some events in each b[i].html page, and ids and many things are same in every b[i].html page. But the point is, at any time, just one of them may be in DOM. So there will be no id conflicts or such.
The problem
The problem is the conflict of the events. When user come back to a.html from b[i].html, the HTML will be removed, but events will remain. This will cause many problems if I first go to b[i].html, and then come back to a.html and then go to b[j].html. I mean, b[j].html will not work correctly... :(
What I have tried
I have putted this in a.html, to remove all events:
$("#mainpage").off("pagebeforeshow").on("pagebeforeshow",function() {
$("*").not("#mainpage").off();
//Other initialization codes...
});
But, problem not solved...
(mainpage is the id of data-role="page" of a.html)
Example
For example, I have this in each b[i].html:
$(window).resize(function () {
alert("Resized");
});
At the beginning (in a.html), If I resize the window, there will be no alerts, but after visiting b[i].html and then coming back to a.html, I'll see alerts if I resize the window, even with that line of code (What I have tried part.)...
So, How to remove those event handlers when users come back to a.html from b[i].html?
If you are using jQuery Mobile, more than one of said pages may exist in the dom at the same time, resulting in non-unique id conflicts.
I would ditch putting js on the individual pages and have it done from the primary page, or through a script loading system such as require.js. Then do all of the events through delegation from the document. Obviously that won't work with window.resize(), but it doesn't need to be delegated anyway.
"Can you please explain more?"
Basically, if you are including scripts on the child pages, you will need to have both setup and teardown for every page. setup adds the events, and teardown removes them. If you instead used a single global script that adds ALL of the events using event delegation from the document, all of the pages should work. Obviously that global script could get pretty big on a complex site, so you could instead use require.js to load in js that does the same thing as needed, preventing it from loading the same dependency more than once.
As far as removing all events, I've never tried this, but can you use $("*").off()? According to the docs it should work. I'm not sure how it will affect jQuery mobile. To remove events on the document and/or window, you will have to do it manually because $("*") will not select them.
$(document).on("vmousemove","#link",func) is how you delegate an event from the document.
I am trying to hide all the label tags on my jQuery Mobile site in an accessibility friendly way. To this end, I am using javascript to apply the class ui-hidden-accessible to every label tag on my site per documentation (http://jquerymobile.com/test/docs/forms/docs-forms.html).
However, my javascript is not working.
Here is a Fiddle demonstrating how the label tag still appears.
http://jsfiddle.net/tW4Xu/
Why is it not working? I have also scrutinized other jQM event handlers such as pageinit and pagecreate:
http://jquerymobile.com/test/docs/api/events.html
My javascript to hide label tags:
// done after page is loaded
$(document).on("pageshow", "label", function(event) {
$(this).addClass("ui-hidden-accessible");
});
It seems like you have a few things going wrong here, although I'm not sure how much of it is coming from the jsfiddle summary and how much is in your full code.
The first thing to note is that 'pageshow' is a page transition event. It seems like you might want to use 'pageinit' instead. Here's how the jQM docs describe it:
Triggered on the page being initialized, after initialization occurs. We recommend binding to this event instead of DOM ready() because this will work regardless of whether the page is loaded directly or if the content is pulled into another page as part of the Ajax navigation system.
$( '#aboutPage' ).live( 'pageinit',function(event){
alert( 'This page was just enhanced by jQuery Mobile!' );
});
Note also here that 1) the event is being bound with live() instead of on() (no idea if there's a difference), and 2) it is being attached to a specific id for a jQM 'page'. This is part of what is missing in your jsfiddle example. There aren't any named jQM pages. jQM kind of messes up the whole idea of a page being ready, since everything is in one html file and then gets chunked out using ids and inserted via AJAX.
And so finally: Even though jQM says not to, if your goal is to add this class to every single label on every single jQM page, I would use good-old $(document).ready() and then use $.each() to change them all in one go. Again, from the jQM docs:
However, in jQuery Mobile, Ajax is used to load the contents of each page into the DOM as you navigate, and the DOM ready handler only executes for the first page. To execute code whenever a new page is loaded and created, you can bind to the pageinit event.
So there isn't anything evil about $.ready(), it's just that this event is only fired once so subsequent page transitions won't trigger it. But that could be exactly what you want in the first place.
This code works on jsfiddle:
$(document).ready( function(event) {
$("label").each( function(index, element) {
element.addClass("ui-hidden-accessible");
});
});
If in your real site you notice that page transitions cause the labels to come back, then you'll want to bind to something else, again probably 'pageinit'.
Hope this helps! Apologies for the verbosity...I kind of got going there huh?
http://jsfiddle.net/tW4Xu/2/
That? Not sure what your specific requirement is for using on('pageshow'), in my fiddle I used
$(function() {
$('label').addClass("ui-hidden-accessible");
});
Don't use live its deprecated as of jquery 1.7. You had the right idea just do it before pageshow and make sure you use the page id. Also in your fiddle the top drop down menu change from onload to no wrap(head). I have had issues with that in the past.
$(document).on("pageinit", "#thepageid", function(event) {
$('label').addClass("ui-hidden-accessible");
});
This will work for all your JQM pages.
$(document).on("pageinit", "[data-role=page]", function(event) {
$('label').addClass("ui-hidden-accessible");
});