« AJAX and Prototype | Main | Kegging homebrew is a total game changer »

November 10, 2005

Some AJAX issues and some workarounds

In working with AJAX (which I should probably start calling AJAH, since my responseText is never XML -- but HTML), I've run across two issues. I was able to get both to work, but I thought there might be something I'm missing so here's my solutions, I'd welcome any feeback.

Scenario #1:
I have a website where the UI consists of a series of tabs. The active tab is loaded normally via the request. The unselected tabs are loaded asynchronously via AJAX into hidden divs. The responseText of one of the AJAX requests contains javascript, some that runs immediately in the context of the request, and some javascript functions that need to be stored in the DOM for later onClick events.

The problem: There's an option to the AJAX.Updater method, evalScripts: true, that you can use to cause any Javascript in the resulting responseText to run, and it works great for code that runs within the page immediately, but any declared functions that work when the page is being loaded, are subsequently lost and not stored in the DOM as expected. So, when you click on the tab, and then click an element intended to fire one of those functions, it complains. I was surprised the evalScripts didn't also load the functions into the DOM, but hey, I'm sure there's a perfectly good explanation. In order to get around this problem, I used the onComplete option in the AJAX.Updater method to run a function that searches the responseText for script blocks, and manually inserts them into the DOM. It works great. When doing this, you don't need to use the evalScripts parameter for other Javascript that needs to run immediately. Here's my function:
function insertScriptsInHead(ajaxObj)  {
    var allScriptsRegexp = /<script type="text/javascript">[\s\S]*?</script>/g;
    var results = ajaxObj.responseText.match(allScriptsRegexp);
    var head = document.getElementsByTagName("head")[0];

    if (results)  {
        // loop through all of the script blocks and append them to the head
        for (var i = 0; i < results.length; i++)  {
            var result = results[i].match(/<script type="text/javascript">([\s\S]*?)</script>/);
            var node = document.createElement('script');
            node.type = 'text/javascript';
            node.text = result[1];
            head.appendChild(node);
        }
    }
}
I should point out that I'm only developing for the latest versions of IE and Firefox, so I don't have any clue how this code might work in older browsers. If anyone has any knowledge of how to get Prototype to do this for me, I would love to hear it.

Scenario #2:
I launch a popup window with some AJAX that is fired via an onClick event in the context of the popup. After the AJAX is complete, I want the popup window to close itself via window.close().

The Problem:
For once in my life, I have to say that this works great in IE, but not in Firefox. In Firefox, a few seconds after failing to close the window, the browser will crash.

I was able to workaround this issue by storing a reference to the window object for the popup in the parent. Then, when the AJAX is complete, the popup calls a function in the parent via this command setTimeout('opener.closePopup()', 1000), which can close the window with something like this, popupWindowObj.close(). I'm not sure why I have to wrapper the opener.closePopup() command in a timout, since the AJAX is complete, but it doesn't work without a slight delay. So basically, instead of the popup closing itself, the opener (parent) does.

I actually had another issue very similar to this, in that a popup window was supposed to close itself, and launch an AJAX update method in the opener. However, this also didn't work, and I got it to work via the method described in this blog:
http://dema.ruby.com.br/articles/2005/05/06/be-careful-when-mixing-ajax-and-popup-windows-on-firefox

Again, if anyone has any feedback, I'd appreciate it. If not, I think that both of my workarounds are acceptable.

Posted by mark at November 10, 2005 4:53 PM Subscribe (FeedBurner)

Comments

Thankyou!!!!! This is brilliant! I was pulling my hair out trying to understand why evalscripts was not persiting anything to the DOM in Mozilla. thankyou

Posted by: James Head at February 3, 2006 6:31 AM

Er, - looks like my actual real problem was: there is no such property as "innerHTML" in mozilla... (!) shows I havent been doing much cross browser development.

Posted by: James Head at February 3, 2006 9:25 AM

This is great. I have this trick really really useful! Here is a more robust impl of your function in case you care :)

function insertScriptsInHead (ajaxObj) {
var results = ajaxObj.responseText.extractScripts( );
var head = document.getElementsByTagName("head")[0];

if (results) {
// loop through all of the script blocks and append them to the head
for (var i = 0; i var node = document.createElement('script');
node.type = 'text/javascript';
node.text = results[i];
head.appendChild(node);
}
}
}

Posted by: Sami at February 6, 2006 9:09 AM

Thanks a lot for the workaround to this close-bug. You made my day.

Christian

Posted by: Christian Kirsch at March 22, 2006 10:57 AM

Hey..this one was superb..
AS per the post I used this method

function insertScriptsInHead (ajaxObj) {
var results = ajaxObj.responseText.extractScripts( );
var head = document.getElementsByTagName("head")[0];
var node;
if (results) {
//loop through all of the script blocks and append them to the head
for (var i = 0; i node = document.createElement('script');
node.type = 'text/javascript';
node.text = results[i];
head.appendChild(node);
}
}
}


and called this method after setting innerhtml property of the div element from ajax response object.

It worked gr8..thanks a lot..

Posted by: Anshu at March 30, 2006 1:09 PM

FYI "node.text = results[i];" fails in Safari. OpenJSAN has a cross-browser implementation:

if (null != node.canHaveChildren) node.text = results[i];
else node.appendChild(document.createTextNode(results[i]));

Posted by: bill at July 28, 2006 3:09 PM

Post a comment




Remember Me?

(you may use HTML tags for style)