5

I wrote JavaScript library to use FileSaver.js and its associated libraries. However, I don't want to always load FileSaver.js whenever someone wants to use my library. And I don't want to force them to load all the various FileSaver related JavaScript libraries with script tags themselves (or even load one of mine which would do that).

Instead, what I'd prefer is something like this. When they call my createImage function, it first does the following:

function createImage(image, name) {
 if (typeof(saveAs) !== 'function') {
 var element = document.createElement('script');
 element.async = false;
 element.src = 'FileSaver.js';
 element.type = 'text/javascript';
 (document.getElementsByTagName('head')[0]||document.body).appendChild(element);
 }
 // now do the saveImage code
}

Problem is, after the above, the saveAs function is still not defined. It's only after my createImage completes is the saveAs function finally defined.

Dan Dascalescu
154k66 gold badges336 silver badges422 bronze badges
asked Nov 4, 2013 at 1:00
1

5 Answers 5

9

the Holistic solution is to use a module system. AMD is (in-my-just-an-observation-please-dont-start-a-holy-war-opinion) probably the most commonly used system for browser async code loading. AMD is just a spec, but something like require.js is a very popular tool for using AMD modules.

The idea being that you can define dependencies between your modules, and require.js will go fetch them if need be. The general idea is to mimic the import/namespace functionality of other languages (like java, C#, or python). "code sharing" i think is the term?

simply put you have all your code in a callback function that runs once the dependencies are loaded, so you can be sure the needed objects and methods are present.

update 2015

just an addendum. while the info above is still correct, front end code management is moving quickly toward solutions like Webpack and Browserify, which bundle and concatenate code of any module type and both have dynamic code loading capabilities (webpack calls this code splitting). That coupled with the exponential growth of npm for dependency management is beginning to make AMD less relevant.

answered Nov 4, 2013 at 2:03
Sign up to request clarification or add additional context in comments.

Comments

1

Alright, what you need to do is listen for the script to finish loading. Unfortunately there are some bugs with this code for ie<7.

This is the way Mootools Asset.javascript loads scripts and calls a callback when its complete:

var loadScript = function (source, properties) {
 properties || (properties = {});
 var script = document.createElement('script');
 script.async = true;
 script.src = source;
 script.type = 'text/javascript';
 var doc = properties.document || document, load = properties.onload || properties.onLoad;
 return delete properties.onload, delete properties.onLoad, delete properties.document, 
 load && (script.addEventListener ? script.addEventListener("load", load) : script.attachEvent("readystatechange", function() {
 [ "loaded", "complete" ].indexOf(this.readyState) >= 0 && load.call(this);
 })), script.set(properties).appendChild(doc.head);
}

Now in loadImage you can load the file library as follows:

function createImage(image, name) {
 function createImg() {
 // now do the saveImage code
 }
 if (typeof(saveAs) !== 'function') {
 loadScript("FileSaver.js", {onLoad: createImg});//load library
 }
 else {
 createImg();
 }
}

Should work on most browsers.

answered Nov 4, 2013 at 1:07

Comments

0

Use Head.js: http://headjs.com/

It will load scripts on demand.

answered Nov 4, 2013 at 1:11

Comments

0

So I agree with the AMD comment (can't put code blocking into comments meh...)

Here's what I do for FileSaver.js

First in my requirejs config / main.js :

(function() {
 // REMEMBER TO DUPLICATE CHANGES IN GRUNTFILE.JS
 requirejs.config({
 paths: {
 "jquery": "PATH/jquery.min", // NO .js
 "lib.filesaver" : "PATH/FileSaver", // NO .js
 "shim.blob" : "PATH/Blob" // NO .js
 },
 shim: {
 "lib.filesaver": {deps: ["shim.blob"]}
 }
 });
 define([
 "jquery"
 ], function(
 $
 ) {
 $(document).ready(function() {
 // start up code...
 });
 return {};
 });
})();

Then I place the Blob.js/jquery and Filersaver in correct places

I also created a IEShim for pre IE10

define([], function () {
 /**
 * @class IEshims
 * container for static IE shim functions
 */
 var IEShims = {
 /**
 * saveFile, pops up a built in javascript file as a download
 * @param {String} filename, eg doc.csv
 * @param {String} filecontent eg "this","is","csv"
 */
 saveAs: function (filename, filecontent, mimetype ) {
 var w = window.open();
 var doc = w.document;
 doc.open( mimetype,'replace');
 doc.charset = "utf-8";
 doc.write(filecontent);
 doc.close();
 doc.execCommand("SaveAs", null, filename);
 }
 };
 return IEShims;
});

And lastly when I want to use Filesaver make it required (along with IEShim for bad browsers)

define([
"lib.filesaver",
"IEShims"
],
function (
 FileSaver, // it's empty, see saveAs global var
 IEShims
 ) {
...
 var fileName = "helloworld.txt";
 var fileContents = "Me haz file contents, K Thx Bye";
 var mimeType = "text/plain";
 if(saveAs) {
 var blob = new Blob(
 [fileContents],
 {type: mimeType + ";charset=" + document.characterSet}
 );
 saveAs(blob, fileName);
 } else {
 IEShims.saveAs(fileName, fileContents,mimeType );
 }
 ...
};
answered Mar 20, 2014 at 9:55

Comments

0

The simplest answer is to put your code in the onload handler of the script tag you create:

<script>
 var firstScript = document.getElementsByTagName('script')[0],
 js = document.createElement('script');
 js.src = 'https://cdnjs.cloudflare.com/ajax/libs/Snowstorm/20131208/snowstorm-min.js';
 js.onload = function () {
 // do stuff with your dynamically loaded script
 snowStorm.snowColor = '#99ccff';
 };
 firstScript.parentNode.insertBefore(js, firstScript);
</script>

Loading scripts dynamically this way is done by Facebook.

answered Jul 8, 2015 at 2:45

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.