how to use jQuery on() in dynamically loaded scripts - javascript

If I have the following html:
<html>
<div id="main_content">
<button>click me</button>
<script src="main.js" ></script>
</div>
</html>
And main.js:
$('#main_content').on('click', 'button', function() {
console.log('you clicked');
});
If I do a full page load, then clicking the button only registers one console message. If, however I subsequently re-load main_content's contents via an AJAX request then each button click gives 2 console messages. It will give 3,4,5... messages for each subsequent AJAX load, but always one for a full page reload.
Might someone explain this behavior and suggest possible solutions?

That's because you are loading multiple copies of main.js and thus you are attaching one more event handler each time.
Every time you load the <script> one more handler is attached
<button>click me</button>
<script src="main.js" ></script>
The lesson here is that script loaded by ajax are parsed and so if you have handlers in them they are attached

If you really need to reload that script all the time, then do this in it:
$('#main_content').off("click").on('click', 'button', function() {
console.log('you clicked');
});
Otherwise put it somewhere else obviously.

I suppose you placed that script into the reloaded content and not within the head section, where all scripts usually reside, because event handler becomes detached from the #main_content each time it's reloaded? Well, there's another way:
$(function(){
...
$('body').on('click', '#main_content', function() { console.log('You clicked!'); });
});
You can place, load and execute this code just once - and won't need to reattach the event each time the block in question is reloaded.
Take note, though, that body in my code is better be replaced with a more specific element - in a perfect world, direct parent of #main_content element.
UPDATE: this is so called event delegation, and it's very well described in the .on docpage. In the previous versions of jQuery, you had to use either .live or .delegate methods.

As has already been said, main.js is getting reparsed and rerun each time you do a .load().
You have a couple of options to solve this:
Move main.js outside of the dynamically loaded content so it isn't reparsed and run each time you do a .load().
Modify main.js so the whole script only executes once.
Modify main.js to aware of being loaded multiple times so it protects against repeatedly installing the same event handlers.
These are presented in order of simplicity of implementation.
For option 2, you could put all of main.js in an if statement like this so that it only ever gets executed once:
if (!window.__main__js__defined) {
window.__main__js__defined = true;
// rest of main.js code here
}
For option 3, you'd have to protect each individual event handler that you wanted anti-dup protection with, probably using .data() on each object to set a flag that you'd already installed a given event handler.

Related

jquery event trigger not working

