1

I'm trying to write a generic javascript script to facilitate clicking through <a href> links while only replacing the inner HTML instead of reloading the whole page. The strange thing is, it works, except on any link inside of freshly loaded HTML.

<script src="{{ asset('bundles/app/js/jquery-2.2.0.min.js') }}"></script>
<script src="{{ asset('bundles/app/js/jquery.menu-aim.js') }}"></script>
<script src="{{ asset('bundles/app/js/main.js') }}"></script>
<script type="text/javascript">

    $(document).ready(function() {
        $("a").on("click", function(){
            event.preventDefault();
            $.ajax({
                'url': $(this).attr('href'),
                type: "post",
                success: function(response, status) {
                    document.getElementById("content").innerHTML = response;
                },
                error: function() {
                    console.log('failure');
                }
            });
        });
    });
</script>

When I place the exact same URL from the loaded HTML directly in the sidebar menu containing the initial links, it loads fine. According to the documentation, the .on function should attach itself to any elements added later. I've also tried .delegate and the deprecated .live as suggested by older answers but then even the menu sidebar stopped working.

What am I missing here?

G_V
  • 2,396
  • 29
  • 44
  • You'll need to install the click handlers on the newly loaded HTML as well. – Kenney Feb 20 '16 at 13:50
  • 1
    You need to delegate the click event on dynamically added content like so `$(parentElement).on("click", "a", function(){...`, where the parentElement should be an element which will not be dynamically loaded itself. You could as well just use the document as the parentElement. – DavidDomain Feb 20 '16 at 13:50
  • 1
    Possible duplicate of [Event binding on dynamically created elements?](http://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements) – random_user_name Feb 20 '16 at 13:52
  • It should break because `event` is undefined – baao Feb 20 '16 at 14:17
  • My question differs because I am not dynamically creating elements, I am loading existing pages in another already opened page – G_V Feb 20 '16 at 14:24

2 Answers2

3

You have to apply the behaviour to the loaded HTML as well, like in this example (not tested):

$(document).ready(function() {

    function addBehaviour() {

        // .off first so existing behaviour will be removed and not applied multiple times

        $("a").off("click").on("click", function(){

            event.preventDefault();
            $.ajax({
                'url': $(this).attr('href'),
                type: "post",
                success: function(response, status) {
                    document.getElementById("content").innerHTML = response;
                    addBehaviour(); // add the behaviour
                },
                error: function() {
                    console.log('failure');
                }
            });
        });
    }   

    addBehaviour();


});
Tobias Beuving
  • 630
  • 6
  • 18
  • 1
    I laughed quite hard at how seemingly simple and effective this solution is. I've been racking my brain on this for hours reading jquery tutorials but this just werks. Great answer! – G_V Feb 20 '16 at 14:18
  • 1
    While this did solve my problem, I have to give it to Mark because his follows coding standards closely and is more efficient in finding the element. I am leaving it upvoted since it was very helpful understanding how JS works with elements. – G_V Feb 20 '16 at 15:13
  • No problem, glad I could help :) – Tobias Beuving Feb 21 '16 at 06:52
  • This was an INSANELY simple solution that seemed like it couldn't possibly be right, BUT it was, and man, did you save my bacon! Thank you so much for offering a simple way to solve something that was driving me NUTS! – TC_Guy Mar 13 '23 at 13:09
2

Here I assume your link container is "content" by ID, if not fix that with the correct container ID OR even wrap them IN one:

$(document).ready(function() {
  $('#content').on('click', 'a', function() {
    event.preventDefault();
    $.ajax({
      'url': $(this).attr('href'),
      type: "post"
    }).done(function(response, status) {
      document.getElementById("content").innerHTML = response;
    }).fail(function() {
      console.log('failure');
    });
  });
});

Example markup:

<div id="content">
    <a href="myurl">clickme</a>
</div>

This is NOT as desirable, (placing it on the document) place the handler on the container if you can

$(document).ready(function() {
  $(document).on('click', 'a', function() {
    event.preventDefault();
    $.ajax({
      'url': $(this).attr('href'),
      type: "post"
    }).done(function(response, status) {
      document.getElementById("content").innerHTML = response;
    }).fail(function() {
      console.log('failure');
    });
  });
});

As to WHY this is not desirable (the document); you want to place your event handler hooks as close to the element as possible; when you attach to the document as here, if forces the code to go through the entire document for the event handler to find the a links and look for clicks on those.

Note the the documentation says

"A selector string to filter the descendants of the selected elements that trigger the event. If the selector is null or omitted, the event is always triggered when it reaches the selected element."

Thus for the a selector in your case. SO it places the event handler on EVERYTHING in the document, THEN filters on the a selector for yours. SO if you place it on a smaller container, it has to filter less on every event (click) that is executed.

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100
  • Why is this not desirable? I am using Twig blocks with Symfony and this is all quite new to me and this seems to sacrifice readability for some gain I don't quite grasp. Does this auto-attach itself to new elements? I am eager to learn, if you have any interesting reading material I'll gladly pick it up. – G_V Feb 20 '16 at 14:29
  • added note for the select filter – Mark Schultheiss Feb 20 '16 at 14:49
  • I see, that is quite ugly indeed. I didn't notice the difference in speed because my pages are tiny but this is undesirable behavior at any rate. – G_V Feb 20 '16 at 15:14