Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
Locked
0

Solution: Scripts not working after Ajax replaces part of page

Engaged ,
Apr 18, 2012 Apr 18, 2012

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.
TOPICS
How to
22.2K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 09, 2013 Jan 09, 2013

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jan 09, 2013 Jan 09, 2013

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 09, 2013 Jan 09, 2013

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?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jan 09, 2013 Jan 09, 2013

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 10, 2013 Jan 10, 2013

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 10, 2013 Jan 10, 2013

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jan 10, 2013 Jan 10, 2013

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 10, 2013 Jan 10, 2013

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jan 10, 2013 Jan 10, 2013

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jan 10, 2013 Jan 10, 2013

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 10, 2013 Jan 10, 2013

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jan 10, 2013 Jan 10, 2013

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?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 10, 2013 Jan 10, 2013

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 10, 2013 Jan 10, 2013

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()?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jan 10, 2013 Jan 10, 2013

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 19, 2013 Jan 19, 2013

@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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jan 19, 2013 Jan 19, 2013

Your getting there Adam

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 19, 2013 Jan 19, 2013

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jan 21, 2013 Jan 21, 2013

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jul 21, 2014 Jul 21, 2014

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 21, 2014 Jul 21, 2014

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);

        }

});

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jul 21, 2014 Jul 21, 2014

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 21, 2014 Jul 21, 2014

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jan 19, 2013 Jan 19, 2013

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines