Browsers support module imports, how do I use it? - javascript

I'm trying to figure out why my script tags don't work in my html file, and I came across an article that says you can import modules into browsers via a script tag with a module type. Directly from this site: https://www.sitepoint.com/understanding-es6-modules/
<script type="module" src="./main.js"></script>
// or
<script type="module">
import { something } from './somewhere.js';
// ...
</script>
So then I tried it and the imported module is recognized but getting this error Can't find variable World is it because my example.js file doesn't have it in its scope? I don't understand the order in which they are "processed?" don't know the terminology sorry.
My attempt below
//HTML
<body>
<script type="module">
import { World, Ball, Paddle, Brick, Hud} from "./brickbreakclasses.js"
</script>
<script src="example.js"></script>
</body>
example.js file
// commented this part out because it was imported in html file
// import { World, Ball, Paddle, Brick, Hud } from "./brickbreakclasses";
var canvas = new World(document.getElementById("canvas") );
var ball = new Ball(canvas)
var brick = new Brick(canvas)
var hud = new Hud(canvas)
var paddle = new Paddle(canvas)
I hope this makes sense but I really want to understand why I can't just require or import something in JS file and add it to html via script. I've been using webpack to bundle everything and putting that into my html.However I'm now making a npm package and I hear people don't like pre-bundled packages so I'm trying to find a way for a user to use my modules in html without having to install webpack or something like it.

Variables declared inside a <script type="module"> are local to that module. Make example.js a module, import the classes from there, and you can use them:
HTML
<body>
<script type="module" src="example.js"></script>
</body>
example.js
import { World, Ball, Paddle, Brick, Hud } from "./brickbreakclasses.js";
var canvas = new World(document.getElementById("canvas") );
var ball = new Ball(canvas);
var brick = new Brick(canvas);
var hud = new Hud(canvas);
var paddle = new Paddle(canvas);

Related

Imported three.js module OrbitControls "not defined"

Javascript and HTML newbie here:
I'm trying to display a 3D model (.gltf file) in a website with controls (pan, zoom, etc.), using the javascript three.js OrbitControls module. In spite of using <script type="module"> tags in the HTML to load OrbitControls, when I try to load the page, I get Uncaught ReferenceError: OrbitControls is not defined from firefox. I can successfully render the image if I do not include the statement defining the controls, but do include the import tag.
Brief code:
--HTML:
<head>
<!-- Imports for three.js and GLTFLoader.js, both seem to be working -->
<script type="module">
import { OrbitControls } from "https://unpkg.com/three/examples/jsm/controls/OrbitControls.js";
</script>
<script src="script.js" defer="defer"></script>
</head>
--Javascript (from script.js):
"use strict";
function main() {
//Create render and camera objects; both work when not using controls
const controls = new OrbitControls( camera, renderer.domElement );
// create scene, GLTFLoader, run animate function
}
const obj_returned = main();
I've tried a few different styles of the script command:
<script type="module"> import { OrbitControls } from "https://unpkg.com/three/examples/jsm/controls/OrbitControls.js"; </script>
This gives the Uncaught ReferenceError: OrbitControls is not defined error,
<script type="module" src="https://unpkg.com/three/examples/jsm/controls/OrbitControls.js">
produces the same error, and
<script src="https://unpkg.com/three/examples/jsm/controls/OrbitControls.js"></script>
This actually gives the same error plus Uncaught SyntaxError: import declarations may only appear at top level of a module, I think due to the missing type="module" flag.
All local files are in the same directory.
I've looked at several other examples of this sort of thing to see what I'm missing (like here, here, here, the three.js documentation, the OrbitControls source code, and several others).
I'm very new to JS and HTML, so 1) it's likely something very obvious and 2) if it's not trivial / a typo, I would appreciate a little explanation as to why this doesn't work so I can avoid similar mistakes in the future. Thanks!

Why Stats.min.js can only be imported by `script src=` but not `import`?

I have Javascript codes as below. When the importation is inside <script src="..."></script>, it works. However, if I moved the importation to import ...;, it doesn't work. The error message is: "Uncaught ReferenceError: Stats is not defined".
Why is that? Since I am working on a Jekyll site, I prefer to do the import ...; way to make sure other elements of the site work. Any idea how to do the import ...; way without error?
It works
<div id="stats"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
<script type="module">
const stats = new Stats()
stats.setMode(0)
document.getElementById('stats').appendChild(stats.domElement)
</script>
It doesn't work
<div id="stats"></div>
<script type="module">
import 'https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js';
const stats = new Stats()
stats.setMode(0)
document.getElementById('stats').appendChild(stats.domElement)
</script>
import allows you to import JavaScript modules which conform to the ES6 module format.
Stats.min.js does not. The code is obfuscated but appears to create a global and support the old CommonJS module format.

