Tuesday, April 17, 2012

jquery : trigger $document.ready (so AJAX code I can"t modify is executed)


My requirements are the following:



  • I've got a rich webpage that at a certain moment loads a bunch of html on a div, via AJAX.

  • The HTML I retrieve does have javascript (...)

  • The retrieved javascript contains $('document').ready( ... ) parts

  • I can not modify the retrieved javascript; it comes from an external lib





  • I've got a javascript function that is called when the AJAX is loaded. I'm trying to "trick it" into executing by doing:



    function AjaxLoaded() { $('document').trigger('ready'); }





That doesn't cut it, I'm afraid.



I've seen several responses on stackoverflow that "evade" this question by changing the code that is returned on the ajax (make it a function and call it after loading, or just remove the $(document).ready() ). I need to stress out that I can't change the retrieved code on this case.


Source: Tips4all

5 comments:

  1. Afer some research i created a way to get it to work.

    here is my test that shows it working: http://www.antiyes.com/test/test2.php

    here is the relevant code:

    <script type="text/javascript" charset="utf-8">

    Array.prototype.copy = function() // easy copy of an array
    {
    return [].concat(this);
    };

    // this function is added to jQuery, it allows access to the readylist
    // it works for jQuery 1.3.2, it might break on future versions
    $.getReadyList = function()
    {
    if(this.readyList != null)
    this.myreadylist = this.readyList.copy();
    return this.myreadylist;
    };

    $(document).ready(function(){
    alert("blah");
    });

    </script>

    <script type="text/javascript" charset="utf-8">

    // this should be added last so it gets all the ready event
    $(document).ready(function()
    {
    readylist = $.getReadyList();
    });

    </script>


    then in the body I have:

    <input type="button" onclick="$(readylist).each(function(){this();});" value="trigger ready" />


    basically what i did was add a function to jQuery that copies the readyList before it's cleared out, then it will be available to be used by you.

    it looks like the code below doesnt work:

    function AjaxLoaded() { $(document).trigger('ready'); }


    drop the quotes around document.

    ReplyDelete
  2. Just in case anyone needs it, I refined John's solution a bit so it could be used directly as an included javascript file.

    // jquery_trigger_ready.js
    // this function is added to jQuery, it allows access to the readylist
    // it works for jQuery 1.3.2, it might break on future versions
    $.getReadyList = function() {
    if(this.readyList != null) { this.myreadylist = [].concat(this.readyList); }
    return this.myreadylist;
    };

    $(document).ready(function() {
    readylist = $.getReadyList();
    });

    $.triggerReady = function() {
    $(readylist).each(function(){this();});
    }


    Including this file after including jquery allows for triggering ready by invoking $.triggerReady(). Example:

    <html>
    <head>
    <title>trigger ready event</title>
    <script src="test2_files/jquery-1.js" type="text/javascript"></script>
    <script src="jquery_trigger_ready.js" type="text/javascript"></script>
    </head>
    <body>
    <input onclick="$.triggerReady();" value="trigger ready" type="button">
    <script type="text/javascript">
    $(document).ready(function(){
    alert("blah");
    });
    </script>
    </body>
    </html>


    By the way, I wanted to make it $(document).triggerReady(). If anyone is willing to share some advice on that, ill be appreciated.

    ReplyDelete
  3. We had the same problem and solved it another way.

    Instead of

    $(document).ready(function () {
    $('.specialClass').click(....


    We used :

    $(document).bind('ready', function(event) {
    $('.specialClass', event.target).click(..


    jQuery will trigger a "ready" event on the document as usual. When we load the content of a new div via ajax, we can write:

    loadedDiv.trigger('ready')


    And have all the initialization performed only on the div, obtaining what expected.

    ReplyDelete
  4. Since the jQuery readyList is not exposed as of version 1.4 (discussed here) the nice solutions above are broken.

    A way around this is by creating your own readyList, through overriding the original jQuery-ready method. This needs to be done before other scripts that use the original ready method are loaded. Otherwise just the same code as John/Kikito:

    // Overrides jQuery-ready and makes it triggerable with $.triggerReady
    // This script needs to be included before other scripts using the jQuery-ready.
    // Tested with jQuery 1.7
    (function(){
    var readyList = [];

    // Store a reference to the original ready method.
    var originalReadyMethod = jQuery.fn.ready;

    // Override jQuery.fn.ready
    jQuery.fn.ready = function(){
    if(arguments.length && arguments.length > 0 && typeof arguments[0] === 'function') {
    readyList.push(arguments[0]);
    }

    // Execute the original method.
    originalReadyMethod.apply( this, arguments );
    };

    // Used to trigger all ready events
    $.triggerReady = function() {
    $(readyList).each(function(){this();});
    };
    })();


    I'm not sure whether it is advisable to override the ready method. Feel free to advise me on that. I have not yet found any side effects myself though.

    ReplyDelete
  5. Simone Gianni's Answer I think is the most elegant and clean.

    and you can even simplify it to become even more easy to use:

    jQuery.fn.loadExtended = function(url,completeCallback){
    return this.load(url,function(responseText, textStatus, XMLHttpRequest) {
    if (completeCallback !== undefined && completeCallback !== null) {
    completeCallback(responseText, textStatus, XMLHttpRequest);
    }
    $(this).trigger("ready");
    });
    };


    So, now instead of using:

    $(".container").load(url,function(responseText, textStatus, XMLHttpRequest) {
    $(this).trigger("ready");
    });


    you can just use:

    $(".container").loadExtended("tag_cloud.html");


    or:

    $(".container").loadExtended("tag_cloud.html",function(){
    alert('callback function')
    });


    This has the advantage of only applying the trigger on the div that's being updated.

    ReplyDelete