27 Replies Latest reply: Aug 8, 2014 4:06 PM by Liam Dilley RSS

    Solution: Scripts not working after Ajax replaces part of page

    Adam Cook Community Member

      I'm far from an expert at js and jQuery, so I welcome collaboration and corrections on this solution. This is all from the context of a catalog's large product view when using ajax to switch between grouped products.

       

      First, the issue:

       

      1. BC updates the product information via an ajax call when you click the 'add to cart' button or make selections between product groupings.
      2. When BC does this, it entirely reloads page_content.html. The net result: all the script bindings that were attached to those tags are wiped out.
      3. The scripts are never rebound to the tags because that initially occurs when the DOM is being created. Content changes via an ajax call don't recreate the DOM.

       

      Possible solutions:

       

      1. Hardcode the affected scripts inside page_content.html. Don't just link a script file to it. It has to be hardcoded. This is not recommended, as it is bad coding practice to place scripts inline with our html.
      2. Avoid the use of scripts in that area (good luck).
      3. Use delegation with .on() to rebind. I believe this is the best option, and it's the one I am going to demonstrate.

       

      To avoid inline scripts, I like using an external script.js file. It keeps my document clean from script clutter and it's easier to manage my scripts. Also, I want a solution I can implement one time, in one place, instead of with every situation I encounter. Below is an example of how my scripts are organized. The areas in bold are the specific solution.

       

      jQuery.noConflict();

      function scriptlist() {

      var $ = jQuery;

      var Engine = {

      utils : {

      functionName : function(){

      // Do stuff in here

      },

      functionName2 : function(){

      // do something else   

      }

      },

      ui : {

      functionName : function(){

      // Do stuff in here

      },

      functionName2 : function(){

      // do something else   

      }

      }

      };

       

                     if ( $(element).length ) { Engine.utils.functionName(); }

                     if ( $(element2).length ) { Engine.utils.functionName2(); }

                     if ( $(element3).length ) { Engine.ui.functionName(); }

                     if ( $(element4).length ) { Engine.ui.functionName2(); }

       

      };

       

       

      jQuery('body').on('click.productSubmitInput', function(){

                jQuery.ready(scriptlist());

      });

      scriptlist();


       

       

      The key here was the use of the .on() jQuery method. Here is how it works:

       

      1. I attached on() to the body element on the page with jQuery('body').on()
      2. It's attached to the body, because the body is outside of page_content.html and won't have it's binding removed during the ajax call. Events register all the way up their ancester elements on the page, so on() can pick up the event all the way up to the body.
      3. Told it to look for a certain event: 'click.productSubmitInput'
      4. When the event occurs, it should trigger ready() to refresh the DOM. jQuery.ready()
      5. and run my functions against it, thereby rebinding my document.
        • 1. Re: Solution: Scripts not working after Ajax replaces part of page
          Adam Cook Community Member

          If anyone has thoughts about this, I would love to hear them. I'm always happy to stand corrected or receive new insights! I think it would be nice to have a 'best practice' model collaborated on by the community.

          • 2. Re: Solution: Scripts not working after Ajax replaces part of page
            Liam Dilley CommunityMVP

            an .on on the body is really nasty Adam. You nto see why?

            Google , heaps of articles out there on how you do it the right way Adam.

            • 3. Re: Solution: Scripts not working after Ajax replaces part of page
              Adam Cook Community Member

              I'll check it out. I'd really love to hear/see better solutions people would implement instead. Even if it's just a slight change. How would you do it differently, Liam?

              • 4. Re: Solution: Scripts not working after Ajax replaces part of page
                Liam Dilley CommunityMVP

                Really fun to google and read articles.

                Also side note. I hope people test IE when they do partial refresh stuff, they may notice something if they do not know what IE does

                 

                When you Ajax Adam, basically you have a callback element to it. It is there for a reason

                • 5. Re: Solution: Scripts not working after Ajax replaces part of page
                  Adam Cook Community Member

                  I think I will attach it to 'document' because it's faster. I don't want to attach it to something deeper because everybody's implementations are different. Not designer will have the same page structure I do. I suppose I could simply note in the doc that the shorter distance it goes the better.

                  • 6. Re: Solution: Scripts not working after Ajax replaces part of page
                    Adam Cook Community Member

                    Here's a quote from api.jquery.com:

                     

                    Attaching many delegated event handlers near the top of the document tree can degrade performance. Each time the event occurs, jQuery must compare all selectors of all attached events of that type to every element in the path from the event target up to the top of the document. For best performance, attach delegated events at a document location as close as possible to the target elements. Avoid excessive use of document or document.body for delegated events on large documents.

                     

                     

                    Document is faster than body, but the main issue still remains. It's likely doing a lot more work than it needs to. So, for implementing this, find an element just outside the affected area to attach it to.

                    • 7. Re: Solution: Scripts not working after Ajax replaces part of page
                      Adam Cook Community Member

                      Liam, can I convert this into a document. I intended for it to be editable.

                      • 8. Re: Solution: Scripts not working after Ajax replaces part of page
                        Liam Dilley CommunityMVP

                        You really need to google, no where near faster, bit cringe worthy actually I do not think you understood what I said.

                        • 9. Re: Solution: Scripts not working after Ajax replaces part of page
                          Liam Dilley CommunityMVP

                          What happens when you run a script, when you want it, not constant as you shown here. Google, honestly. You wont see ajax re-iniitlsement done in this way. I really would never do it like this ever.

                          • 10. Re: Solution: Scripts not working after Ajax replaces part of page
                            Liam Dilley CommunityMVP

                            I will do try find time to do a demo with your method and one of the correct ways and get you to run it in chrome and actually see how much more time your method has and load on it.

                            • 11. Re: Solution: Scripts not working after Ajax replaces part of page
                              Adam Cook Community Member

                              Sounds good.

                               

                              Believe me, I Google. The thing about that is, though, that you have to know what you are looking for. I'm nowhere near your level, so the questions I would ask are not the same as the questions you would ask.

                              • 12. Re: Solution: Scripts not working after Ajax replaces part of page
                                Liam Dilley CommunityMVP

                                You will get double launches on some instances if you do that as well (done similar you see myself,  trying to be clever and not research).

                                 

                                Look at the jQuery ajax.

                                On success as a function you can do stuff. Your code is all objects, what do you need to trigger, what are you actually fetching?

                                When to use .load, .ajax, .post .get etc. What are you fetching and bringing to the table.


                                Also you have IE. IE Even in the latest one (but does not generate the issue mind you) heavily caches. MS cheat the speed they have by heavy heavy cache.

                                You use ajax to refresh an image on a page it will work in all but IE. IE cached it, wont let it go. Even does it with content elements that contain images and other cases.

                                 

                                Think about what you done, Everything always on. Do that with a light switch what happens?

                                • 13. Re: Solution: Scripts not working after Ajax replaces part of page
                                  Adam Cook Community Member

                                  So are you saying to put it on the individual functions so the lights are only on in the current room? Save electricity?

                                  • 14. Re: Solution: Scripts not working after Ajax replaces part of page
                                    Adam Cook Community Member

                                    As far as ajax is concerned, I don't have access to BC's ajax request. But now you have me wondering if I can call functions based on success of their request. I know jquery has ajax events for...ajaxSuccess()?

                                    • 15. Re: Solution: Scripts not working after Ajax replaces part of page
                                      Liam Dilley CommunityMVP

                                      jquery Ajax can set the response as text, html json etc depending on what is returned. You can have html returned and handle it.

                                      But there is always a success and fail. I see heaps of people on BC using ajax and have a blind success and no correct handling.

                                      Take a bc confirmation page from something which is a success or fail. Both those in terms of ajax is a success so you handle that on top.

                                      But a sucess function is something you run anything in.

                                      If a function or object of yours ends due to an ajax load you only need to relaunch that, not everything or have everything live.

                                      You also need to consider that inline scripting in some calls will actually run. IF you have html response and just dump it in a element it will execute and screw up.

                                      Your method will also run into endless loops as wll in some cases.

                                       

                                      http://api.jquery.com/jQuery.ajax/

                                      http://jqfundamentals.com/chapter/ajax-deferreds - Read the asynchronous bit

                                      • 16. Re: Solution: Scripts not working after Ajax replaces part of page
                                        Coast Creative Community Member

                                        Hi Adam,

                                         

                                        There seems to be a total disconnect in this thread.

                                         

                                        You originally posted "BC updates the product information via an ajax call when you click the 'add to cart' button or make selections between product groupings."

                                         

                                        With a BC ajax call and there is no way to access a call back, not that I've found anyway.

                                         

                                        The .on() solution works. There may be a slight performance penalty but there are situations where it seems to be unavoidable to make the UI work.

                                         

                                        So the problem simply stated is: "BC ajax breaks jQuery bindings and there is no way to rebind without access to a callback."

                                         

                                        If there is in fact a way to rebind without .on() or a similar approach I would love to hear about an ACTUAL SOLUTION.

                                         

                                        Can we please see a serious discussion of this issue and hear from BC developers?

                                         

                                        It is a real problem trying to implement the myriad work arounds necessary to create a quality shopping experience with your hands tied.

                                        • 17. Re: Solution: Scripts not working after Ajax replaces part of page
                                          Adam Cook Community Member

                                          @Coast Creative,

                                           

                                          That's what I originally intended with this thread. What I wanted to do was collaboratively produce a best solution with other partners. However, that didn't happen. Also, I wanted to be able to go back and edit the original post whenever a correction or best practice was presented. But, I can't edit the post now that there are additional posts, so it is now stuck in its semi-original state.

                                           

                                          I'm having a hard time seeing how this isn't an ACTUAL solution for the problem, though.

                                          • 18. Re: Solution: Scripts not working after Ajax replaces part of page
                                            Adam Cook Community Member

                                            @Liam,

                                             

                                            I just got to a place where I could look through the links you provided and really study is out. They led me to an important paragraph on API.jquery.com:

                                             

                                            jQuery.Deferred() introduces several enhancements to the way callbacks are managed and invoked. In particular, jQuery.Deferred() provides flexible ways to provide multiple callbacks, and these callbacks can be invoked regardless of whether the original callback dispatch has already occurred. jQuery Deferred is based on the CommonJS Promises/A design.

                                             

                                            Second to last sentence is the key. Instead of using .on() to recall scriptlist(), use .when().then() or something. Then, for each function inside scriptlist, make sure it is set to be conditional so I'm not running more code than necessary.

                                            • 20. Re: Solution: Scripts not working after Ajax replaces part of page
                                              Adam Cook Community Member

                                              Sweet! That took a lot. I suppose the only other BIG thing to take care of (as far as efficiency is concerned) has to do with calling the Dom a lot. The whole Dom.js practice you were referring to in the other discussion.

                                              • 21. Re: Solution: Scripts not working after Ajax replaces part of page
                                                Adam Cook Community Member

                                                Hey Liam,

                                                 

                                                I would love the ability to create and edit a document on here. That's what I originally had in mind for this. What's the word on that.

                                                • 22. Re: Solution: Scripts not working after Ajax replaces part of page
                                                  aanguelo Community Member

                                                  Is there an example for using jQuery.Deferred() to hook into ajax callback?

                                                  • 23. Re: Solution: Scripts not working after Ajax replaces part of page
                                                    Adam Cook Community Member

                                                    function executeCallback(callback,param){

                                                            if (typeof callback === 'function') {

                                                                    var deferred = $.Deferred();

                                                                    if (param) deferred.resolve(callback(param));

                                                                    else deferred.resolve(callback());

                                                                    return deferred.promise();

                                                            }

                                                    }

                                                     

                                                     

                                                    $.ajax({

                                                            type: 'POST',

                                                            url: 'my/action/path',

                                                            data: $('#myForm').serialize(),

                                                            success: function(response) {

                                                                    executeCallback(myScript,response);

                                                            }

                                                    });

                                                    • 24. Re: Solution: Scripts not working after Ajax replaces part of page
                                                      TheBCMan Community Member

                                                      Just for future people you might want to update the original post as you figured out that the entire page isn't refreshed just the container DIV. You have the correct methods with rebinding everything on content reload.

                                                      • 25. Re: Solution: Scripts not working after Ajax replaces part of page
                                                        Adam Cook Community Member

                                                        Wish I could. I can't edit the original post.

                                                        • 26. Re: Solution: Scripts not working after Ajax replaces part of page
                                                          aanguelo Community Member

                                                          Although it would be nice to handle shopping cart behavior as part of the BC framework, I was able to finally implement work arounds to do what I needed to get control over a number of validation\UI scenarios. Thank you Adam\Liam, your posts helped guide the way.

                                                           

                                                          To do this, I wrapped the entire cart contents in a DIV and gave it an ID of "catCartDetails" so I can bind to it's onChange event. In the query onChange function I then bind the ajax monitoring to receive the ajax submission events made by BC when the shopping cart page is changed. This is important to handle multiple iterations based on user interactions. Inside the ajax monitor I have function calls to handle whatever is needed, in my case;

                                                           

                                                          1) I have a function to handle multiple checkout validations with corresponding UI behavior and Google Analytics calls to implement the new eCommerce Checkout Funnel.

                                                           

                                                          2) Correct implementation of "Continue Shopping" link that returns customer to the last page they were on.

                                                           

                                                          3) Function that rebuilds mobile specific cart behavior.

                                                           

                                                          <script type="text/javascript">

                                                                $("#catCartDetails").on("change", function() {

                                                                      $.when( $.ajax( "/ajaxpro/CMS.OrderRetrievev2,Catalyst.Web.CMS.ashx") ).then(function( data, textStatus, jqXHR ) {

                                                                               shippOptionsValidate();

                                                                               setContinueShopping()

                                                                               mcartBuild();

                                                           

                                                                         });

                                                                  });


                                                               //Click on BC generated "Remove" link was not changing my DIV, but rather the table cell contents it creates, so needed to

                                                               //bind to the remove link click event to run same logic.

                                                               $(".mcart-remove").click(function(){

                                                                      $.when( $.ajax( "/ajaxpro/CMS.OrderRetrievev2,Catalyst.Web.CMS.ashx") ).then(function( data, textStatus, jqXHR ) {

                                                                            shippOptionsValidate();

                                                                             mcartBuild();

                                                                             setContinueShopping();

                                                                      });

                                                                });

                                                          </script>

                                                           

                                                          SOME ADDITIONAL NOTES:

                                                          To run code specific to mobile and testing I setup a new domains, i.e. mobile.mywebsite.com and test.mywebsite.com; and created a utility controller that helps manage running code for mobile, testing, other, etc.

                                                           

                                                          To not hose up your Google analytics with development\test traffic, setup filters to exclude hostname test.mywebsite.com.

                                                           

                                                          To test your Google eCommerce functionality, setup a new view in Google Analytics for your domain, call it "Testing" or something like that and apply the reverse logic; by filter only include traffic from  test.mywebsite.com

                                                          • 27. Re: Solution: Scripts not working after Ajax replaces part of page
                                                            Liam Dilley CommunityMVP

                                                            Glad you guys have solutions, Interest use of .when and .then - Not actually what they are there to use for though. I have a youtube channel I will be doing videos on jQuery, BC and one of the first is what you would use .when for example. But interest use.

                                                             

                                                            The method I will be a git about and not sure to solve all BC dom activity issues is actually so simple. IF your app building for example as well (and if not it is a great way to learn activity, the dom, binding etc) you will lean a lot about binding and not just trying to keep things a live but how you keep states or reactivate states etc.

                                                            I will defiantly be doing  the .when and .then and show everyone the tricks people miss with .on though.