jFresh™ - Refresh just your Javascript?

We often work on very large ‘ajax’ driven web applications at work for one of our main clients. These are behemoths of the web app. world also everything is loaded via XHR means, with almost zero page refreshes. They are intranet based applications used by thousands of people day in day out.

One aspect that has yet to be implemented in it is a whole history management system that would manage where a user is in the system and allow for full back button, bookmarking and refresh page support (it’s been one of those features that has not been overly requested by the users, and in big dev. projects, it’s all about supply and demand combined with limited resources).

Anyway, back on point, the main impact on the developers that this URL statelessness has, is that clicking refresh anywhere in the application takes you back to the main opening page. This only really causes issues when you’re debugging a JS function that is linked to a UI gesture that only occurs in a deep rooted part of the work flow. This exact situation occurred for me today. I was debugging and adding new functionality to a modal popup system, the quickest number of clicks from entering the application to get a popup was somewhere between 4-7 depending on the route.

So my work flow for the first 2 hours of today was:

  • Open local running build of application
  • Click 7 times, with a pause between each as new chunks of the UI loaded.
  • Popup appears with bug
  • Edit a line of JS in the war file of the app.
  • Click refresh.
  • Click 7 times, with a pause between each as new chunks of the UI loaded.
  • Popup appears with bug (only this time it’s 1 line less of a bug)
  • Repeat for 2 hours.

Now this was somewhat frustrating, usually we try to setup test beds for all the JS widgets we work on, but as usual it’s only once everything is in the page and all the JS is firing and the server is involved that the real bugs come out, so I needed the context to work on it.

I guess I could have gone in and hacked my main page to include the popup launcher, but again, it’s not a real contextual situation in which to debug, and anyway I have and ton of other bugs for popups that are 5-20 clicks further into the application, hacking them all onto the main page was not going to be a long term goal.

What I needed was a way to have a JS refresh button in firebug (if someone posts saying there is one, I will be very happy and somewhat angry) in the absence I needed an alternative.

As Usual Dojo has the answer

I looked to Dojo for my inspiration. I know that Dojo has a whole JS loading system:

  1. dojo.require(‘dojo.widget.crazyFaceFun’);
  2.  

Will try to load the content of dojo/widget/crazyFaceFun.js into the browser while the page is loading.

This is part of the Dojo bootstrap system, which can be found in the dojo.js file at the root of the whole library…

  1. for(var x=0; x < tmps.length; x++){
  2.             var spath = loaderRoot+"src/"+tmps[x];
  3.             if(isRhino||isSpidermonkey){
  4.                 load(spath);
  5.             } else {
  6.                 try {
  7.                     document.write("<scr"+"ipt type=’text/javascript’ src=’"+spath+"’></scr"+"ipt>");
  8.                 } catch (e) {
  9.                     var script = document.createElement("script");
  10.                     script.src = spath;
  11.                     document.getElementsByTagName("head")[0].appendChild(script);
  12.                 }
  13.             }
  14.         }

Some digging around found the JS to achieve this in Firefox was very simple. Add to that the wonderfulness of the firebug JS console and you get a nice little solution:

  1. var script = document.createElement("script");
  2. script.src = "http://localhost.com/yourApplication/script/javscript.js";
  3. document.getElementsByTagName("head")[0].appendChild(script);

And that’s it. Paste that into the console, with the correct path to your JS and the JS file will reload overwriting the existing code and allowing you to refresh just the JS and not the actual page.

Still to be tested.

I’ll put together a test page with a couple of links that fire off reloads of a couple of different JS file setups. One of the things I’m curious to see is how it handles overwriting existing objects. We work in a very namespaced world on this application, there is 1 global object and everything hangs under that. For that reason I have so far not had issues with objects under than namespace not being overwritten if they are removed. But I’d be curious to see if you loaded a JS file that contained.

  1. foo = function(arg){
  2.  
  3. alert("foo: "+arg);
  4.  
  5. }
  6.  
  7. foo("bar");

and then edited it and reloaded it as:

  1. foo2 = function(arg){
  2.  
  3. alert("foo version2: "+arg);
  4.  
  5. }
  6.  
  7. foo("bar");

Is the old foo() function is still there? I see no reason why it wouldn’t be. Which wouldn’t be great if your debugging and part of your solution was to delete a function or two.

But what about if you namespace it under a global object and do:

  1. foo={
  2.     bar: function(arg){
  3.         alert("foo.bar: "+arg);
  4.     }
  5. }
  6.  
  7. foo.bar("boing");

and then reload the same JS file now edited to look like:

  1. foo={
  2.     bar2: function(arg){
  3.         alert("foo.bar: "+arg);
  4.     }
  5.  
  6.  
  7. foo.bar("boing");

Is the original foo.bar function is still there in that object, or if the object has been overwritten in it’s entirety?

I think only a test will tell, I’m off to set those up and see. BRB….

Update: OK here’s the tests

OK, threw together a quick test to see if overwriting a namespace cleared out methods and if removing a global function and reloading a JS file had any impact.

Not overly surprising, reloading the JS as described above does not remove global functions that were previoudly defined and then removed in the reloaded file.

But reloading an object that has a function method removed does overwrite the old one.

So as long as you are aware of this caveat and are working in a mainly namespaced world, you should be safe.

Oh and before I forget, this of course does not refresh things like onload events or the like. For those things I’m afraid you’ll have to do traditional refreshes, or find an alternative combination of clicks and JFresh™ to get those events to fire.


About this entry