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.
Related
Assume that we know that an element (or a very specific selector) is going to appear on a page. Is it possible to set up beforehand, via JS or jQuery, an event that goes off when the browser gets to that element and parses it? This is NOT content loaded through AJAX but is present in the primary page's source.
The reason for this need is that I'm working with a hosted system that greatly limits where and when I can inject code to fix problems with the page. I can pretty much only place my code at the start and end of what is a really long page. Right now, the page has to load completely before it can inject any desired changes (yuck!). Plus, I cannot make the pages shorter in content.
This is basically the process I would like to happen:
Page begins loading
Listener set up to watch for .specialClass elements
...
.specialClass element gets parsed/added to DOM
Listener triggers function on that element
...
.specialClass element gets parsed/added to DOM
Repeat as before
...
Page finishes rendering
So, is this possible at all? Thanks in advance.
I have a situation where I am working on a large site and what I have been doing is using one main .js file to store all my bound js code that I want to use on elements such as onclick, onchange etc etc.... these are all held within the one onDomReady method.
Now I'm wondering is it such a good idea to have each page have to go over these and "search" for each element to see if it has to bind anything?
..or should I perhaps use more specificity to prevent this such as the main page ID like #page1, #page2 etc OR should I store these in the specifics pages header (I don't really want to do that as I prefer to keep it all in one place).
Just trying to optimize things and get rid of unnecessary overhead! :)
If I understand correctly, you have one js file with all your event handlers.
This file is included i many pages.
So for example, if there are 100 event handlers in the file, each page may be using only 10 of these.
If thats the case, then its not efficinet, because you have lots of
document.getElementBy... that are not fnding the elements, because they belong to a different page, or worse, finding elements with same selector on multile pages that should not be binded to handlers on a specific page.
also, you are adding script to a page that it does not need.
Best to give each page only what it needs, be it in external js or if very little script, in doucment head.
js that you share across pages should be code that you intend to re-use often.
EDIT:
In response to comment:
regarding reducing http requests, you mean the one file will be in cache, for other pages to use? fair enough, that counts as a benefit. Though there are tradeoffs, such as increased memory usage due to javascript that you dont need in page.
using more specific selector will reduce the risk of attaching event handler to wrong element in a page that you did not mean to target, but there is a safer option:
If you insit on sharing one event handler file across pages,
Group them by wrapping them a function, one for each page. call that function from the page.
This way, you dont have to execute a bunch of code that you dont need, and don't risk adding wrong event handlers to simmilar elements accross pages.
Is there anyway to listen for elements adding themselves to the DOM after load? Currently I have a bunch of tables using DataTables and I have a bunch of other elements that I would like to manipulate when and if the type of element I am looking for is found to have "appeared" on the page. Seeing as what I am attempting to do only works on elements currently on the page when loaded but not after they load such is the case with many elements currently in play.
Ultimately I am currently looking for a means of avoiding going back through the entire code base just to make something happen only if something is found on the page, but after its loaded.
There's no "element added to dom" sort of event that's cross-browser compatible, and the ones that exist for specific browsers are generally discouraged as they tend to cause more issues than they solve.
Usually the issue with elements not existing in the DOM is that they miss event-binding. jQuery has awesome event-delegation that can be used to bind events to a container so that when an event bubbles up to the container, the callback is fired in the context of the element that received the event.
For example, you could bind events using:
$('a').click(doStuff);
but that would not work for any new links, instead you could use something like:
$('#some-container').on('click', 'a', doStuff);
which would intercept all click events on all links in the #some-container element, regardless of when they were added to the page.
programs! I've found solutions to similar problems such as mine here on the site but what's happening to me is rather unusual and I don't think they apply.
On this page: http://tdg.gswitchhost.com/calendar/
I'm locked into using this plugin which I really don't care for. This is a Wordpress site but the plugin, which lists upcoming events, doesn't behave like Wordpress. It has this system in place which is entirely unique to it. Posts live outside of the posts database table and you have to query these in an entirely different way. It's a bummer. So my problem:
We have some jquery working its magic on the events list to add an accordion effect and this works. However. When you click on the pagination links to load the next set of events, the plugin, instead of linking to page 2 of events, it runs an asynchronous query and loads the next set of events on to the existing page without a page reload. If you click on one of the new events, the accordion no longer works.
What I think is happening is that on click, the plugin removes the entire UL which contains the events and loads a second entirely new one, containing the second record set, with the same class name but since the javascript initialized on the first UL, the one the plugin removed, the new set hasn't been affected since the page didn't reload and run the javascript again.
I've tried using .on() and the Livequery plugin to rerun the javascript when you click the pagination links but there's a delay as the query is running and loading the new UL so I believe that the javascript runs again when you click on the link but because the UL hasn't been loaded already when you click, there's nothing for the jquery to work on.
Sorry this is so long but I just want to be as clear as possible. Am I wrong? This is killing me, I'm running out of time and I really need to get this to work so that no matter which set of events has been loaded on the page, the accordion function works on it.
Here's the javascript that initializes the accordion:
$('.eventListingNew').accordion({
headerClassName: 'accordionHeader',
headerActiveClassName: 'accordionHeader-active',
contentClassName: 'accordionContent',
collapseAll: false,
speed: 250
});
And here's a pastebin of the entire accordion function since it's so long. http://pastebin.com/BvDseg3g
Easy thing is just call it when the Ajax complete is done running to reinitialize it.
$(document).ajaxComplete(function(event, xhr, settings) {
$('.eventListingNew').accordion({
headerClassName: 'accordionHeader',
headerActiveClassName: 'accordionHeader-active',
contentClassName: 'accordionContent',
collapseAll: false,
speed: 250
});
});
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");
});