Fork me on GitHub

Drew's World

Rants, News, Etc on my Life and Projects

Ajax Request Generated JavaScript

Hi all. Recently I was working on a web application that utilized JavaScript and AJAX. Who would have guessed. Anyways, I ran into a bit of a snag when I had the AJAX requests result defining a JavaScript snippet. The problem was that AJAX requests strip out script tags and don’t let them get inserted into the DOM tree. I am not sure why this is the case, maybe for some security reason that I have yet to see. Anyways, this creates a bit of a problem when you are trying to do things like use a JavaScript calendar widget inside the AJAX result set when the calendar widget needs to be initialized by some JavaScript code that is unique to that calendar.

Despite all my efforts in googling I failed to find a clearly defined solution. Bits of the sites have clues but none of them had actual solutions with enough detail to make it reproducible. Hence, after exploring all of these hints and all of the combinations of the hints I managed to find a working solution for using JavaScript that is dynamically generated as part of the result of an AJAX call.

Lets assume that you are using Prototype because I was, and it is a very common AJAX JavaScript library. The first thing I looked to find out was if there was an event fired off to the portion that is generated by AJAX request. The answer is, not that I could find. There are events in the top level document but none which really help you. Since, there are no events or callbacks in the child document (one produced by the ajax call and loaded into the top level) and no script tags carried through to the top level document we first have to figure out a way to get the JavaScript code to the top level document and then secondarily have JavaScript evaluate it.

In order to get the script tags content to the top level document I had to create a div that contained the raw content of the script tag that I wanted available. Beyond that the div had to have a unique ID that was able to be obtained programatically in the top level document using JavaScript. An example of this might look as follows (html.erb template of JavaScript):

<div id="<%= calendar_id %>_script" style="display: none;">
  <%= calendar_id %>SelectHandler = function(type, args, obj){
    var selected = args[0];
    var selDate = selected[0][0] + "-" + selected[0][1] + "-" + selected[0][2];
    <% if txt_input_id.nil? %>
      document.getElementById("<%= calendar_namespace %>_<%= calendar_id %>").value = selDate;
      <%= calendar_id %>hide();
    <% else %>
      document.getElementById("<%= txt_input_id %>").value = selDate;
      <%= calendar_id %>hide();
    <% end %>
  }

  <%= calendar_id %>show = function (){
    var cur_z = $('<%= calendar_container %>').style.zIndex;
    if (!cur_z) {
      cur_z = 1;
    } else {
      cur_z = parseInt(cur_z);
    }
    $('<%= calendar_container %>').show();
    $('<%= calendar_container %>').style.zIndex = (cur_z + 1);
  }

  <%= calendar_id %>hide = function(){
    var cur_z = $('<%= calendar_container %>').style.zIndex;
    if (!cur_z) {
      cur_z = 1;
    } else {
      cur_z = parseInt(cur_z);
    }
    $('<%= calendar_container %>').hide();
    $('<%= calendar_container %>').style.zIndex = (cur_z - 1);
  }

  YAHOO.namespace("<%= calendar_namespace %>.<%= calendar_id %>");
  YAHOO.<%= calendar_namespace %>.<%= calendar_id %>.init = function() {
    YAHOO.<%= calendar_namespace %>.<%= calendar_id %>.Cal = new YAHOO.widget.Calendar("<%= calendar_id %>","<%= calendar_container %>", <%= @opts %>);

    YAHOO.<%= calendar_namespace %>.<%= calendar_id %>.Cal.selectEvent.subscribe(<%= calendar_id %>SelectHandler, YAHOO.<%= calendar_namespace %>.<%= calendar_id %>.Cal, true);
    YAHOO.<%= calendar_namespace %>.<%= calendar_id %>.Cal.render();
  }
</div>

This div with a unique id gives me the capability of pulling out the associated raw JavaScript that is the innerHTML content of this div in the top level document and having the top level documents JavaScript evaluate it. This can be done with a JavaScript function like the following:

function createRowEditCalendar(id) {
  var script = document.getElementById("cal_visited_on_" + id + "_script").innerHTML;
  eval(script);
  var my_init_cmd = "YAHOO.namespace_o_hell.cal_visited_on_" + id + ".init();";
  eval(my_init_cmd);
  return true;
}

The next logical question of course is what event or callback launches the above JavaScript function in the top level document. The answer in this case is simply to use the Prototypes Ajax.Updater onComplete callback by having it first programatically figure out the name of the div using the reproducible naming scheme and then call the above JavaScript function passing it the necessary id to access the hidden JavaScript div. This might look something like the following:

<input type="button" id="rowedit_create_button" value="Create" onclick="new Ajax.Updater(<% if prefix %> '<%= prefix %>_rowedit_data_rows' <% else %> 'rowedit_data_rows' <% end %>, '<%= urls[:create] %>', {asynchronous:true, parameters:$(<% if prefix %> '<%= prefix %>_rowedit_create_form' <% else %> 'rowedit_create_form' <% end %>).serialize(), insertion: Insertion.Bottom, onComplete: function() { var drows = document.getElementById('rowedit_data_rows'); var last_row_id = drows.lastChild.id; last_row_id = last_row_id.replace('edit_', ''); last_row_id = last_row_id.replace('_row', ''); createRowEditCalendar(last_row_id); } });"/>

Assuming that you have those three components in place and properly setup with all the syntax correct you should have a working solution. Note: Be very careful of syntax errors in the JavaScript inside the eval() method because FireBug won’t detect them, or run-time errors in the JavaScript anywhere because not all of them will be detected by FireBug. Anyways, hope this helps someone out there, it sure would have helped me if I had it.