views:

98

answers:

3

What is the best practice of activating jquery ui widgets for html loaded and inserted into the document by ajax?

I am an advocate of unobtrusive javascript and strongly believe that all functionality accessed by javascript should be also accessible without it. So in the ideal case, each form which opens in a popup, should also have its separate page and links to them should be replaced with javascript-based ajax loading.

I find this pattern very useful for loading and inserting a part of another page into the current document:

$('#placeholder').load('/some/path/ #content>*');

Or to make it more generic:

$('a.load').each(function() {
    $(this).load($(this).attr('href') + ' #content>*');
});

However, I would also like to activate the javascripts from the dynamically loaded page, so that different widgets function correctly.

I know, I could add those javascripts to the current document and activate all of them in the callback of .load(), or I could use $.get() to get some JSON with html and javascripts separately, but I guess, there might be a more elegant generic solution to this.

What would you recommend?

BTW, I am using Django in the backend.

+1  A: 

The question is how you're activating your javascript currently. If you're doing something like:

$(document).ready(function() {
      $('a.foo').click(function() { ... });
  })

You could consider changin things to:

$(document).ready(function() {
      $('a.foo').live('click', function() { ... });
  })

That way when new DOM objects are loaded the event handlers are attached.

koblas
`.live()` and `.delegate()` are a good solution to custom widgets, but not for jquery ui internals and not for third-party ones.
Aidas Bendoraitis
+1  A: 

What I've done is used the "load" option that is specifiable by jquery.ui widgets. Unfortunately, this isn't well documented, so you won't see the option here: http://jqueryui.com/demos/tabs/#options for example, but you will see it here: http://jqueryui.com/demos/tabs/#method-load

For the most part, each of the methods you invoke have an initial option that can be set, which is what prompted me to try using the load.

In my own application, I have 3 levels of nested tabs that are being created dynamically via AJAX. In order to have the javascript for each of the tabs applied dynamically, I have nested load functions that are first initiated when the document is loaded.

So my template file has:

<script type="text/javascript" src="{{ MEDIA_URL }}js/tabs.js"></script>
<script type="text/javascript">
$(function() {  
    $('.overall_tabs').tabs({
        load: initializeOverallTabs
    });
});
</script>

My tabs.js file has:

function initializeOverallTabs(event, ui){
    ...
    $('.lvl_two_tabs').tabs({
        load: initializeTabLevel2
    });
    ...
}

function initializeTabLevel2(event, ui){
    ...
    // So on and so forth
    ...
}

Also, I recommend when working inside the loaded areas to make your references be specific to that pane. This was extremely important when working with tabs. The best way I found to do this is below.

In your tabs.js file:

function initializeOverallTabs(event, ui){
   $panel = $(ui.panel);
   $panel.find('lvl_two_tabs').tabs(...);
}

I found this question strangely coincidental! I recently explained my solution to a few developers to the same situation with the same Jquery/Django Environment. I hope that helped!

Michael Merchant
A: 

One way I decided myself for handling widgets from external pages is parsing the HTML of the other page, searching for scripts and executing them in the current page.

For example, there is a form with autocomplete widget on another page which is loaded and inserted to this page. The autocomplete widget should be activated with specific properties, like available choices:

<script type="text/javascript">
//<![CDATA[
$(function() {
    $("#colors").autocomplete({
        source: ['red', 'green', 'blue', 'magenta', 'yellow', 'cyan']
    });
});
//]]>
</script>

Then in the current page I can have the following script which loads HTML and additionally collects all javascripts within it and executes them:

var oRe = /<script\b[^>]*>([\s\S]*?)<\/script>/gm;
$('#placeholder').load(
    '/some/path/ #content>*',
    function(responseText, textStatus, XMLHttpRequest) { // <-- callback function
        var sScripts = "";
        responseText.replace(
            oRe,
            function($0, $1) {
                sScripts += $1;
                return $0;
            }
        );
        eval(sScripts);
    }
);

One drawback here is that the current document should initially be loading all the libraries which might appear in the included forms. For example, in this case, it would be the jquery-ui including the autocomplete widget. I guess I could extend the code by searching for script tags which load external scripts and loading them in the current document if they are not present.

Aidas Bendoraitis