I have a jquery powered voting system, and the items on other pages are loaded via ajax, but on ajax-loaded contents the jquery voting needs the page to be refreshed for the vote to be processed.
http://ohmygosh.vr.lt/
I think the problem is within the actions in this file (?) But i'm not really sure. I don't have any code to start since I don't even know where to start. How do I fix this Problem?
Seems that you are using a SmartAjax to load the page content. What SmartAjax does is that it changes the page url (using pushState or hash), loads data and REPLACES the content of #posts div with the loaded content.
The problem is that you are listening click events from DOM elements which are later deleted and replaced with new DOM elements. Thus, the click events from the ajax loaded content are not listened. live() doesn't help in this case.
To fix this, you have at least these two options:
1) Use jQuery's delegate() to attach the handler and use #posts div as the root element, since it remains after the new content is loaded.
Something like this should work:
$('#posts').delegate(':input', 'click', function() {
var value = $(this).val();
alert('You have clicked thumbs up/down, value: ' + value);
return false;
});
2) Add click listener to the loaded items after data loading is completed.
Notice: Starting jQuery 1.7 you should use on() instead of delegate()
Related
I know putting JavaScript in partials is a bad idea because the page will have to load up a new script every time a new partial is loaded. I am aware of and have read this question, but its answer did not work for me (putting the script into app/javascripts/application.js). I think it is because I am working with partials that are dynamically generated onto the page. I think the dynamically generated partial does not react to the script loaded up on the initial page.
For example, I have a "Rule" div with a select input that has a script to do something when the select input is changed. This works for every Rule div that is generated on page load. But then there is also a "+" or "ADD" button which will dynamically generate more Rule divs which do not respond to the script, unless that script is in the partial.
Is there a good way to keep the script out of the partial when the partial is dynamically generated?
JQuery sets listeners on page load (i.e. $(selector).on(etc.)), so it doesn't listen for events on dynamically added elements. There is a way around it, though. You need to use what is called a delegate.
$(document).ready( function() {
$('body').on('change', 'input.selector', function(e) {
// do something
});
});
I'm not sure what your event (here I put change) or selector for the select you are using (here I put input.selector), but if you replace those with the appropriate information, it should work even with dynamically added elements.
You can use JQuery to execute the code only after the document has loaded onto the DOM:
$( document ).ready(function() {
//Call your functions here
});
That way, your JS will have access to whatever is on the page, because you are ensuring that it is fully loaded.
If your divs are not in place on document ready, you can use event delegation, as suggested by ptd. Basically what this means is that you install a handler on a parent div (which will be present on document ready) which says, "hey, when you click on this dynamic div inside of me, call this function".
$('div#master').on('click', 'div.dynamic', function(event) {
console.log("action here")
var $dynamicDiv = $(event.currentTarget) //The current Target is the thing you clicked on, not the parent.
});
If you are adding elements to the DOM using AJAX calls, but want to keep your JavaScript in your assets folder only, here's a quick and clean way to accomplish this.
// /app/assets/javascript/foo.js
// On intial page load
$(document).ready(function() {
yourJavaScriptForPartials();
});
// After a subdomain field is loaded via AJAX
$(document).ajaxComplete(function() {
yourJavaScriptForPartials();
});
function yourJavaScriptForPartials() {
// Insert your javascript here.
};
Now, any JavaScript you put in the yourJavaScriptForPartials() function will be available both to the initially loaded DOM, and to any DOM elements added via AJAX. For reference, here is the JQuery page for the ajaxComplete event listener.
I am loading HTML content using jQuery's .html() function. Part of the HTML content I am trying to load are images, which take some time to be loaded. What I do is on an onclick event,
$('div').on('click',function() {
$('html').fadeOut(1000)
.html(content)
.load(function() {
$('html').fadeIn(1000)
});
});
What I wanted to happen is that, when the DOM has finished loading, I want it to fadeIn. If it is still not finished, I want it to stay hidden hence, the fadeOut function before the html load.
Is this possible? That method doesn't seem to work for me.
First, if you are replacing the entire HTML element, you might as well just do a full request cycle. That's essentially what you're going to end up with and it will be easier to hook to the window load event using a full request than to handle this with AJAX. If you're not replacing the entire page, then you've got your selector wrong.
Second, if you are just loading the DIV, then you can try hiding the DIV, binding the load event to it, then loading it with content. See the caveats for the http://api.jquery.com/load-event/ method for more information.
$('div').on('click',function() {
var $this = $(this); // save reference for future use
$this.fadeOut(1000) // hide
.load(function() { // hook up handler
$this.fadeIn(1000)
})
.html(content); // load content
});
Not sure if this is a bug or I am not suppose be doing this.
I have a page with sidebars that loads the main body dynamically.
For page1, I have a callback for an element #id1, which works on initial load.
After the user navigates to page2, the main content will get replaced by contents of page2, which also has an element with #id1, they serve the same purpose. events are initialized there as well.
The problem is that everything on page 2 would work except the event associated with #id1.
If navigating back to page 1, #id1 wouldn't work as well.
After looking at the console, I found that when calling $("#id1") sometimes give me the initial load element (not destroyed?), which is probably the reason.
The ajax load simply uses:
$.get(path, function(data) {$('#main').html(data)});
Any idea what's going on here?
If the old elements are not 'properly destroyed in jquery', what is suppose to be done here.
While it's not clear exactly what you're binding, the solution is to use (depending on your jQuery version) live() or on() to ensure that you bind to elements that aren't in the DOM at execution time.
jQuery 1.7+: on()
$(document).on('click', '.selector', function() { ... });
jQuery <1.7: live()
$('.selector').live('click', function() { ... });
Remember that an ID should only occur once
Because you don't know what elements with ID's may still be living in the DOM after the Ajax call you should stick with classname's instead.
With that you can use jQuery's .live() to bind to elements that have been dynamically loaded.
You have to generate dynamic id for that. When you click the right bar option the id will generated and place the id in the body element "id" tag.So you know that which id is generated for which page. Call a javascript function on "onclick" event and pass that id to this function then call the body element as $("#id"+that generated_id).something;
I think this will help.
I have a table, that I loop through using jquery and modify the href of each link.
IF someone pages through the table, the links are refreshed using ajax (page doesn't reload).
Now all my links are not modified since the table has refreshed.
Paging is done via a drop down list.
Can jquery live help in some way to re-apply the modifications to the urls that I do when the page initially loads up?
$("#someTableID").each(function () {
// modify href of links in each row, append ?user=342 to it.
}
There's a couple ways to approach this. You could go in any of the following directions, but your only limitation is how creative you can be:
First option would be to delegate the paging event to fire a loop as described above. After the content is finished loading, you can loop through the links modifying each individually.
Alternatively, you can store your query string modification as data in the link markup itself:
some link.
Then, you can create a delegate function to wire up your new urls(as they are clicked or any delegated event you choose):
$("body").delegate("a", "click", function(e) {
e.preventDefault();
var $this = $(this);
window.location = $.param.querystring($this.attr("href"), $this.metadata().myData);
});
For option 2, I'm using a couple of plugins, but those are not required for the idea. Plugins used are metadata and bbq. Metadata is built in to the edge version of jQuery and should be included in $.data() as soon as they make the next release(currently 1.4rc1). Custom attributes would also work in this scenario as opposed to metadata.
String appending is also an option, I'm just a fan of the jquery-bbq project as it provides a lot of power in url manipulation.
I've provided a jsFiddle sample here.
No, .live() can't help you. All that .live() and .delegate() do is help handle events from content that changes. It can't automatically fix things for you. The code that performs the ajax reload will have to apply all transformations to whatever is reloaded.
Try this:
$("#someTableID > tr").each(function () {
var $link = $(this).find('a');
$link.attr('href', $link.attr('href') + '?user=342');
}
You aren't actually hooking an event up to each link, you're just modifying the text for each link, correct? Then no, jquery.live() is the incorrect tool; it is a way of monitoring user events on the page. You're just trying to decorate existing text.
What you want is to connect a callback to the ajax handler to do the decorations to the new content of the page after it has been received by the ajax handler.
You may even want to hook the callback in after the new content has been received, but before it has been placed into the page, to make sure there's no possible race condition where the link exists but the extra material hasn't been added.
I suspect you're using load. Look to the jquery.ajax.load() source code, and see how you can replace it with a more robust callback handler using jquery.ajax().
I have a list of elements that I am loading via ajax (using jQuery's .load()). Each of these elements has an (edit) link next to it that lightboxes (using colorbox) a little edit form. When the lightbox is closed I use the onClosed callback to reload the ajax list to show and changes made during the edit.
The colorbox call looks likes this:
$('.colorbox').colorbox({
'iframe':true,
'onClosed':function(){
$("#featureList").load("/template/featureList","id="+$("#model_id").val());
}
});
My list looks like this:
<div id="featureList">
<ul id="features">
<li id="item_000000000008+0">Element 1 (<a class="colorbox" href="/template/featureLightbox?template_id=000000000008&delta=0">Edit</a>)</li>
<li id="item_000000000008+1">Element 2 (<a class="colorbox" href="/template/featureLightbox?template_id=000000000008&delta=1">Edit</a>)</li>
</ul>
</div>
I looked at the colorbox source code and saw that is uses jquery live() to do the bind. Here it is:
$('.' + boxElement).live('click', function (e) {
if ((e.button !== 0 && typeof e.button !== 'undefined') || e.ctrlKey || e.shiftKey || e.altKey) {
return true;
} else {
launch(this);
return false;
}
});
You can see above the way colorbox works is by binding to "boxElement", which is a class it creates called "cboxElement". Before the live() bind colorbox adds this class (cboxElement) to all of the elements matching the selector (.colorbox in my example), then binds to this new class.
So thought that if I placed the colorbox bind outside of the ajaxed content it would bind to the links after I replaced the #featureList div with ajax, since live() is supposed to bind to elements "now or in the future". But it doesn't because it's binding to .cboxElement instead of .colorbox so when the ajax reloads colorbox doesn't re-add the .cboxElement class to the elements.
I tried calling $.fn.colorbox.init() in the ajax content to get colorbox to re-add the .cboxElement class to the elements, but this had no effect. (I do something like this when dealing with shadowbox, but it doesn't seem to work the same for colorbox.)
So then I tried to place all the colorbox code inside the ajax content. When I do this the colorbox binds are stacking/chaining. So the second time I call it I get two colorboxes (and have to hit the "close" button twice to return to the main screen). The third time I get three. This is because when I call colorbox again it adds the .cboxElement class, making the old live() binds active again, and it also adds ANOTHER live() bind to it. I tried to clear out the .live() binds by calling .die() first, but it didn't work for some reason.
I have found a couple of related posts but none of them solved this problem since colorbox is already using live():
Problem with jQuery Colorbox
jQuery AJAX table to a page but now the colorbox overlays no longer work
Any other ideas out there? I am really stumped. I feel like I should switch to a different lightbox, but in general I like colorbox and it was working great everywhere else on the site until this ajax problem came up.
Thanks!!!
EDIT:
So, in this case my issue was that my framework (Yii) was including a duplicate colorbox script on each AJAX call, which was causing the problem. So watch out for that!
For everyone having issues who is NOT facing the duplicate-script issue I was: #Relic points out below you can sort of "sidestep" some issues by doing your own jQuery delegate() bind which does a "direct call" of colorbox, instead of relying on colorbox's default live() binding. I would tweak it like this for my case:
$(document).delegate("#features a", "click", function (event) { // $.colorbox() call }
First of all, you shouldn't use .live() it's deprecated. Instead learn how to use .delegate() You'll find this is a much more powerful listener and will help solve your problem.
On page load initially the DOM is ready, and colorbox is initilized for the selector
AJAX calls a new piece of the page with some DOM element that is in the colorbox selector list, but isn't noticed because it loaded into the page after the selector was read by the javascript.
Try the following - as it watches body #main for all, existing and new a[rel='lightbox']:
$("body #main").delegate("a[rel='lightbox']", "click", function (event) {
event.preventDefault();
$.colorbox({href: $(this).attr("href"),
transition: "fade",
innerHeight: '515px',
innerWidth: '579px',
overlayClose: true,
iframe: true,
opacity: 0.3});});
EDIT for ".on()"
$("body #main").on("click", "a[rel='lightbox']", function (event) {
event.preventDefault();
$.colorbox({href: $(this).attr("href"),
transition: "fade",
innerHeight: '515px',
innerWidth: '579px',
overlayClose: true,
iframe: true,
opacity: 0.3
});
});
Yes, big change, I know, but the point is the 'on' method can also be used as 'bind', so that's kind of cool.
i solved this in a pretty easy way.
If you are sending back an ajax response (usually a javascript response) - attach the normal colorbox binding code (as you would do in any place) in that response.
$('.colorbox').colorbox({
'iframe':true,
'onClosed':function(){
$("#featureList").load("/template/featureList","id="+$("#model_id").val());
}
});
attach this in your js response from server. this worked for me.
The problem appears to be related to something I didn't notice at first and therefore didn't put in my question. I have edited the question to reflect this new information.
My issue was that AJAX response was multiple-binding and including the colorbox script multiple times. The Yii Framework helper widget I was using to include the script is also including jQuery and Colorbox in the response EACH TIME. Yii doesn't have a way to determine if the required scripts (like jQuery) have already been included in the main page (typical stateless HTTP issue) so it includes it in the AJAX partial render each time.
I solved this issue by not using a Yii Widget to do the Colorbox binding on each Ajax call's renderPartial view. I just include thecolorbox binding on the parent page, so the Ajax content has no JS in it.