Ruby on Rails - JavaScript in dynamically generated partials - javascript

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.

Related

jQuery event not firing (advanced)

For a dynamic added element via ajax or any after page load, I know that we have to use the .on like
$(document).on('click', '#dynamically-added-element', function() {
// do something
console.log('Hello World!');
});
I even wrapped mine with (function($){})(jQuery); to make sure no conflict; and to make sure everything should be run/loaded in proper order, I also have $(document).ready(function(){});.
Now that event was originally meant to run on Page A and it works well, but when I made and try it run on Page B having the same id for the main container and have the same child html, it doesn't work.
So I've done some experiment/tests and both works.
(1) So what I've done, I wrote exact same event listener through my browser's console with just console.log and click the target element, it works!
(2) On the same .js script, I've added exact same event listener with just console.log but wrapped it inside setTimeout(function(){}, 5000);, after 5 seconds, I clicked the target element and it worked!
So my question now is, why my original code doesn't work? Why these tests works? So what's the solution?
Also, my html elements aren't loaded dynamically, it's loaded on page load, generated via server side PHP.
Edit:
This info might be useful.
running in magento
js script is being included on head (that's why $(document).ready is really important).
jquery script is being loaded before my custom script.
Page A and Page B are on different pages.
It's not an ajax generated element nor js/jquery added element on the fly; it's generated on postback with php code
(3) My third experiment works as well. But I don't want this to be my solution if possible, nor using javascript inline onclick. So what I did, I added a whole new script block inside the php file where that 'part' of the page is being included, with exact same code and it works. But the thing is, I have now two identical event listener.
So now I have 2 identical event listener (regardless if the other one doesn't work - only for Page B), 1 from within (internal) and 1 from external .js file.
<script type="text/javascript">
(function($) {
$(document).ready(function() {
$(document).on('click', '#the-container a', function(e) {
e.preventDefault();
var target = $(this).attr('href');
});
});
})(jQuery);
</script>
Just write you document.on outside of the document.ready that should work and comment your "e.preventDefault();". It worked on the fiddle when I changed it.
<script type="text/javascript">
$(document).on('click', '#the-container a', function(e) {
//e.preventDefault();
var target = $(this).attr('href');
});
</script>
jQuery event handlers are attached to the elements themselves not the selector, if you are instantiating an event handler to an element selector before the element has been loaded, it is not attaching a listener to anything.
For example this will not work:
$.get('http://example.api.com',
function(data){
$node = $('<p></p>')
$node.text(data)
$node.addClass('elementToListen')
$('body').append($node)
})
// this gets executed before the ajax request completes. So no event
// listener gets attached
$('.elementToListen').on('click',function(e){'do somthing'})
because the event listener has been set up before any of the elements have been added to the DOM.
While this will work:
$.get('http://example.api.com',
function(data){
$node = $('<p></p>')
$node.text(data)
$node.addClass('elementToListen')
// here it is getting executed after the AJAX request in the
// success callback
$node.on('click',function(e){'do somthing'})
$('body').append($node)
})

How can I replace(re-execute ) current script functions?

I want to replace load script into existing script after ajax response.
Due to I will only update some element so the whole page will not be reloaded, and some event on replaced element will be lost.
Re-Run application Javascript on ajax loaded content <--- This is not a solution for me...
I tried take the script tag from loaded html and eval() them. However, this will not replace the existing functions but double them.
I want to replace, or re-execute the script function by not use $(document).html(data);
I read many references such like,
http://www.javascriptkit.com/javatutors/loadjavascriptcss2.shtml
But I dont very understand it.....
Please help and advice a more easy understanding example. Thank you very much!
My script now is
$script.each(function(index){ //data from loaded page, same page
if(!$(this).attr('src')){ //replace where is not from external only
$(document.getElementsByTagName( 'script' )).slice(index).remove();
//remove existing script, no working
eval($(this).text());
// re-execute, wokring
};
});
You may try to combine your logic to functions and call it when you want. Moreover, if you ajax-loaded content will contains JS functions you can call it after place retrieved html to the page.
When I need to bind events to some dynamically loaded components I implement function like following:
function rebindDynamic() {
$('#elem1')
.unbind() //Or unbind('click') if you want to unbind specific handlers
.click(function (e) { ... });
$('#elem2')
.unbind() //Or unbind('keyup') if you want to unbind specific handlers
.keyup(function (e) { ... });
...................
}
I hope it will helps.

jQuery .html function with .load

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
});

jQuery after ajax call, events with the same Ids stopped working

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.

script not working on Ajax-loaded Content

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()

Categories