Hi all I'm learning javascript and have run into a problem when defining a function in head utilizing imported functions:
<script type="module">
import {g1} From './scripts/main.js';
myFunction(){
do something, g1 is called
}
</script>
when I call myFunction in body I get a myFunction() not defined error:
<script>
myFunction();
</script>
My understanding is this should work, am I making a mistake with the type attribute in head?
In JS the scope of a given code is executed simply like so:
(1) Global scope > (2) Module scope and then the > (3) Function scope
Meaning that you try to access a module scope variable/function in the global scope, you need to restructure you code someway. Your options are:
Either to put the code within the same module
Or find a way to import one module into the other one
Related
I am testing ES6 Modules and want to let the user access some imported functions using onclick:
test.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Module Test</title>
</head>
<body>
<input type="button" value="click me" onclick="hello();"/>
<script type="module">import {hello} from "./test.js";</script>
</body>
</html>
test.js:
export function hello() {console.log("hello");}
When I click the button, the developer console says: ReferenceError: hello is not defined. How can I import functions from modules so that they are available as onclick functions?
I am using Firefox 54.0 with dom.moduleScripts.enabled set to true.
Module creates a scope to avoid name collisions.
Either expose your function to window object
import {hello} from './test.js'
window.hello = hello
Or use addEventListener to bind handler. Demo
<button type="button" id="hello">Click Me</button>
<script type="module">
import {hello} from './test.js'
document.querySelector('#hello').addEventListener('click', hello)
</script>
Module scope in ES6 modules:
When you import a script in the following manner using type="module":
<script type="module">import {hello} from "./test.js";</script>
You are creating a certain scope called module scope. Here is where modules scope is relative to other levels of scope. Starting from global they are:
Global scope: All declarations outside a module on the outermost level (i.e. not in a function, and for const and let not in a block) are accessible from everywhere in the Javascript runtime environment
Module scope: All declarations inside a module are scoped to the module. When other javascipt tries to access these declarations it will throw a reference error.
Function scope: All the variables declared inside (the top level of) a function body are function scoped
Block scope: let and const variables are block scoped
You were getting the referenceError because the hello() function was declared in the module, which was module scoped. As we saw earlier declarations inside module scope are only available within that module, and you tried to use it ouside the module.
We can make declarations inside a module global when we explicitly put it on the window object so we can use it outside of the module. For example:
window.hello = hello; // putting the hello function as a property on the window object
While the accepted answer is correct, it scales poorly once you start importing from multiple modules, or declaring multiple functions . Plus, as noted by #Quentin, it risks global name space pollution.
I prefer a slight modification
import { doThis, doThat } from './doStuff.js'
import { foo1, foo2 } from './foo.js'
function yetAnotherFunction() { ... }
window._allTheFns = { doThis, doThat, foo1, foo2, yetAnotherFunction }
// or, if you prefer, can subdivide
window._doStuffjs = { doThis, doThat }
window._foojs = { foo1, foo2 }
Uses an "unusual" property name to (hopefully) avoid global namespace issues
No need to immediately attach (or forget to attach) an EventListener.
You can even put the listener in the HTML, e.g. <button onclick="window._foojs.foo1(this)">Click Me</button>
As of 2022 I don't know of any other solution than the provided, except that you don't need to use window, but you can use globalThis
I don't know if this makes it any better.
advantages:
it is a tick more readable
you don't get a compiler error when checkJS is on (attribute "..." is not found on window etc...)
I'm even using LitHtml where you have #event notation, looks like this:
<element #click="${(e)=>console.log(e)}"></element>
but sadly there is no clean way to actually get a reference of the object that has the listener attached. the target, originalTarget and explicitOrOriginalTarget can be anything in the bubbling queue, which is super annoying and super confusing, but if you don't need that, LitHTML would be the way to go.
I am testing ES6 Modules and want to let the user access some imported functions using onclick:
test.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Module Test</title>
</head>
<body>
<input type="button" value="click me" onclick="hello();"/>
<script type="module">import {hello} from "./test.js";</script>
</body>
</html>
test.js:
export function hello() {console.log("hello");}
When I click the button, the developer console says: ReferenceError: hello is not defined. How can I import functions from modules so that they are available as onclick functions?
I am using Firefox 54.0 with dom.moduleScripts.enabled set to true.
Module creates a scope to avoid name collisions.
Either expose your function to window object
import {hello} from './test.js'
window.hello = hello
Or use addEventListener to bind handler. Demo
<button type="button" id="hello">Click Me</button>
<script type="module">
import {hello} from './test.js'
document.querySelector('#hello').addEventListener('click', hello)
</script>
Module scope in ES6 modules:
When you import a script in the following manner using type="module":
<script type="module">import {hello} from "./test.js";</script>
You are creating a certain scope called module scope. Here is where modules scope is relative to other levels of scope. Starting from global they are:
Global scope: All declarations outside a module on the outermost level (i.e. not in a function, and for const and let not in a block) are accessible from everywhere in the Javascript runtime environment
Module scope: All declarations inside a module are scoped to the module. When other javascipt tries to access these declarations it will throw a reference error.
Function scope: All the variables declared inside (the top level of) a function body are function scoped
Block scope: let and const variables are block scoped
You were getting the referenceError because the hello() function was declared in the module, which was module scoped. As we saw earlier declarations inside module scope are only available within that module, and you tried to use it ouside the module.
We can make declarations inside a module global when we explicitly put it on the window object so we can use it outside of the module. For example:
window.hello = hello; // putting the hello function as a property on the window object
While the accepted answer is correct, it scales poorly once you start importing from multiple modules, or declaring multiple functions . Plus, as noted by #Quentin, it risks global name space pollution.
I prefer a slight modification
import { doThis, doThat } from './doStuff.js'
import { foo1, foo2 } from './foo.js'
function yetAnotherFunction() { ... }
window._allTheFns = { doThis, doThat, foo1, foo2, yetAnotherFunction }
// or, if you prefer, can subdivide
window._doStuffjs = { doThis, doThat }
window._foojs = { foo1, foo2 }
Uses an "unusual" property name to (hopefully) avoid global namespace issues
No need to immediately attach (or forget to attach) an EventListener.
You can even put the listener in the HTML, e.g. <button onclick="window._foojs.foo1(this)">Click Me</button>
As of 2022 I don't know of any other solution than the provided, except that you don't need to use window, but you can use globalThis
I don't know if this makes it any better.
advantages:
it is a tick more readable
you don't get a compiler error when checkJS is on (attribute "..." is not found on window etc...)
I'm even using LitHtml where you have #event notation, looks like this:
<element #click="${(e)=>console.log(e)}"></element>
but sadly there is no clean way to actually get a reference of the object that has the listener attached. the target, originalTarget and explicitOrOriginalTarget can be anything in the bubbling queue, which is super annoying and super confusing, but if you don't need that, LitHTML would be the way to go.
I have html:
<div id="test" onclick="test()">TEST</div>
Also I have main.js which is included in the file app.js
import "./main"
The file main.js contains a function:
function test() {
alert('Test');
}
But when I click on the test block, I get an error:
Uncaught TypeError: test is not a function
Please tell me how to properly access functions using the attribute onclick?
You need to make the function available to the global context like this:
window.test = function() {
alert('Test');
}
Exposing to global scope via window property will work, but since you have the function in a module, just make an advantage of it. Directive import is meant to be used together with export one (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export):
app.js:
import { test } from "./main"
main.js:
export function test() {
alert('Test');
}
What do we gain? test function doesn't pollute the global scope. It's isolated, but shared between app.js and main.js. However in your case you refer to the function from HTML, meaning addressing global scope. So to make it "best practice", you could do in app.js:
document.querySelector("#test").addEventListener("click", test, false);
and remove onClick in the HTML.
I am pretty new to JavaScript, and I was experimenting with the global scope following a tutorial.
In this tutorial I have 3 files:
1) index.htm:
<html>
<head></head>
<body>
<h1>HELLO WORLD !!!</h1>
<script src="app.js"></script>
<script src="utility.js"></script>
</body>
</html>
As you can see I include 2 JavaScript files.
2) app.js:
var person = 'Torny'; // When I call the logPerson() function this line override the person variable value into the global stack
//console.log(person);
logPerson();
3) utility.js in which is defined the logPerson() function defined into the app.js file:
var person = 'Steve';
function logPerson() {
console.log(person);
}
Ok, so I expected the following behavior:
Into the app.js file set the string 'Tony' as value of the person variable, the call the logPerson() function declared into the utility.js file and so print 'Tony' in the console.
The problem is that when I try to open the index.htm file into FireFox, I go into FireBug console and instead the 'Tony' value I obtain this error message:
ReferenceError: logPerson is not defined
So it seems that from the app.js file it can't see the logPerson() function that is defined into the utility.js file and imported.
Why? What am I missing? what is wrong?
In javascript the order of the files matters. You need to load the script which defines your function before you can use it. Switch the 2 <script> tags
<body>
<h1>HELLO WORLD !!!</h1>
<script src="utility.js"></script>
<script src="app.js"></script>
</body>
</html>
change
<script src="app.js"></script>
<script src="utility.js"></script>
to
<script src="utility.js"></script>
<script src="app.js"></script>
;) You try to get definitions from a script which is loaded later
greetings
First browser will parse the JS files.
If you see the order of script included
app.js
utility.js
So when it is parsing app.js. It sees logPerson() which is a function call not function definition. At this point of time the file utility.js is not parsed and the browser is not aware of that function also. (meaning till it reads utility.js).
So to solve this, call the logPerson api after dom-loaded, usiing body onload function.
The explanation is as simple as that Javascript code is interpreted on the fly: As one script is loaded, it is executed immediately.
So, you just have to declare the scripts in the right order of dependence.
You run the script which tries to use logPerson before you run the script that defined logPerson.
If you reverse the order of the scripts, it will work.
Function declaration hoisting only works within a given script.
In my case, it wasn't the <script> ordering that was the issue (as initially thought), but the scope in which the function was defined. To enable access of the function in other JS files, I made it a property of the object that is in the scope of those files. So now, it can be accessed as MyObject.myFunction wherever MyObject is instantiated.
(function ($) {
function MyObject(data) {
var myFunction = function(){
console.log('');
}
$.extend(this, {
// methods
"myFunction": myFunction,
});
}
}(jQuery));
I have two JS files. One is called common.js and it use $.getScript to include other file with my js code. Part of including looks like this:
jQuery.getScript(js_path + "table.js", function(){
generateTable(chartData, dataTypes);
});
This file (common.js) also contains function compare(a, b).
Now, the second one (table.js) has declared different function which uses the compare function from the first file. Something like this:
function someName() {
var a = 2,
b = 5;
var test = compare(a, b);
return test;
}
When I run the code it gives me:
Uncaught ReferenceError: compare is not defined
How can I use function from the first file.
jQuery.getScript first fetches the JS file from the server, then executes it. If you want to work with global functions (as it seems) you need to pay attention to the following:
Your compare function must be declared before the table.js file is executed.
The compare function must be declared on the global namespace of table.js.
Sorry but without more info this is all you can get.
If your main file, as something like:
(function() {
function compare(){...}
}());
Then the compare function is not declared in the global namespace.
did you check the order of imports? The file with 'compare' method should be first. It should solve the problem.
What I would suggest is to skip getScript if it's just for separating the code.
<script src="common.js"></script>
<script src="table.js"></script>
<script src="app.js"></script>
Where common functions go into common, your table stuff goes into table. This way you get the ordering right. This also clears out the circular dependency one might see a hint of if table depends on common that depends on table by extracting all but the 'common' parts into some form of 'app'.