How do I include a JavaScript file inside another JavaScript file, similar to @import in CSS?
72 Answers
My general solution taken from the efekt.js.st library from EdgeS (which I authored).
shameless plug alert - I am on other stackexchange network sites. This is a relink of
https://codereview.stackexchange.com/questions/263764/dynamic-load-css-or-script.
What code or design would you use to support dynamic-loading of css and scripts?
Requirements
- support promise-await-async including error-handling
- support load-once caching, including reloading
- support load in
head,body, or currentscript-element - support load
css,js,mjsmodules or other script-types - support other tag attrs, like
nonce,crossorigin, etc
static loadScriptOrStyle(url, options) { // provenance :<# **Smallscript EdgeS efekt** `efekt.js.st` github libraries #> // returns :<Promise#onload;onerror> // options :<# `fIgnoreCache`, `fAppendToHead`, `fUrlIsStyle`, `attrs:{}` #> const head = document.head; let node = options?.fAppendToBody ? document.body : head; const url_loader_cache = document.head.url_loader_cache ? head.url_loader_cache : (head.url_loader_cache = {script:{},link:{}}) const kind = (options?.fUrlIsStyle || /\.css(?:(?:\?|#).*)?$/i.test(url)) ? 'link' : 'script'; // check already-loaded cache if(url_loader_cache[kind][url]) { const el = url_loader_cache[kind][url]; // support `fIgnoreCache` reload-option; should not use on `head` if(options?.fIgnoreCache) el.remove(); else return(new CustomEvent('cache',{detail:el})); } // (re)create and record it const self = document.currentScript; const el = url_loader_cache[kind][url] = document.createElement(kind); const append = (!self || options?.fAppendToHead || options?.fAppendToBody) ? el => node.appendChild(el) : el => self.parentNode.insertBefore(el, self); const load = new Promise((resolve, reject) => { el.onload = e => {e.detail = el;resolve(e)}; el.onerror = e => {e.detail = el;reject(e)}; // `onload` or `onerror` possibly alter `cache` value // throw(new URIError(`The ${url} didn't load correctly.`)) }); // configure `module` attr, as appropriate if(/\.mjs(?:(?:\?|#).*)?$/i.test(url)) el.type = 'module' // configure other attrs as appropriate (referrer, nonce, etc) for(const key in options?.attrs) {el[key] = attrs[key]} // trigger it if(kind === 'link') el.rel = 'stylesheet', el.href = url; else el.src = url; append(el); return(load); } Comments
If you want to have a single link to all your javascript functions which are located in different files, you can do something like this using php:
Create a php file that will store all your javascript functions/triggers javascript.php and make sure its a .php file:
in your <head> section
<script src="/directory/javascript.php"></script> Let say you have a folder called /functions where all the javascript files are located. Within your javascript.php, create a header with content type application/javascript, and use php glob to include all the javascript files/scripts like so:
javascript.php
<?php header("content-type:application/javascript"); foreach(glob("/functions/*.js") as $filename){include($filename);} ?> Now you can just create separate javascript files and add them to your /functions folder, or whatever you may call it. Those will be automatically included into the javascript.php file that is actually a functioning js file which you can link in your <head> section.
Comments
This is the easiest solution. Use a bundler like Vite.js and then simply do:
import "./path/to/js/file"; That's it! The OP has asked for something like "@import in CSS" and this is exactly like that. It is also not as rocket science complex as some of the old methods. It is at least undoubtedly the most beginner-friendly method, but I'm sure non-beginners do like it as well.
To get started with Vite for a vanilla JavaScript project, just have Node.js and [NPM3 installed, and then do:
npm create vite@latest <your-vanilla-js-app-name> --template vanilla E.g.:
npm create vite@latest my-js-app --template vanilla Now add imports like mentioned at the beginning of this answer and call it a day.
Just as a side note: Also another thing that might pop into your mind is namespacing issues, e.g., what if a name you have used in a file you're including is similar to a name you already have in your current file? But that's the nature of JavaScript, right? It's not an issue specific to this method.
So you'll need to devise strategies for handling that separately. There's a comprehensive article on Addy Osmani's blog in case you want to learn more about that: Design Patterns for Handling the Global Namespace in JavaScript.
1 Comment
npm run dev as well in case it doesn't start the server for you automatically. Notice2: Of course Vite.js is more than just a bundler, refer to their website if you want to know more: vitejs.dev. Notice3: I was the 5959th upvote on this question, it's pointless, but it's funny :DOn the back-end, you can use CommonJS modules. For example:
//a.js function func () { var result = "OK Bro"; return result; } module.exports = { func }; //b.js var a = require('./a.js'); console.log(a.func); Comments
I did not see an answer whereby you create an object of all functions and variables in a file and then make that object an argument to refer to it in another file.
E.g., you have files called 'jsMod.js', 'jsView' and 'jsContr.js':
File jsMod.js
JSMODOBJ = {}; JSMODOBJ.valueAddition = function(/* element value 1 */ val1, /* element value 2 */ val2) { return val1 + val2; } File jsView.js
JSVIEWOBJ = {}; JSVIEWOBJ.elementColour = function(/* element id to change colour */ id, /* css colour classname */ col) { document.getElementById(id).className = col; } File jsContr.js
JSCONTROBJ = {}; var jsMod = JSMODOBJ; var jsView = JSVIEWOBJ; JSCONTROBJ.changeColourByValue = function (val1, val2, id, clss) { if (jsMod.valueAddition(val1,val2) !== 0) { jsView.elementColour(id, clss); } } Then you can set the .js files dynamically by echoeing the scripts into your .html or .php file:
<?php echo "<script src = './js/dleafView.js'></script> <script src = './js/dleafModule.js'></script> <script src = './js/dleafContr.js'></script>"; ?> Then just call the control function within a <script type="text/javascript"></script> tag. Of course, this will take a lot of time in the beginning to set up, but it saves you time in the long run.
I use this in a slightly different way, but this way also works.
Comments
You can also use gulp, gulp-concat, gulp-typescript with /// <reference path= includes:
File packages.json
{ "scripts": { "gulp": "gulp main" }, "dependencies": { "@types/gulp": "^4.0.6", "@types/gulp-concat", "@types/gulp-typescript", "gulp": "^4.0.2", "gulp-concat": "^2.6.1", "gulp-resolve-dependencies": "^3.0.1", "gulp-typescript": "^6.0.0-alpha.1", "typescript": "^3.7.3" } } File src/someimport.ts
class SomeClass { delay: number; } File src/main.ts
/// <reference path="./someimport.ts" /> someclass = new SomeClass(); someclass.delay = 1; This main Gulp.js task (on gulpfile.js) targets only the src/main.js file, resolving all its /// <reference path=... include references. These includes are know as Triple-Slash Directives, and they are used only for transpilers tools to combine files. In our case, they are used explicitly by .pipe(resolveDependencies({ and by TypeScript itself when checking the file for missing types, variables, etc.
Refer to gulp-typescript if you would like to customize the var tsProject = ts.createProject call and not use a tsconfig.json file or override its parameters.
File gulpfile.js
var gulp = require("gulp"); var concat = require('gulp-concat'); var resolveDependencies = require('gulp-resolve-dependencies'); var ts = require("gulp-typescript"); var tsProject = ts.createProject("tsconfig.json"); gulp.task("main", function() { return gulp .src(["src/main.ts"]) .pipe(resolveDependencies({ pattern: /^\s*\/\/\/\s*<\s*reference\s*path\s*=\s*(?:"|')([^'"\n]+)/gm })) .on('error', function(err) { console.log(err.message); }) .pipe(tsProject()) .pipe(concat('main.js')) .pipe(gulp.dest("build/")); }); Pure javascript version
If you would like to target all your TypeScript project files instead of only src/main.ts, you can replace this:
return gulp .src(["src/main.ts"]) .pipe(resolveDependencies({ ... // --> return tsProject .src() .pipe(resolveDependencies({ ... If you do not want to use TypeScript, you can use this simplified gulpfile.js and remove all TypeScript includes from package.json:
File gulpfile.js
var gulp = require("gulp"); var concat = require('gulp-concat'); var resolveDependencies = require('gulp-resolve-dependencies'); gulp.task("main", function() { return gulp .src(["src/main.js"]) .pipe(resolveDependencies({ pattern: /^\s*\/\/\/\s*<\s*reference\s*path\s*=\s*(?:"|')([^'"\n]+)/gm })) .on('error', function(err) { console.log(err.message); }) .pipe(concat('main.js')) .pipe(gulp.dest("build/")); }); File packages.json
{ "scripts": { "gulp": "gulp main" }, "dependencies": { "gulp": "^4.0.2", "gulp-concat": "^2.6.1", "gulp-resolve-dependencies": "^3.0.1" } } Then, after running the command npm run gulp, the file build/main.js is created with the following as its contents:
File build/main.js
class SomeClass { } /// <reference path="./someimport.ts" /> someclass = new SomeClass(); someclass.delay = 1; Which allows me to include it in the browser with the script tag, after serving the build directory files:
<html> <head> <script src="main.js"></script> </head> <body> <script type="text/javascript"> console.log(someclass.delay); </script> </body> </html> Related questions:
- TypeScript: Gulp
- Can I use TypeScript without RequireJS?
- Simple concatenation of main file that requires another JavaScript file using Gulp.js
- Client on Node.js: Uncaught ReferenceError: require is not defined
- How can TypeScript browser Node.js modules be compiled with Gulp.js?
- Concatenate files using babel
- How can I 'require' CommonJS modules in the browser?
- Is there an alternative to Browserify?
Comments
include js internal url path
function createElement(urlPath) { let s = document.createElement("script"); s.url = urlPath document.body.appendChild(s); } function includeMoreJsPath(jsFilePath) { for(let path of jsFilePath){ createElement(path) } } includeMoreJsPath(["/path/to/some/file.js","/path/to/some/file2.js"]); /* <script src="/path/to/some/file.js"></script> <script src="/path/to/some/file2.js"></script> */ include the external js url path
or you can eval directly from text code
function createElement(code) { let s = document.createElement("script"); s.appendChild(document.createTextNode(code)); document.body.appendChild(s); } async function includeMoreJsPath(jsFilePath) { for(let path of jsFilePath){ let code = await(await fetch(path)).text() createElement(code) } } includeMoreJsPath(["'https://cdn.jsdelivr.net/momentjs/2.17.1/moment.min.js'"]); /* <script> ...code... </script> */ Comments
You can just use the require(); tag.
For example, if I had a addition.js module that I wanted to add to math.js, I would do this:
//this is math.js //vars let a = 1; let b = 3; //start of code const additionfile = require('addition.js'); window.alert("You added " + a + " and " + b + " together, to get " + additionfile.add(a,b) + "!"); if you wanted the addition.js file, it would look something like this
function add(a,b) { const sum = a + b; return sum; } 3 Comments
Uncaught ReferenceError: require is not definedmodule.exports = { add }; in the end of the second (required) file. Here is a in detail explantation: linkstanleyulili.com/node/…