hostgator coupon July 2014 hostgator coupon 2014 June Hostgator coupon June 2014 Dreamhost promo code 2014 our business news > first class business
Joseph Smarr » Fixing eval() to use global scope in IE

Main menu:


Add to Google

Subscribe via e-mail:

insurance types cosmetics today fitness animals automobile reviews business money buy jewellery finance loans home helps> insurance companies in market finance news medical product money us technology in finance time roof repairing places where cook recipes business ideas business pay buy insurance car price in compamy marketing company guide cosmetics product diet info healthy live samples of business plan home decoration tech news auto insurance home improvement online business tips personal loans product reviews security types top business list top company list home tricks weight loss help what kind of business should i start business letter format business case template general business business type it security what is a good business to start health loss business me magazine news

business ideas

Site search

Categories

January 2007
M T W T F S S
    Feb »
1234567
891011121314
15161718192021
22232425262728
293031  

Archive

Fixing eval() to use global scope in IE

[Note: This is the first in what I hope will become a series of technical articles on the lessons I've learned "from the trenches" of my web development work at Plaxo. Non-techy readers are invited to skip any articles categorized under "Web development". :)]

Update: This article has been picked up by Ajaxian, and it’s sparked an interesting discussion there. 

At Plaxo I’ve been working on a new (soon to be released) version of Plaxo Online (our web-based address book, calendar, and more) that is very ambitious both technically and in terms of user experience. We’re currently deep into performance tuning and bug fixing, and we’ve already learned a lot of interesting things, most of which I hope to share on this blog. The first lesson is how to correctly eval() code in the global scope (e.g. so functions you define inside the eval’d code can be used outside).

When we built the first version of the new site, we combined all the JavaScript into one giant file as part of our deployment process. The total codebase was huge and it had the predictable effect that initial page-load time was terrible because the user’s CPU was solidly spiked for several seconds while the poor browser choked through the massive amount of code it had to parse. So we started loading a lot of our code on-demand (packaging it into several logical chunks of related files and using dojo’s package/loader system to pull in the code as needed).

All was well until we started defining global functions in the loaded JavaScript. (We did this mainly for event handler code so we didn’t have to spend time walking the DOM and finding all the clickable nodes after injecting innerHTML to hook them up to the right scoped functions.) In Firefox, everything kept working fine, but in IE, none of the global functions were callable outside of the module being loaded on-demand (you would get a typically cryptic IE error that in effect said those global functions weren’t defined). It seemed clear that when the code being loaded got eval’d, the functions weren’t making it into the global scope of the page in IE. What was unclear was how to fix this.

Here’s a simplified version of the situation we faced:

function loadMyFuncModule() {
  // imagine this was loaded via XHR/etc
  var code = 'function myFunc() { alert("myFunc"); }';
  return eval(code); // doesn't work in FF or IE
}

function runApp() {
  loadMyFuncModule(); // load extra code "on demand"
  myFunc(); // execute newly loaded code
}

The thing to note above is that just calling eval() doesn’t stick the code in global scope in either browser. Dojo’s loader code solves this in Firefox by creating a dj_global variable that points to the global scope and then calling eval on dj_global if possible:

function loadMyFuncModule() {
  // imagine this was loaded via XHR/etc
  var code = 'function myFunc() { alert("myFunc"); }';
  var dj_global = this; // global scope object
  return dj_global.eval ? dj_global.eval(code) : eval(code);
}

This works in Firefox but not in IE (eval is not an object method in IE). So what to do? The answer turns out to be that you can use a proprietary IE method window.execScript to eval code in the global scope (thanks to Ryan “Roger” Moore on our team for figuring this out). The only thing to note about execScript is that it does NOT return any value (unlike eval). However when we’re just loading code on-demand, we aren’t returning anything so this doesn’t matter.

The final working code looks like this:

function loadMyFuncModule() {
  var dj_global = this; // global scope reference
  if (window.execScript) {
    window.execScript(code); // eval in global scope for IE
    return null; // execScript doesn't return anything
  }
  return dj_global.eval ? dj_global.eval(code) : eval(code);
}

function runApp() {
  loadMyFuncModule(); // load extra code "on demand"
  myFunc(); // execute newly loaded code
}

And once again all is well in the world. Hopefully this is the type of thing that will be hidden under the hood in future versions of dojo and similar frameworks, but for the time being it may well impact you if you’re loading code on demand. So may this article save you much time scratching you head and swearing at IE. :)

(PS: Having found the magic term execScript, I was then able to find some related articles on this topic by Dean Edwards and Jeff Watkins. However much of the details are buried in the comments, so I hope this article will increase both the findability and conciseness of this information).

  • dave t

    Don’t quite understand what you are dealing with innerHTML. Are you dealing with the fact that tags don’t get evaluated when included as part of innerHTML?

  • http://www.josephsmarr.com jsmarr

    Dave-the issue is that I’m building my HTML by building up a big string of HTML and then injecting it into the DOM using innerHTML, because that’s much faster that building using DOM methods (appendChild, etc.). But one problem with using innerHTML is that if I have instantiated objects with methods I want to call when an event happens (e.g. when you click on a contact, I want to call something like this.getContactManager().setContactSelected(true)) you have to wire those calls up AFTER you inject the innerHTML (because you’re just injecting HTML, so there’s no scope or object reference available).

    We used to solve this problem by giving each node we wanted to attach an event handler to a unique className, and then after we called innerHTML we would use getElementsByClassName to find the newly created element in the DOM and hook up the event with JavaScript. That’s an elegant solution, but it’s too slow if you’re doing it a lot. So we decided to use global functions (e.g. g_plx_setContactSelected(‘contact-id-here’)) instead, since those could be written directly into the innerHTML and the global function could then find the instantiated object to call the method on when it was invoked.

    This was great until we realized that it wasn’t working in IE, because the code where the global function was defined was being loaded on-demand, and since we weren’t using the global-scope eval (execScript) trick I mention above, those functions weren’t ending up defined as event handlers. The global-eval trick fixes the problem though, and I’m now quite happy with our setup (load code on-demand, build the UI with innerHTML, no walking the DOM to hook up events, still get to use instantiated objects and their instance methods).

    I hope that clears things up!
    Thanks, js

  • Daniel

    Don’t swear at IE too much. What you’re doing isn’t standard ECMAscript and is deprecated in Javascript.

  • http://trevinchow.com/blog Trevin

    Hey Joseph, long time no see. Good to see you’re still up to your old tricks and hacking away at code :) Saw some great coverage on the Plaxo widget so congrats!

  • Pingback: Joseph Smarr » Handling GeoCoding Errors From Yahoo Maps

  • mano

    the loader is async…

  • http://www.yessoftware.com infous

    Why don’t you use window.eval(code) instead of dj_global.eval(code)? thinks, it can be use even inside object’s method, but if you use “dj_global = this”, “this” is a current object, not window.
    BTW, thanks a lot! good article :)

  • Pingback: Perfection kills » Global eval. What are the options?

  • http://pharma911.co.tv Kiler

    Wow this is a great resource.. I’m enjoying it.. good article

  • 6465876786

    http://www.dafdfsa.info/   [url=http://www.dafdfsa.info/]fdsg[/url]   dafd