Uncaught SyntaxError: Cannot use import statement outside a module

https://www.youtube.com/watch?v=1TeMXIWRrqE
<!DOCTYPE html>
<html>
<head>
<title>Three.js</title>
<style type="text/css">
html, body {margin: 0; padding: 0; overflow: hidden}
</style>
</head>
<body>
<div id="webgl"></div>
<script src="three.js"></script>
<script src="GLTFLoader.js"></script>
<script>
let scene,camera,renderer;
function init(){
scene = new THREE.Scene();
scene.background = new THREE.Color(0xdddddd);
camera = new THREE.PerspectiveCamera(40,window.innerWidth/window.innerHeight,1,5000);
hlight = new THREE.AmbientLight(0x404040,100);
scene.add(hlight);
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth,window.innerHeight);
document.getElementById('webgl').appendChild(renderer.domElement);
let loader = new THREE.GLTFLoader(); //THE ERROR WITH THIS, I TRIED WITHOUT THREE. ONLY GLTLOADER() BUT DID NOT WORK
loader.load('scene.gltf', function(gltf){
scene.add(gltf.scene);
renderer.render(scene,camera);
});
}
init();
</script>
</body>
</html>
https://sketchfab.com/3d-models/1972-datsun-240k-gt-non-commercial-use-only-b2303a552b444e5b8637fdf5169b41cb
//https://www.youtube.com/watch?v=1TeMXIWRrqE
This is tutorial for GLTFLOADER , i imported same 3d file as well and written same code, but when i open the file on local server, i get 2 errors in console:
1) "GLTFLoader.js:9 Uncaught SyntaxError: Cannot use import statement outside a module"
2) "(index):29 Uncaught TypeError: THREE.GLTFLoader is not a constructor."
Although in youtube video it opens with him immediatly!! :(
I would be greatful if anybody can hep me out with this. THANK YOU SO MUCH
GLTFLoader.js:9 Uncaught SyntaxError
(index):29 Uncaught TypeError
Here's a newer tutorial. The bottom of this article explains what changed.
The short version is three.js changed to prefer using es6 modules so instead of
<script src="three.js"></script>
<script src="GLTFLoader.js"></script>
<script>
let scene,camera,renderer;
function init(){
...
THREE.GLTFLoader.load(...
you'd do this
<script type="module">
import * as THREE from './build/three.module.js';
import {GLTFLoader} from './examples/jsm/loaders/GLTFLoader.js';
let scene,camera,renderer;
function init(){
...
GLTFLoader.load(...
But to use it like that requires you copy the three.js files to the same folder structure.
someFolder
|
├-build
| |
| +-three.module.js
|
+-examples
|
+-jsm
|
+-controls
| |
| +-OrbitControls.js
| +-TrackballControls.js
| +-...
|
+-loaders
| |
| +-GLTFLoader.js
| +-...
|
...
If you want to use the old method <script> tag style then you need to make sure you use the files from the js folder, not the jsm folder
Note: You must use this folder structure because the files in the examples/jsm folder like GLTFLoader.js refer to various other files like three.module.js via relative but hard coded paths. For example in GLTFLoader.js there is a line which is effectively
import {stuff} from "../../../build/three.module.js";
A couple of advantages to the new style are
the various modules can pull in the other parts they need.
In the past to use one extra part you might need to add 1 to 10 other <script> tags. Now you just need 1 import and that part can pull in the other 1 to 10 parts on it's own
If you build your page with a web builder it can strip out the parts you are not using.
Rumor is there are plans to get rid of the <script> method completely at some point in the future.
Just to try to make it clear both why ES6 modules are better and why you need to keep the same structure.
In pre r105 to use the EffectComposer you'd do this
<script src="threejs/examples/js/postprocessing/EffectComposer.js"></script>
You run and you'd get an error
THREE.EffectComposer relies on THREE.CopyShader
So you'd dig around to find it, it's not in the same folder as EffectsComposer.js. When you finally find it you add it
<script src="threejs/examples/js/postprocessing/EffectComposer.js"></script>
<script src="threejs/examples/js/shaders/CopyShader.js"></script>
Run again and get another error THREE.EffectComposer relies on THREE.ShaderPass so again digging around you add that
<script src="threejs/examples/js/postprocessing/EffectComposer.js"></script>
<script src="threejs/examples/js/shaders/CopyShader.js"></script>
<script src="threejs/examples/js/postprocessing/ShaderPass.js"></script>
That sucked.
Now as of r105 using es6 modules you can just do
import {EffectComposer} from './threejs/examples/jsm/postprocessing/EffectComposer.js';
Which is arguably much nicer. You won't get the other errors because the ES6 module version EffectComposer.js can reference the files it needs, its dependencies, itself. At the top of EffectComposer.js are the references to its dependencies.
import {
Clock,
LinearFilter,
Mesh,
OrthographicCamera,
PlaneBufferGeometry,
RGBAFormat,
Vector2,
WebGLRenderTarget
} from "../../../build/three.module.js";
import { CopyShader } from "../shaders/CopyShader.js";
import { ShaderPass } from "../postprocessing/ShaderPass.js";
import { MaskPass } from "../postprocessing/MaskPass.js";
import { ClearMaskPass } from "../postprocessing/MaskPass.js";
But, as you can see above, EffectsComposer.js expects that three.module.js is in a folder called build 3 subfolders down from itself. It expects CopyShader.js is in a folder called shaders one folder down from itself. Etc...
In other words, it needs the same folder structure.
I faced a similar issue, this is how I solved it.
In your HTML, the order matters when adding other scripts as it needs certain things from Three.js.
It should be
<script src="../build/three.min.js"></script>
<script src="three.js-master/examples/js/controls/OrbitControls.js"></script>
<script src="three.js-master/examples/js/loaders/GLTFLoader.js"></script>
I would suggest trying to import from your local javascript folder instead of using from jsm. I hope this will solve your issue.

Javascript classes in ES6, how to import them within other js-files and in html files?

Obviously, I am a beginner and trying to figure out basic staff, so please bear with me!
I have all these files (index.html, app.js and calc.js) in the same folder and their content is shown below.
I have 2 issues:
1: why in file (app.js), importing class Calc and creating a new instance of it works, whereas in file (calc.js) similar process doesn't work with class Test?
2: how could I use the class "Calc" for example in the *.html file?
Note: I am using chrome (62.0.3202.94 (Official Build) (64-bit)) on mac OS 10.13
// app.js
import { Calc } from './calc.js';
class Test {
static hello() {
console.log("hello from class1");
}
}
let c = new Calc(); // this works!
console.log(c.sum(2, 3) + 1); // 6
export { Test };
and
// calc.js
import { Test } from "./app.js";
class Calc {
sum(a, b) {
return a + b;
}
}
let t = new Test(); // Uncaught ReferenceError: Test is not defined
t.hello();
export { Calc };
Finally the HTML file:
<html>
<head>
<meta charset="utf-8" />
<script src="./app.js" type="module"></script>
<script src="./calc.js" type="module"></script>
</head>
<body>
<h1>ES6</h1>
<script>
let c = new Calc();
// simulate input summation from two textboxes for example
console.log(c.sum(2, 1)); //Uncaught ReferenceError: Calc is not defined
</script>
</body>
</html>
Problem 1:
You have a circular dependency - app.js imports calc.js, but calc.js imports app.js.
Usually the module system will ensure all of a module's dependencies are executed before the module itself, but this is impossible when your dependency tree has cycles - it has to pick one of them to run first. In this case, calc.js is run first, at which point Test isn't yet defined!
There are ways to work around this if you're careful, but cyclic dependencies are pretty much always a sign that you're structuring your code wrong - the best solution is to reorganize it so you no longer have a cycle.
Problem 2:
ES2016 modules don't execute in the global scope. If you want to make something available to the window as a whole, you need to be explicit:
window.Calc = Calc;

How to access variables across Javascript files with babel?

I'm trying to migrate my code from ES5 to ES6 and use babel. I use the module pattern quite a bit in my code, such that if I have a module like apple I'll do something like this:
var appleModule = (function() {
var yummy = true;
var eat = function() { }
return { "eat": eat }
})();
and then access appleModule in a different file. However, when moving everything from this:
<script type="text/javascript" src="/scripts/apple.js"></script>
<script type="text/javascript" src="/scripts/banana.js"></script>
to this:
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.25/browser.js"></script>
<script type="text/babel" src="/scripts/apple.js"></script>
<script type="text/babel" src="/scripts/banana.js"></script>
I can no longer access appleModule in different files. I get a ReferenceError saying it doesn't exist. How do access variables across files with babel and ES6?
Please actually read the documentation for babel-browser
Not intended for serious use
Compiling in the browser has a fairly limited use case, so if you are working on a production site you should be precompiling your scripts server-side. See setup build systems for more information.
You're not supposed to use babel-browser like that in a production environment. Not even in a development one, really.
Instead, setup a proper build step for your code to transpile and bundle your code.
You don't even have to create your own modules like that
a.js
var yummy = true;
var eat = function(){};
export var eat;
b.js
import {eat} from './a.js';
ES6 exporting only takes the exported parts and everything else is practically equivalent of being in a function, to import appleModule
export var appleModule = (function() {
var yummy = true;
var eat = function() { }
return { "eat": eat }
})();
import {appleModule as appleModule} from './apple';

Categories