I have implemented stickyfloat (http://plugins.jquery.com/files/stickyfloat_0.htm) on a site. It works great with one gotcha. The function triggers on $(window).scroll and not on $(window).load. I want it to trigger on either because I am linking to anchor points in the page (http://tre-stage.wdogsystems.com:8000/faq/#does-the-sale-of-my-receivables-have-a-negative-effect-on-my-credit-report) and I would like the side menu to appear when the page loads and not just when I initiate a scroll.
If you look at the page above, it's working just as I want it to. However, this is only because I've repeated the same function with a $(window).load. This seems highly inefficient to me. So, is there a way to chain the two together?
For example:
$(window).scroll || $(window).load (function () ...
jQuery's .bind()help method allows multiple events bound at once.
$(window).bind('scroll load', function() {
// code here triggers for both, scroll & load events
});
just chain in the bind, like:
$(window).bind("scroll load", ...)
however it is very bad idea to attach to scroll event
a very good explanation why and a great solution: http://ejohn.org/blog/learning-from-twitter/
Like this:
$(document).bind('ready load scroll', function() { ... });
Why don't you just trigger a window scroll event on load? You could namespace your scroll event too to isolate it and have better access to it later...
$(window)
.on('scroll.myscroll', function () {
// do something on scroll event
})
.trigger('scroll.myscroll'); // trigger scroll event on pageload
But if you were actually wanting to run it on window load (ensuring the DOM is fully loaded incl. images etc) then the other examples mentioned are fine. But use the .on() method, rather than .bind().
$(window).on('scroll.myscroll load.myload', function () {
// do something on scroll and load events
});
Related
I'm using a Bootstrap Modal dialog, and I have an event handler set up so that once the modal closes, it triggers a couple of other updates on the page. I had been using the .delegate method which works perfectly. After reading that it was deprecated, I tried to move to the .on method, however the handler was not getting triggered. I cannot figure out why. Here are my two code snippets for comparison:
Delegate:
$(document).delegate('#streamingPopup', 'hide.bs.modal', function () { ... });
On:
$('#streamingPopup').on('hide.bs.modal', function () { ... });
No code withing the callback function has changed.
As far as I can tell, I'm using it the way the documentation says it should be used (http://api.jquery.com/on/). I'm assuming it has something to do with the hide.bs.modal event, or with the fact that it's attached directly to the jQuery object rather than the DOM itself, but I can't work out why it would work in one but not the other. Can anyone point me to what I'm doing wrong?
You might need to change the syntax for .on:
$(document).on('hide.bs.modal', '#streamingPopup', function () { ... });
This must work, as it targets a static parent. Replace the document with a static parent of #streamingPopup.
I am using an infinite scroll plugin which uses ajax.
When the 'next page' is loaded via ajax, all other ajax related scripts that are on the next page do not work. I have been told that I have to use 'delegated events'(ie change $(id).click() to $(document).on) - problem is that means editing multiple plugins and changing dozens of function calls.
Is there any way I can avoid changing everything to $(document).on and do something cool with the infinite scroll?????
I'd much rather modify the infinite scroll plugin rather than modifying other ajax related plugins to make them fit.
Unfortunately you have very few options here, and switching to delegated events is by far the best of them.
The problem is that your old code was assigning behaviour to "particular elements" when what it should really have been doing is creating page-wide responses to "certain types of actions".
I see 3 possibilities, and only one of them is guaranteed to work.
Run any scripts that are needed on new pages each time a new page is loaded. The downside here being that unless you are careful about also "tearing down" between content loads you will have behaviours repeating or colliding with each other (eg: double popups, broken animations).
Encapsulate the dynamic areas in <iframe>s. Depending on your architecture this may or may not be possible, and certainly won't be easy to integrate with some kind of infinite scrolling plugin which already expects a certain page structure.
Bite the bullet and fix the crappy code.
Loading scripts inside your ajax loaded content is a bad way to start with anyway. What you need is event delegation to attach itself to any dynamically added elements.
$("body").on("click", ".yourclass", function() {
//This function will run for every element with `yourclass` class you load via ajax
});
If you must keep using .click() then you must have a function you can call on the new content to re-hook the events every time you add more content to the page.
e: though it is worth noting that a change from .click to .on can often be handled by a properly structured find/replace
Event delegation is the correct solution. The issue is that the HTML elements on the "next page" were not part of the DOM when the page loaded. Therefore, if you did something like:
$(function() {
$('#some-element-on-the-next-page').click(function() {
foo();
});
});
Your handler did not bind.
I wouldn't attach the events to $(document). I would attach them to the closest parent which is available when the DOM loads. For example, the body tag or the fixed width wrapper which is the first child of the body (assuming your layout uses this type of structure.)
Make sure that the element that you attach to is not emptied with .empty() or repopulated with .html() as that will break the binding. Attaching the delegated handlers lower down on the DOM tree will give you better performance since the events will not have to bubble all the way up to the document node to fire your methods.
You shouldn't need to rewrite all of your functions and plugins, just the bindings to the events that fire them.
I typically use the module pattern and de-couple my method definitions from the click handlers. All of my methods are defined in the outer closure. I'll have a "document ready" section where I bind user events like clicks.
For example:
var myModule = (function() {
var public = {};
public.foo = function() {
// do something cool here
};
// document ready
$(function () {
$('#site-container').on('click', '.js-foo', function() {
public.foo();
});
});
return public;
})();
If you need to change the bindings in the future you will only need to change the call inside the document ready section.
I'm using the jQuery Mobile option allowSamePageTransition, which enables me to go from
page A > page A > page A ...
I need this to allow browsing through a catalogue of items. My problem is, the items need some form of interaction and I used to attach the interaction binding to document, because it is set before the elements affected are generated.
However, reloading the same page over and over again will re-bind my event handlers every time I reload.
My first idea was to use .off when the page is being hidden, but reloading a page #foo, will trigger pagehide on the same page being shown, so all bindings set on
$(document).on("pagebeforeshow.foo_events", "#foo", function(e) {
// bind when shown
});
will be unbound again by the previous #foo being hidden
$(document).on("pagehide", "#foo", function (e) {
$(this).off(".foo_events");
// removes bindings on #foo being hidden AND shown
});
The only solution I have come up with is plastering the document with classes, which I don't like doing:
priv.setBindings = function (param) {
var doc = $(document);
doc
.filter(function() { return $(this).is(".e_gallery") !== true; })
.on("pagebeforeshow.gallery", param.pageId, function (e) {
doc.addClass(".e_gallery");
// run stuff
});
};
But I'm no fan of attaching classes to the dom.
Question:
Is there a way to prevent multiple event bindings set on $(document) when going to the same page over and over again WITHOUT toggling classes?
Solution 1
Best solution would be to use pageinit to bind events. If you take a look at an official documentation you will find out that pageinit will trigger ONLY once, just like document ready, so there's no way events will be bound again. This is best solution because you don't have processing overhead like when removing events with off method.
Working jsFiddle example: http://jsfiddle.net/Gajotres/AAFH8/
Of course this will fail in case multiple HTML solution is used.
Solution 2
Remove event before you bind it:
$(document).on('pagebeforeshow', '#index', function(){
$(document).off('click', '#test-button').on('click', '#test-button',function(e) {
alert('Button click');
});
});
Working jsFiddle example: http://jsfiddle.net/Gajotres/K8YmG/
Solution 3
Use a jQuery Filter selector, like this:
$('#carousel div:Event(!click)').each(function(){
//If click is not bind to #carousel div do something
});
Because event filter is not a part of official jQuery framework it can be found here: http://www.codenothing.com/archives/2009/event-filter/
This is probably best solution because event is going to be bound ONLY once.
Solution 4
Probably an easiest of them all.
$(document).on('pagebeforeshow', '#index', function(){
$(document).on('click', '#test-button',function(e) {
if(e.handled !== true) // This will prevent event triggering more then once
{
alert('Clicked');
e.handled = true;
}
});
});
Working jsFiddle example: http://jsfiddle.net/Gajotres/Yerv9/
This is a 180 percent different solution then solution 3, in this case event is going to be bound numerous times but it will be allowed to execute only once.
More info
If you want to find more about this problem take a look at this article, working examples are included.
I have a script that does graphing, using jqplot. It works fine when the document is loaded rendering each graph using jquery's .each method. However, the problem lies when I replace the div with another one when a bar is clicked. It is suppose to render another graph in the position of the old graph. It changes the graph but does not execute the script.
The script that loads the items has this function to change all divs to graphs:
$("div.barchart").each(function(){
barChart($(this).attr("id"),$(this).attr("data-xmlurl"));
});
is there another way to do this so that it would work when a div is changed too?
Update:
Rails generates a script that is ran. However, it doesn't seem to work when I have this:
chart$=$("#<%=params[:chart_id]%>");
chart$.replaceWith("<%=escape_javascript(render :partial=>"chart_partial"}%>");
barChart(chart$.get(0).attr("id"),chart$.get(0).attr("data-xmlurl"));
Note:
For reference, the actual source code can be found in the jquery_cheats project
Perhaps you could add a listener on the parent element? Is it OK if the barChart() function gets called more than once?
Maybe something like this:
$("div.barchart").parent().on("DOMSubtreeModified", function(e) {
// (or maybe use DOMNodeInserted event instead)
$("div.barchart[id][data-xmlurl]").each(function() {
barChart($(this).attr("id"),$(this).attr("data-xmlurl"));
});
});
You can check out my jsFiddle for this here.
On the current application I'm working on, I'm stuck with version 1.5.2. To get around this, I would unbind and rebind my event and load the initialization in both "ajaxComplete" and "ready". I wasn't able to get the DOM to automatically rebind the event. Delegate is suppose to work like "on", but in my instance I still had to use the below logic.
In short, it would look something like this.
$(document).ready(function () {
InitSomethingCool();
});
$(document).ajaxComplete(function() {
InitSomethingCool();
});
function InitSomethingCool(){
$(".something").unbind('click').click(function(e) {
//Unbind and rebind click event.
alert('You clicked me!');
});
}
Reference:
http://api.jquery.com/on/
http://api.jquery.com/delegate/
One of my elements has a mouseenter event on it. The trouble is, I can't add the event until the dom is fully loaded, so I use something like:
document.observe('dom:loaded', function() {
${"my_element").observe("mouseenter", function() { ... }
});
Now, the user might be mousing over the element before the page is fully loaded, and so the event doesn't fire. They have to move their mouse to have it fire. How can I detect if I should fire the event after the page is fully loaded, so the user doesn't have to move their mouse?
the $(document).ready(function(){ and $(function(){ fire when the dom is ready, use on to attach an event handler function for one or more events to the selected elements:
$(function(){
${"my_element").on("mouseenter", function() { ... }
});
This doesn't directly answer your question, but livequery might help you. I think it attaches event handlers immediately, but it might wait until the DOM has been loaded, in which case it doesn't help you.
Depending on the effect you're applying, can you use a CSS rule to simulate it?
Something like
#my_element:hover { color: red }
Then in jQuery, in your mouseenter method, jQuery.Rule to remove the rule.