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)
})
Related
Yesterday I had an issue where a .on('click') event handler I was assigning wasn't working right. Turns out it's because I was was trying to apply that .on('click') before that element existed in the DOM, because it was being loaded via AJAX, and therefore didn't exist yet when the document.ready() got to that point.
I solved it with an awkward workaround, but my question is, if I were to put a <script> tag IN the ajax loaded content and another document.ready() within that, would that second document.ready() be parsed ONLY once that ajax content is done being loaded? In other words, does it consider that separately loaded ajax content to be another document, and if so, does having another document.ready() within that ajax-loaded HTML work the way I think it does?
Alternatively; what would be a better way to handle this situation? (needing to attach an event listener to a DOM element that doesn't yet exist on document.ready())
To answer your question: No, document.ready will not fire again once a ajax request is completed. (The content in the ajax is loaded into your document, so there isn't a second document for the ajax content).
To solve your problem just add the event listener to the Element where you load the ajax content into it.
For example:
$( "div.ajaxcontent-container" ).on( "click", "#id-of-the-element-in-the-ajax-content", function() {
console.log($( this ));
});
For #id-of-the-element-in-the-ajax-content you can use any selector you would use in $("selector"). The only difference is, only elements under div.ajaxcontent-container will be selected.
How it works:
As long as div.ajaxcontent-container exists all elements (if they exist now or only in the future) that match the selector #id-of-the-element-in-the-ajax-content will trigger this click-event.
Javascript in the resulting ajax call will not be excecuted (by default) due to safety. Also, you can't directly bind event to non-existing elements.
You can bind an event to some parent that does exist, and tell it to check it's children:
$(document).ready(function(){
$(document).on('eventName', '#nonExistingElement', function(){ alert(1); }
// or:
$('#existingParent').on('eventName', '#nonExistingElement', function(){ alert(1); }
});
Always try to get as close to the triggering element as you can, this will prevent unnessesary bubbling through the DOM
If you have some weird functions going on, you could do something like this:
function bindAllDocReadyThings(){
$('#nonExistingElement').off().on('eventName', function(){ alert(1); }
// Note the .off() this time, it removes all other events to set them again
}
$(document).ready(function(){
bindAllDocReadyThings();
});
$.ajaxComplete(function(){
bindAllDocReadyThings();
});
try this, that is not working because your control is not yet created and you are trying to attach a event, if you use on event it will work fine. let me know if you face any issues.
$(document).ready(function(){
$(document).on('click', '#element', function (evt) {
alert($(this).val());
});
});
The answer here is a delegated event:
JSFiddle
JSFiddle - Truly dynamic
jQuery
$(document).ready(function(){
// Listen for a button within .container to get clicked because .container is not dynamic
$('.container').on('click', 'input[type="button"]', function(){
alert($(this).val());
});
// we bound the click listener to .container child elements so any buttons inside of it get noticed
$('.container').append('<input type="button" class="dynamically_added" value="button2">');
$('.container').append('<input type="button" class="dynamically_added" value="button3">');
$('.container').append('<input type="button" class="dynamically_added" value="button4">');
$('.container').append('<input type="button" class="dynamically_added" value="button5">');
});
HTML
<div class="container">
<input type="button" class="dynamically_added" value="button1">
</div>
I'm working on a code-base with a friend that has a similar requirement. The delegated event handler option is definitely best if all you want is to attach event handlers. An alternative, especially if you need to do other DOM processing in your $(document).ready function, is to put the code you want run into a script element at the end of your code. Basically, instead of:
<script type="text/javascript">
$(document).ready(function() {
// Your code here
});
</script>
<!-- rest of dynamically loaded HTML -->
Try swapping the script and the rest of the HTML around so you have:
<!-- rest of dynamically loaded HTML -->
<script type="text/javascript">
// Your code here
</script>
This forces the browser to only process your code once it has loaded every other DOM element in the dynamically loaded HTML. Of course this means you'll have to make sure the inserted HTML does not have unintended UI consequences by using CSS/HTML instead of JS. Its an old Javascript trick from years gone by. As a bonus, you don't need jQuery for this anymore.
I should mention that in Chromium v34, putting a second $(document).ready call inside a <script> tag in the dynamically loaded HTML seems to wait for dynamically loaded DOM to load and then runs the function as you described. I'm not sure this behaviour is standard though as it has caused me great grief when trying to automate tests with this kind of code in it.
JQuery AJAX .load() has a built-in feature for handling this.
Instead of simply $('div#content').load('such_a_such.url'); you should include a callback function. JQuery .load() provides room for the following:
$('div#content').load('such_a_such.url',
{ data1: "First Data Parameter",
data2: 2,
data3: "etc" },
function(){ $('#span1').text("This function is the equivalent of");
$('#span2').text("the $(document).ready function.");
}
);
However, you do not need to include the data argument.
$( "#result" ).load( "ajax/test.html", function() {
alert( "Load was performed." );
});
http://api.jquery.com/load/
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 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.
I'm having trouble with some jquery code.
in my HTML page I use ajax to get some info, and then I'm changing an HTML element
with $("#id").html(...).
The problem is, I also have a $(document).ready code which I wanna call only once
when the page is done loading, but after each change in the html with the $("#id").html(...)
the code is called once again.
How can I run the $(document).ready code only once?
Here is an example:
$(document).ready(function(){
// this code will run not only once...
}
function f(){
$("#id").html(...);
}
Try:
var run = false;
$(document).ready(function() {
if(!run) {
...
run = true;
}
});
...or...
$(window).load(function() {
...
});
The first one will make sure it is only run once; the 2nd one is run when the entire page is finished loading (useful if you need to resize things once images have finished loading).
From the comments on the .ready documentation:
Looks like .ready() fires not only when page initially has settled the
DOM, but apparently also after changes to the DOM. This is an issue if
your ready handler changes the DOM. That will result in ready() firing
more than once. It may result in an endless loop if each invocation
adds yet more to the DOM. Firefox and IE behave differently to this,
including different error messages, and leaving the page display in
different states. So, if ready() modifies the DOM, then it would be
wise to have a way to check whether ready has already been fired.
Replying to self: Well it appears that part of the problem is not that
the ready function fires again (though that is possible aparently),
but that changing the DOM causes the script that creates the ready
function to fire again, adding an additional ready function, etc etc.
This seems to happen if the javascript is embedded in the html at a
point beyond (or contained in) the part of the DOM that the ready
handler modifies. (Obviously would be better to put script that
creates a ready function in the document head, but in this case that's
not an option.) Problem fixed by checking a global flag variable to be
undefined before executing jQuery(document).ready(...).
If this might be your problem, you can adopt the same solution:
var onLoadFired = false;
$(document).ready(function() {
/* Ensure this function only runs once */
if (onLoadFired) {
return;
}
else {
onLoadFired = true;
}
/* Business logic */
// .. your code here ..
});
Or, better, move your handler into a separate script file that's included by a script tag in your page's head element.
Try this:
$(window).bind("load", function() {
...
});
My primary navigation [Our Clients, Our Culture, Our People] uses .load() to pull the content and display it in a div on the same page w/o refresh.
I would like for links within the new content to do the same, just display whatever content is being referenced in this same div. However, when they're clicked it goes directly to the new page.
$(function(){
$("a.aboutContent").click(function (e) {
e.preventDefault();
$("#aboutContainer").load($(this).attr("href"));
});
});
So, when Our People is clicked, it pulls ourpeople.htm in to the #aboutContainer div. If you click on a link inside of ourpeople.htm, I'd simply like for that content to display in the same #aboutContainer div. I'm assigning the aboutContent class to links in the subpages as well, but it still isn't working.
You will need to use .live() to listen to clicks from everything, including new DOM elements:
$(function(){
$("a.aboutContent").live('click', function (e) {
e.preventDefault();
$("#aboutContainer").load($(this).attr("href"));
});
});
The reason for doing this is, jQuery code runs when the page is ready - it will attach a click handler to every dom anchor with the class aboutContent - when you load new content, those elements where not there when the page was ready, so never have a click handler attached to them.
Using .live() takes care of that for you. Alternatively, you could place your code in a function, and run that function when the new content is loaded, that way when it runs, it will attach a click handler and the DOM elements will be there, trouble with this is, you would have to mark elements as already having a click handler, or you would end up adding x number of click handlers to some elements.
Probably you can return false from click handler to prevent browser to exeucte HREF on its own.
Like,
$(function(){
$("a.aboutContent").click(function (e) {
e.preventDefault();
$("#aboutContainer").load($(this).attr("href"));
return false;
});
});
Otherwise I would suggest to call some javascript function on href using href="javascript:clickhandler('')"