0

In order to load some geojson data, I need to source scripts on an external domain, say http://www.stat.ucla.edu/~jeroen/files/la_county_simplified.min.json. I have no control over the contents of this script; all I know is the url, and the name of an object defined in the script that I am interested in. A dummy version of the script looks like:

var my_data = {"foo" : 123, "bar" : 456}

Now in my application, I would like to load the my_data object dynamically from its URL. Because it is cross domain, I can't use ajax. It isn't quite jsonp either, because my script defines an object, not a function. One way would be to insert it simply as a <script> in the head of the current document. However, I would like to avoid possible side effects.

What would be a cleaner solution? I was thinking of creating an <iframe> and then inserting the <script> tag in the iframe, and extracting the object once the iframe has loaded. However I am not sure this is a reliable method that will work cross browsers (especially binding a callback to extract the object after the script has been loaded in the iframe).

Is there some library or standard solution to load a script in a clean page, and extract copy over a particular object to the main page? I already have a dependency on jQuery so that would be fine.

Jeroen Ooms
  • 31,998
  • 35
  • 134
  • 207

4 Answers4

2

If you plan to do this pure client-side and can't format your data, you could use JSONP with a twist. Instead of modifying the data to fit the callback, we refit the loader to adopt to the data.

We listen for the onload of the script. When the script loads, the variable should now be in the global scope and we execute our callback, which passes that global variable into our callback.

//your script loader
function loadData(url,varName,callback){

  var script = document.createElement('script');
  document.getElementsByTagName('head')[0].appendChild(script);

  //when the script loads, we pass in `window.my_data`
  script.onload = function(){
    callback.call(this,window[varName]);
  };
  script.src = url;

}

//using it
loadData('http://example.com/somefile.js','my_data',function(data){
  //executes when script is loaded, where data is `my_data`
});

The drawback of this approach is that every time you are loading the script, you are loading it into the global scope, and collisions could happen.

Joseph
  • 117,725
  • 30
  • 181
  • 234
  • Thanks. Would it be safer to do this in an iframe or so? I want to avoid problems if any of such scripts were to contain something invalid or a nameclash. – Jeroen Ooms Apr 23 '13 at 00:05
  • @Jeroen If the files live in another domain, only CORS or JSONP can do that. AJAX is limited to same-domain, frames are limited to same-top-domain. – Joseph Apr 23 '13 at 00:08
  • @JosephtheDreamer you beat me to it, if this page loads data multiple times you need to clean up the script element and the onload handler. Jeroen: you can't use iframe because your page can't access the iframe since it's source is not the same as the page trying to access it. – HMR Apr 23 '13 at 00:11
  • @Jeroen I updated the answer. I forgot about a method, but will require your server as your "middleman". – Joseph Apr 23 '13 at 00:15
  • @JosephtheDreamer I was actually thinking about creating an iframe with src at the current domain. And then have javascript insert the (cross-domain) script tag in `iframe.contentDocument`. Would that also violate SOP? – Jeroen Ooms Apr 23 '13 at 00:18
  • @Jeroen should work. [see this thread](http://stackoverflow.com/q/10622061/575527). – Joseph Apr 23 '13 at 00:30
  • OK I think I got it to work. JSFiddle based on your solution: http://jsfiddle.net/9MHYL/ – Jeroen Ooms Apr 23 '13 at 00:48
  • @Jeroen to get this to work cross-browser, don't use .onload, but rather addEventListener/attachEvent or jQuery on(). But again, this is just doing what you said you wanted to avoid, add a script to your document head! – Christophe Apr 23 '13 at 01:29
  • @Christophe well the script is hadded to the head of the iframe document. Not my actual application document. And it still doesn't work in firefox: http://jsfiddle.net/qUE3J/ – Jeroen Ooms Apr 23 '13 at 03:26
  • @Jeroen the script doesn't seem to get appended to the head of the iframe on Firefox. You might want to check on that. – Joseph Apr 23 '13 at 03:56
  • @Jeroen my comment was about the answer, which doesn't say anything about iframes. Again, this has little to do with JSONP. As for your fiddle, you also need a cross-browser way to access the iframe document. – Christophe Apr 23 '13 at 04:35
0

There is no other way around it since you have to beat the same origin policy you have to load the script in a new script tag, JSONP works this way too but jquery hides it for you.

Either that or the site has cors headers, if the site has no cors headers here is how you can load the data (not using jsonp because it isn't in jsonp format):

function loadJS(url){
  var s=document.createElement("script");
  s.src=url;
  $(s).on("load",function(){
    console.log("var abvailable");//do something with the variable here
    $(s).remove();
  });
  document.head.appendChild(s);
}
loadJS("http://code.jquery.com/jquery-1.9.1.min.js");
HMR
  • 37,593
  • 24
  • 91
  • 160
0

The iframe method should work fine:

  • create an iframe
  • inject a script tag that points to the file
  • on script load, retrieve the object

The only cross-browser issue I can think of is that you'll need to use addEventListener in modern browsers and attachEvent in old IE.

This is a standard use of an iframe as sandbox - if I understand correctly you are worried about possible conflicts with global variable names.

[Update] To address some of your comments, here is some cross-browser code:

To add an event listener:

function addEvent(element,event,fn,capture){
// capture defaults to false if omitted
if (element.addEventListener) {element.addEventListener(event,fn,(capture||false));}
// else for old IE
else {element.attachEvent('on'+event,fn);}
};

To access the iframe document:

function iframeDocument(ifr){
var doc=ifr.contentWindow||ifr.contentDocument;
if (doc.document) doc=doc.document;
return doc;
};

If you use jQuery, .on("load") and $(ifr).contents() will take care of these cross-browser compatibility issues.

Christophe
  • 27,383
  • 28
  • 97
  • 140
-1

JSON-P is a way of loading JavaScript from a remote domain.

The return format of the JavaScript is to invoke a function with the response data as an parameter.

someGlobalFunctionName({/* your response data */});

function someGlobalFunctionName(data) { /* do something with data */ }

Since the data is contained in an object and passed to a function, there is no global leakage other than the global function itself, which is unavoidable.

More info: http://json-p.org/

Rick Viscomi
  • 8,180
  • 4
  • 35
  • 50
  • The external script does not contain a function. It defines an object. I cannot modify the contents of the script. – Jeroen Ooms Apr 22 '13 at 23:56
  • @Jeroen you should ask the owner to make the resource JSON-P compatible. If the script was intended for cross-domain use, this should be a no brainer. – Rick Viscomi Apr 23 '13 at 00:00