I have two scripts.
The first script holds a prototype class of a game. This class is with use strict and isn't surrounded by document ready. The trigger:
$("#trigger").trigger("noPossibilities");
In the second script, which also has use strict, I try to catch the trigger:
$(document).ready(function() {
$("#trigger").on("noPossibilities", function() {
console.log("noPossibilities trigger");
});
});
The problem is that I can't catch the trigger. This has probaly something to do with use strict/scope but I can't seem to find a way around this.
Really hope someone can help me
UPDATE
The first script has a prototype class.
This class is getting instantiated in the second script. After the handler. Then it still doesn't work because the first script is loaded before the second script?
Also when I execute this line from the console:
$("#trigger").trigger("noPossibilities");
It doesn't get triggered. Shouldn't it work this way?
UPDATE 2
I found the problem. The first script adds the element with id trigger to the document when it is instantiated. I have a popup at the beginning of the game. Now the handler is getting attached on the click of that button.
The document probaly didn't have the element on which the handler should have gotten attached to. Now it is being attached later on and now it's working.
The issue is not with the scope, you are triggering the event before the handler is attaching to the element. The code inside document ready handler executes only after the DOM elements are loaded. In case you are triggering immediately after the script then it won't work since the elements are still loading.
You can check the working of triggering within a different context by moving it to another document ready handler(to execute only after handler attached).
$(document).ready(function() {
$("#trigger").on("noPossibilities", function() {
console.log("noPossibilities trigger");
});
});
$(document).ready(function() {
$("#trigger").trigger("noPossibilities");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="trigger"></div>

When to use $(function() {}) when registering click handlers with jQuery?

What is the difference between
$(function()
{
$(".some").click(function()
{
...
});
});
and
$(".some").click(function()
{
...
});
I know from here that $(function() is shorthand for $(document).ready(function(). But why are we waiting for the document to be ready? Will the function not be only called when some is clicked anyway?
Note: #2 does not work in my case.
The difference is that #1 waits for the DOM to fully load before running the JavaScript.
The second code runs the JavaScript when it receives it which means it looks for .class elements before they have finished loading. This is why it doesn't work.
You need the document to be ready, i.e. all elements of the document to be available, before you can add an event listener to an element.
The reason is: consider a button, and you want an event listener (listening for the click event, for example.
When your sript runs but the button is not yet present, the attempt to attach the listener will fail. As a result, the associated function cannot be called once the button is actually clicked.
Does that answer your question?
You use the $(function()) simply because you need the DOM to fully load.
For example you have a button and you want to add some action on click. You click the button, but nothing happened, because the button was handled prior to the DOM loading.
If you won't check that the DOM is fully loaded, some unexpected behavior might occur.
Please do not confuse between onload() to ready(), as on load executes once the page is loaded and ready() executes only when the DOCUMENT is fully ready.
$(function(){...}) triggers the function when the DOM is load, it's similar to window.onload but part of jquery lib.
you can also use $(NAMEOFFUNCTION);
It's there to be sure the event has a element to listen to.

Why does only one of these lines of javascript fire upon page load?

I am trying to really understand the details of how a browser loads a webpage.
Load and execution sequence of a web page?
window.onload vs $(document).ready()
I have two javascript statements in a .js file attached to an HTML file:
d3.select("body").append("span").text("Hello, world!");
alert("huh?");
When I load the page, I see an alert "huh." So the "huh" statement fires.
However "Hello, world" is not appended to the document body.
If I then go and run d3.select("body").append("span").text("Hello, world!") in the console then it executes as expected--i.e. it adds "Hello, world" to the body.
What's going on here? alert("huh?")fires after the window.onload event in the DOM, correct? But d3.select... does not fire?
Why the discrepancy?
The reason that the code manipulating the DOM doesn't fire is because there isn't a DOM yet. To remedy this, you can either
Put your <script> tags in the body, so it will run once there is a body
Encapsulate your code inside of window.onload, so it will fire when the DOM is ready.
Example for the second option:
window.onload = function(){
d3.select("body").append("span").text("Hello, world!");
alert("huh?");
}
while window load event your function d3.select("body").append("span").text("Hello, world!"); trigger but the Element has not loaded ,so it does not reflect.
when we use $(document).ready function this will trigger the function after the full load of page ,so the trigger may reflect and visible . where as alert will be same in both window.onload and $(document).ready
For this you need to ensure that a selector for an element is called only after the target element exists in the DOM tree. That's why jQuery's DOM ready is so popular.
Basically, sometimes content of a page, being heavy(large image size etc.), takes time to load. DOM ready is fired first before window load event.
Any function where you select something as in your case needs to be fired after ensuring that the node is in DOM tree, or in other words; at least after DOM ready event fires. Otherwise, even if it works once on one browser, it might be intermittent since it depends on the race, which happens first - execution of JS or creation of the element. Whenever the former happens, it will fail.
I hope this helps !
There is no body element yet. There are two solutions:
Either you wait for document to load
<head>
<script>
$(document).ready(function() {
d3.select("body").append("span").text("Hello, world!")
});
</head>
Or you put it in the body
<body>
<script>
d3.select("body").append("span").text("Hello, world!")
</script>
</body>

What's the difference between using onload in the DOM and .load() in jQuery?

For example:
<iframe onload="functionName('id-name', 'http://domain.com');"/>
versus
$('#id-name').load(functionName('id-name', "http://domain.com');
(Actually... is my jQuery right?)
UPDATE: Thanks, all, for the responses. I'm using:
$('#id-name').load(function() {
functionName('id-name', 'http://domain.com');
});
ETA: "body" was supposed to be an iframe (oops), so am not using window.load.
First of all, the syntax of your jQuery is wrong. The jQuery that's analogous would be:
$('#id-name').load(function() {
functionName('id-name', "http://domain.com');
});
To bind an event handler, you have to supply a function, you were actually calling the function at the binding time.
However, this is not equivalent for a few reasons.
First, you're binding the handler to the #id-name element, not the body (unles you also did <body id="id-name">. So it wouldn't run when the body is loaded, but only when that specific element is loaded. In general, per-element load handlers are only useful for elements that have separate sources that get loaded asynchronously (e.g. images and iframes); they allow you to detect and take action when those elements are filled in. This is particular useful if you're changing the source dynamically.
Second, ssuming your jQuery code is in the $(document).ready(...) handler, as most jQuery code is, it doesn't run until after the DOM is fully loaded. By that time, the body's onload event has already been triggered. Any handlers added at this time will not run for elements that were already loaded. I've created a fiddle that demonstrates this:
<body onload="alert('inline body onload');">
<div id="foo"></div>
</body>
$(document).ready(function () {
$("#foo").load(function () {
alert("foo onload");
});
$("body").load(function () {
alert("jquery body onload");
});
});
Only the inline body onload alert fires.
However, if you just want a jQuery equivalent to putting a function call in the <body onload=""> attribute, the $(document).ready() handler is considered analogous. See jQuery equivalent of body onLoad, the first question in the Related sidebar.
However, as Gloserio's answer says, $(window).load(...) does work, despite the timing (it's recognized specially, similar to $(document).ready()). This modified fiddle demonstrates it.
$(window).load(function () {
functionName('id-name', "http://domain.com');
});
Seems to be a fair enough equivalent of body's load event.
Related :
load event in jQuery
SOF has answered all : jQuery equivalent of body onLoad
onload is a DOM event that is triggered when the page begins to load.
.load() is a shortcut to an AJAX call using jQuery.
They really have nothing in common...other than having "load" in their names.

jQuery document.ready

I am a little confused with document.ready in jQuery.
When do you define javascript functions inside of
$(document).ready() and when do you not?
Is it safe enough just to put all javascript code inside of $(document).ready()?
What happens when you don't do this?
For example, I use the usual jQuery selectors which do something when you click on stuff. If you don't wrap these with document.ready what is the harm?
Is it only going to cause problems if someone clicks on the element in the split second before the page has loaded? Or can it cause other problems?
When do you define javascript functions inside of $(document).ready() and when do you not?
If the functions should be globally accessible (which might indicate bad design of your application), then you have to define them outside the ready handler.
Is it safe enough just to put all javascript code inside of $(document).ready()?
See above.
What happens when you don't do this?
Depends on what your JavaScript code is doing and where it is located. It the worst case you will get runtime errors because you are trying to access DOM elements before they exist. This would happend if your code is located in the head and you are not only defining functions but already trying to access DOM elements.
For example, I use the usual jQuery selectors which do something when you click on stuff. If you don't wrap these with document.ready what is the harm?
There is no "harm" per se. It would just not work if the the script is located in the head, because the DOM elements don't exist yet. That means, jQuery cannot find and bind the handler to them.
But if you place the script just before the closing body tag, then the DOM elements will exist.
To be on the safe side, whenever you want to access DOM elements, place these calls in the ready event handler or into functions which are called only after the DOM is loaded.
As the jQuery tutorial (you should read it) already states:
As almost everything we do when using jQuery reads or manipulates the document object model (DOM), we need to make sure that we start adding events etc. as soon as the DOM is ready.
To do this, we register a ready event for the document.
$(document).ready(function() {
// do stuff when DOM is ready
});
To give a more complete example:
<html>
<head>
<!-- Assuming jQuery is loaded -->
<script>
function foo() {
// OK - because it is inside a function which is called
// at some time after the DOM was loaded
alert($('#answer').html());
}
$(function() {
// OK - because this is executed once the DOM is loaded
$('button').click(foo);
});
// OK - no DOM access/manipulation
alert('Just a random alert ' + Math.random());
// NOT OK - the element with ID `foo` does not exist yet
$('#answer').html('42');
</script>
</head>
<body>
<div id="question">The answer to life, the universe and everything</div>
<div id="answer"></div>
<button>Show the answer</button>
<script>
// OK - the element with ID `foo` does exist
$('#answer').html('42');
</script>
</body>
</html>
The document.ready handler is triggered when the DOM has been loaded by the browser and ready to be manipulated.
Whether you should use it or not will depend on where you are putting your custom scripts. If you put them at the end of the document, just before the closing </body> tag you don't need to use document.ready because by the time your script executes the DOM will already be loaded and you will be able to manipulate it.
If on the other hand you put your script in the <head> section of the document you should use document.ready to ensure that the DOM is fully loaded before attempting to modify it or attach event handlers to various elements. If you don't do this and you attempt to attach for example a .click event handler to a button, this event will never be triggered because at the moment your script ran, the jQuery selector that you used to find the button didn't return any elements and you didn't successfully attach the handler.
You put code inside of $(document).ready when you need that code to wait for the DOM to load before executing. If the code doesn't require the DOM to load first to exist, then you can put it outside of the $(document).ready.
Incidentally, $(function() { }) is short-hand for $(document).ready();
$(function() {
//stuff here will wait for the DOM to load
$('#something').text('foo'); //should be okay
});
//stuff here will execute immediately.
/* this will likely break */
$('#something').text('weee!');
If you have your scripts at the end of the document, you dont need document.ready.
example: There is a button and on click of it, you need to show an alert.
You can put the bind the click event to button in document.ready.
You can write your jquery script at the end of the document or once the element is loaded in the markup.
Writing everything in document.ready event will make your page slug.
There is no harm not adding event handlers in ready() if you are calling your js functions in the href attribute. If you're adding them with jQuery then you must ensure the objects these handlers refer to are loaded, and this code must come after the document is deemed ready(). This doesn't mean they have to be in the ready() call however, you can call them in functions that are called inside ready() themselves.

Categories

Resources