281

How can I include a js file into another js file , so as to stick to the DRY principle and avoid duplication of code.

Community
  • 1
  • 1
Aditya Shukla
  • 12,229
  • 11
  • 34
  • 36
  • 3
    possible duplicate of [Including a .js file within a .js file](http://stackoverflow.com/questions/2145914/including-a-js-file-within-a-js-file) or [this one](http://stackoverflow.com/questions/950087/include-javascript-file-inside-javascript-file) – user113716 Jan 08 '11 at 15:47
  • [This](https://stackoverflow.com/questions/950087/how-do-i-include-a-javascript-file-in-another-javascript-file) appears to be the canonical dupe choice now. – ggorlen Oct 03 '20 at 19:35

4 Answers4

293

You can only include a script file in an HTML page, not in another script file. That said, you can write JavaScript which loads your "included" script into the same page:

var imported = document.createElement('script');
imported.src = '/path/to/imported/script';
document.head.appendChild(imported);

There's a good chance your code depends on your "included" script, however, in which case it may fail because the browser will load the "imported" script asynchronously. Your best bet will be to simply use a third-party library like jQuery or YUI, which solves this problem for you.

// jQuery
$.getScript('/path/to/imported/script.js', function()
{
    // script is now loaded and executed.
    // put your dependent JS here.
});
Matt Ball
  • 332,322
  • 92
  • 617
  • 683
  • 7
    I can only repeat: in case of usage `$.getScript` the second depended script will be started to load **after the first one is loaded and executed**. It is **sequential instead of parallel loading**. Moreover if somebody include per ` – Oleg Jan 09 '11 at 00:05
  • In case of your first suggestion one have to use `imported.onload` to cascade the scripts. But in the case we continue to have the same problems which I described. – Oleg Jan 09 '11 at 00:09
  • 2
    If you will use "Timelines" of Developer Tools of Google Chrome for example, you can verify that http://www.ok-soft-gmbh.com/jqGrid/iedeveloper.htm used `document.writeln` loads scripts parallel and execute (evaluate) in the same correct order. The page is XHTML and so the first the statements from http://stackoverflow.com/questions/802854/why-is-document-write-considered-a-bad-practice are also wrong. So didn't found and reason (which I could verified) **why** it is bad to use `document.writeln` inside of ` – Oleg Jan 09 '11 at 00:47
  • 2
    According to this documentation https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script with HTML5 you can specify async="false" when using document.createElement() to force the load to be synchronous. This would appear to solve the problem with using this method. – Steve Waring May 19 '19 at 19:30
  • However, when I tested this with an alert after the appendChild and an alert as the first line of the inserted script, the alert from the inserted script came out second, so it was not a synchronous load. – Steve Waring May 19 '19 at 19:45
20

I disagree with the document.write technique (see suggestion of Vahan Margaryan). I like document.getElementsByTagName('head')[0].appendChild(...) (see suggestion of Matt Ball), but there is one important issue: the script execution order.

Recently, I have spent a lot of time reproducing one similar issue, and even the well-known jQuery plugin uses the same technique (see src here) to load the files, but others have also reported the issue. Imagine you have JavaScript library which consists of many scripts, and one loader.js loads all the parts. Some parts are dependent on one another. Imagine you include another main.js script per <script> which uses the objects from loader.js immediately after the loader.js. The issue was that sometimes main.js is executed before all the scripts are loaded by loader.js. The usage of $(document).ready(function () {/*code here*/}); inside of main.js script does not help. The usage of cascading onload event handler in the loader.js will make the script loading sequential instead of parallel, and will make it difficult to use main.js script, which should just be an include somewhere after loader.js.

By reproducing the issue in my environment, I can see that **the order of execution of the scripts in Internet Explorer 8 can differ in the inclusion of the JavaScript*. It is a very difficult issue if you need include scripts that are dependent on one another. The issue is described in Loading Javascript files in parallel, and the suggested workaround is to use document.writeln:

document.writeln("<script type='text/javascript' src='Script1.js'></script>");
document.writeln("<script type='text/javascript' src='Script2.js'></script>");

So in the case of "the scripts are downloaded in parallel but executed in the order they're written to the page", after changing from document.getElementsByTagName('head')[0].appendChild(...) technique to document.writeln, I had not seen the issue anymore.

So I recommend that you use document.writeln.

UPDATED: If somebody is interested, they can try to load (and reload) the page in Internet Explorer (the page uses the document.getElementsByTagName('head')[0].appendChild(...) technique), and then compare with the fixed version used document.writeln. (The code of the page is relatively dirty and is not from me, but it can be used to reproduce the issue).

Austin Arnett
  • 64
  • 2
  • 9
Oleg
  • 217,934
  • 30
  • 386
  • 757
7

You need to write a document.write object:

document.write('<script type="text/javascript" src="file.js" ></script>');

and place it in your main javascript file

Mister SirCode
  • 799
  • 9
  • 26
Vahan Margaryan
  • 376
  • 3
  • 3
  • 23
    Ugh, please do **not** advocate using `document.write()`. – Matt Ball Jan 08 '11 at 15:49
  • 13
    @Matt Ball: you are right, but since Vahan is new here, you might wanna explain why. – Konerak Jan 08 '11 at 15:51
  • 12
    @Konerak: [here's why](http://stackoverflow.com/questions/802854/why-is-document-write-considered-a-bad-practice). – Matt Ball Jan 08 '11 at 15:52
  • 3
    +1 I disagree with the critic of `document.write`. I write more information in my answer to the question. – Oleg Jan 08 '11 at 21:44
  • @Matt: funny that the top-voted answer on that question advocates the use of document.write for simple things like loading a script. – fretje Mar 09 '11 at 15:01
  • please avoid using document.write is a bad pattern nowadays – Simos Fasouliotis May 18 '17 at 13:04
  • why is the string broken down like that? – 0cd Jun 15 '19 at 23:45
  • yeah, what 0cd said, @VahanMargaryan why are you concatenating the tag? thats not needed at all – Mister SirCode Oct 04 '19 at 19:30
  • The concatenation of the tag might have been because certain older IDEs were confused when they saw the `` in the string, and the IDEs would color the code such that it appeared that all of the javascript proceeding the string was not part of the script at all. I had witnessed this issue firsthand, many years ago in Notepad++. Obviously, IDEs have gotten much better at identifying where the script tag really ends. Good fix. – Cannicide Oct 18 '19 at 22:05
5

It is not possible directly. You may as well write some preprocessor which can handle that.

If I understand it correctly then below are the things that can be helpful to achieve that:

  • Use a pre-processor which will run through your JS files for example looking for patterns like "@import somefile.js" and replace them with the content of the actual file. Nicholas Zakas(Yahoo) wrote one such library in Java which you can use (http://www.nczonline.net/blog/2009/09/22/introducing-combiner-a-javascriptcss-concatenation-tool/)

  • If you are using Ruby on Rails then you can give Jammit asset packaging a try, it uses assets.yml configuration file where you can define your packages which can contain multiple files and then refer them in your actual webpage by the package name.

  • Try using a module loader like RequireJS or a script loader like LabJs with the ability to control the loading sequence as well as taking advantage of parallel downloading.

JavaScript currently does not provide a "native" way of including a JavaScript file into another like CSS ( @import ), but all the above mentioned tools/ways can be helpful to achieve the DRY principle you mentioned. I can understand that it may not feel intuitive if you are from a Server-side background but this is the way things are. For front-end developers this problem is typically a "deployment and packaging issue".

Hope it helps.

Arnab
  • 726
  • 5
  • 14