I have a page setup which references a couple of javascript files and has some javascript inline in the page, which is generated when the page is loaded.
One of the javascript files contains a class from a third-party library.
The other javascript file contains various variables, event handlers and functions relating to the page, which use the class from the third-party library (var v = new thirdPartyClass(parameters);
).
The third party has recently updated their library and switched to using modules, so now instead of having a class available, they have a class export available. This is my first exposure to js modules.
So now instead of including the js file, as I understand it, I have to import the class from the module. When I try that, I get a console error that only modules can import. I added type="module"
to the <script>
tag for my js file (it was already added to the third party js script tag) and then I could import their class, but now my functions can't be accessed from the page (function Uncaught ReferenceError: myFunction is not defined
). I don't know if events work because it doesn't get that far any more.
Is there any way I can use their new exported class without having to pletely restructure my js file to be a module and change the page to work with it?
I have a page setup which references a couple of javascript files and has some javascript inline in the page, which is generated when the page is loaded.
One of the javascript files contains a class from a third-party library.
The other javascript file contains various variables, event handlers and functions relating to the page, which use the class from the third-party library (var v = new thirdPartyClass(parameters);
).
The third party has recently updated their library and switched to using modules, so now instead of having a class available, they have a class export available. This is my first exposure to js modules.
So now instead of including the js file, as I understand it, I have to import the class from the module. When I try that, I get a console error that only modules can import. I added type="module"
to the <script>
tag for my js file (it was already added to the third party js script tag) and then I could import their class, but now my functions can't be accessed from the page (function Uncaught ReferenceError: myFunction is not defined
). I don't know if events work because it doesn't get that far any more.
Is there any way I can use their new exported class without having to pletely restructure my js file to be a module and change the page to work with it?
Share Improve this question asked Nov 3, 2020 at 15:09 Ben HolnessBen Holness 2,7075 gold badges32 silver badges56 bronze badges 2- It seems the easiest solution would be to transpile their module into a regular script that puts the export in a global variable, e.g. UMD format. Then just use that and you don't have to change anything else. – Bergi Commented Nov 3, 2020 at 15:47
- @Bergi - Do you have any remendations for tutorials on how to do that please? This is all new to me, I'm mostly a backend developer. – Ben Holness Commented Nov 3, 2020 at 15:52
4 Answers
Reset to default 7You can create a script type module, import the objects desired and put them in a window.{the name of your obj} as:
<script type="module">
import { theItem } from "/path/to/module.js";
window.theItem = theItem;
</script>
The remaining code can be in regular js.
You can't create global variables in modules using var
. To access the function you can export it out of the file:
export myFunction;
and import it into the file you need it in:
import {myFunction} from "./relativeFilePath";
Or if you want to create a global variable from within the module you can put the variable directly into the window
object:
window.myFunction = function() {
//code
};
So now instead of including the js file, as I understand it, I have to import the class from the module. When I try that, I get a console error that only modules can import.
That's true for conventional, static import
statements.
But, significantly, the dynamic import()
operator (an alternative, aynschronous way to import modules) may be used to import modules into classic javascript files as well as into other modules.
MDN notes:
The
import()
syntax, monly called dynamic import, is a function-like expression that allows loading an ECMAScript module asynchronously and dynamically into a potentially non-module environment [...]
and:
[...] you might need to use dynamic import:
- When you are in a non-module environment (for example [...] a script file).
Source: https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Operators/import
Example:
// /my-classic-javascript-file.js
const myAsyncFunction = async () => {
const { myImportedClass } = await import('/path/to/module.js');
}
Further Reading:
- https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Operators/import
A window
property setter solution:
<script type="module">
import Dms from 'https://cdn.jsdelivr/npm/[email protected]/dms.js';
alert(Dms.toLat(-3.62, 'dms'));
window.Dms = Dms;
</script>
<script>
let Dms;
Object.defineProperty(window, 'Dms', {
get(){
return Dms;
},
set(val){
Dms = val;
window.dispatchEvent(new Event('dms-set'));
}
});
window.addEventListener('dms-set', () => alert('SECOND TIME: ' + Dms.toLat(-3.62, 'dms')));
</script>
Some promise solution:
<script type="module">
import Dms from 'https://cdn.jsdelivr/npm/[email protected]/dms.js';
alert(Dms.toLat(-3.62, 'dms'));
window.Dms = Dms;
</script>
<script>
promisifyGlobal('Dms');
Dms.then(Dms => alert('SECOND TIME: ' + Dms.toLat(-3.62, 'dms')));
function promisifyGlobal(name){
let global, promise = new Promise(resolve => {
Object.defineProperty(window, name, {
get(){
return promise;
},
set(val){
global = val;
resolve(global);
}
});
});
}
</script>
You can easily import dynamically into a non-module script:
<script>
(async () => {
const {default: Dms} = await import('https://cdn.jsdelivr/npm/[email protected]/dms.js');
alert(Dms.toLat(-3.62, 'dms'));
})();
</script>
And finally some crazy generic solution:
<script type="module">
import Dms from 'https://cdn.jsdelivr/npm/[email protected]/dms.js';
alert(Dms.toLat(-3.62, 'dms'));
</script>
<script>
// this script will handle all imports
// tweak to import several exports and named exports
for(const script of document.querySelectorAll('script[type=module]')) {
const regex = /import\s+([^\s]+)\s+from\s+["\']([^"\']+)/g;
let matches;
while(matches = regex.exec(script.textContent)){
const [, name, url] = matches;
const src = `import ${name} from '${url}'; export default ${name};`;
window[name] = new Promise(resolve => {
import(`data:text/javascript, ${src}`).then(({default:imported}) => {
setTimeout(() => resolve(imported));
});
});
}
}
</script>
<script>
Dms.then(Dms => alert('SECOND TIME: ' + Dms.toLat(-3.62, 'dms')));
</script>