2

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?

1
  • move the script into the head, or at the really bottom of the body. Commented Jun 20, 2012 at 21:50

4 Answers 4

5

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

Sign up to request clarification or add additional context in comments.

6 Comments

-@Nicola, can you explain your last sentence a little more. Full page loaded scripts aren't "parsed"? The difference in handling of the <script> tags between regular page loads vs. AJAX confuses me.
@timpeterson yes sure on full page load they are parsed too, i was simply saying that if you load a script tag by ajax the script is parsed :)
still confused by what you are calling parsed, one more time please?
it effectively 'runs' the script each time you re-load it via AJAX.
@timpeterson i mean the the main.js script is loaded every time, is parsed by the browser (executed) and if you have any event handlers in it they are attached
|
3

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.

1 Comment

thanks I hadn't thought of off(). Sometimes loading that script is just laziness on my part in testing things but yeah I usually put this type of code elsewhere.
1

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.

Comments

1

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:

  1. Move main.js outside of the dynamically loaded content so it isn't reparsed and run each time you do a .load().
  2. Modify main.js so the whole script only executes once.
  3. 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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.