Use functions defined in ES6 module directly in html
Each module has its own scope. They are not sharing the global scope like "normal" scripts do. That means hello
is only accessible inside the main.js
module/file itself.
If you explicitly want to create a global variable, you can achieve that by creating a property on the global object, window
:
function hello()
{
console.log(mod());
}
window.hello = hello;
See also Define global variable in a JavaScript function
Having said that, it's good practice to avoid global variables. Instead you can restructure the HTML to load the modules after the button was created and bind the event handler via JavaScript:
<!DOCTYPE html>
<html>
<body>
<button name="next-button">Obi-Wan abandons the high ground to salute you</button>
<script type="module" src="module.js"></script>
<script type="module" src="main.js"></script>
</body>
</html>
and
import { mod } from './module.js';
function hello()
{
console.log(mod());
}
document.querySelector('button').addEventListener('click', hello);
Use parameterised functions defined in ES6 module directly in html
This works fine, but what if your original button was a bit more sophisticated and was parameterised?
There are a couple of solutions to that:
A
data-*
attribute:<button id="the-button" data-string="withThisString">Do Something with String</button>
document.getElementById("the-button").addEventListener("click", function() {
doSomething(this.getAttribute("data-string"));
});(More on this below.)
or
Binding the string when you bind the event
<button id="the-button">Do Something with String</button>
document.getElementById("the-button").addEventListener("click", () => {
doSomething("withThisString");
});
There are lots of variations on the above, and if you use doSomething
with multiple buttons with different strings you can do #1 with a class and a loop rather than with an id
, but that's the general idea.
Re the data-*
attribute thing: If you wanted to, you could make this process entirely HTML-driven via data-*
attributes and a single function that hooks things up. For instance, say you had these buttons:
<button data-click="doThisx@module1">Do This</button>
<button data-click="doThat@module2">Do That</button>
<button data-click="doTheOther@module3">Do The Other</button>
You could have a single reusable function to hook those up:
class EventSetupError extends Error {
constructor(element, msg) {
if (typeof element === "string") {
[element, msg] = [msg, element];
}
super(msg);
this.element = element;
}
}
export async function setupModuleEventHandlers(eventName) {
try {
const attrName = `data-${eventName}`;
const elements = [...document.querySelectorAll(`[${attrName}]`)];
await Promise.all(elements.map(async element => {
const attrValue = element.getAttribute(`data-${eventName}`);
const [fname, modname] = attrValue ? attrValue.split("@", 2) : [];
if (!fname || !modname) {
throw new EventSetupError(
element,
`Invalid '${attrName}' attribute "${attrValue}"`
);
}
// It's fine if we do import() more than once for the same module,
// the module loader will return the same module
const module = await import(`./${modname}.js`);
const fn = module[fname];
if (typeof fn !== "function") {
throw new EventSetupError(
element,
`Invalid '${attrName}': no '${fname}' on module '${modname}' or it isn't a function`
);
}
element.addEventListener(eventName, fn);
}));
} catch (error) {
console.error(error.message, error.element);
}
}
Using it to find and hook up click handlers:
import { setupModuleEventHandlers } from "./data-attr-event-setup.js";
setupModuleEventHandlers("click")
.catch(error => {
console.error(error.message, error.element);
});
It's one-time plumbing but gives you the same attribute-based experience in the HTML (the event handlers could still get parameter information from another data-*
attribute, or you could bake that into your setup function). (That example relies on dynamic import
, but that's supported by recent versions of all major browsers and, to varying degrees, bundlers.
There are a couple of dozen ways to spin that and I'm not promoting it, just giving an example of the kind of thing you can readily do if you want.
But really, this is where libraries like React, Vue, Ember, Angular, Lit, etc. come into play.
How to call a function declared in a javascript module (type=module) from an html page
First of all you have to explicitly export you function:
export function greet() {
alert("Hello from module");
}
Secondly, a module has it's own scope (this is the whole point of modules) thus you need to add the function to the global scope. So, to do it you have to run a script which imports the function and adds it to the window object:
<script type="module">
import { greet } from "./app.js";
window.greetFromModule = greet;
</script>
Now you don't need this part <script type="module" src="app.js"></script>
Alternatively you can create an empty obj and add your modules stuff to it, this is what it would look like:
<html>
<head></head>
<body>
<button onclick="greetFromHtml();">greetFromHtml</button>
<button onclick="module.greet()">greetFromModule</button>
<script type="text/javascript">
function greetFromHtml() {
alert("Hello");
}
const module = {};
</script>
<script type="module">
import { greet } from "./app.js";
module.greet = greet;
</script>
</body>
</html>
How to access function from a javascript type=module
That's not possible. As Ruben mentioned in the comment, you need to import the main.js into your index.js to have access to its bar() function, else you will get a
Uncaught ReferenceError: bar is not defined
error
Checking these questions may be helpful :
how-to-use-code-from-script-with-type-module
use-functions-defined-in-es6-module-directly-in-html
How to call one function defined in a javascript to another javascript file?
You can't really mix and match module
and non-module JS. So:
- Use only one
<script>
element- Make it
type="module"
- Load
select.js
with it
- Make it
- Use
import {today} from "./Test.js";
because the path is relative to the JS - Since variables in modules are not global, use
addEventListener
insideselect.js
and not anonclick
attribute.
How to call ES6 module function from onclick
The only way to do it would be to assign the imported module function to the window object so that it can be referenced by the inline handler:
<script type="module">
import { Es6Module2 } from "./js2.js";
window.Es6Module2 = Es6Module2;
</script>
But that defeats the purpose of modules.
Regardless, inline handlers should never, ever be used. They have way too many problems, including this one (requiring the function to be called to be in the global scope). Best to avoid them and use addEventListener
instead, to add the listener properly.
If you want to add a listener which calls a function with a parameter, then just call the function with the parameter in the listener:
import { fn } from './script.js';
document.querySelector('button').addEventListener('click', () => {
fn('foo');
});
How to import es6 module that has been defined in script type=module tag inside html?
As I understand, there is no way to import "anonymous" module, because "anonymous" module have no module specifier or individual url (its import.meta.url
is just the html url as current spec). In theory it can be extended in the future, but I can not find the good use cases for such feature.
Related Topics
What's the Best Way to Make a D3.Js Visualisation Layout Responsive
How to Update State.Item[1] in State Using Setstate
Change Values in Array When Doing Foreach
Fastest Way to Convert JavaScript Nodelist to Array
Accessing Private Member Variables from Prototype-Defined Functions
Make React Useeffect Hook Not Run on Initial Render
How to Make a JSONp Request from JavaScript Without Jquery
Enabling Cross-Origin Resource Sharing on Iis7
Iterate Through Nested JavaScript Objects
Regex to Match All Instances Not Inside Quotes
Running JavaScript in Selenium Using Python
Firing Event on Dom Attribute Change
React Useeffect in Depth/Use of Useeffect
Determine Original Name of Variable After Its Passed to a Function