65

I am trying to accomplish a pretty simple thing: I have some code on a javascript module file and I import it on another javascript file (that doesn't export anything) and I want to call some of the defined functions in that file from the HTML directly.

Let's se some representative and minimal example of what's happening to me (actually tested the code and gives the exact same issue I'm experiencing with the real one, which is not really much more complex than this one):

  • module.js:

    const mod = () => 'Hello there!';
    export { mod };
    
  • main.js:

    import { mod } from './module.js';
    
    function hello()
    {
        console.log(mod());
    }
    
  • main.html:

    <!DOCTYPE html>
    <html>
        <head>
            <script type="module" src="module.js"></script>
            <script type="module" src="main.js"></script>
        </head>
    
        <body>
            <button name="next-button" onclick="hello()">Obi-Wan abandons the high ground to salute you</button>
        </body>
    </html>
    

Without the import (and putting all the function definitions on a single .js file) I can call the function directly from the HTML. However, once I have introduced the modules I am no longer able to: it simply says that the "hello()" function is not defined.

I am completely new to ES6 modules (and to front-end javascript in fact), so I am fully aware all I just said is just lack of knowledge (or understanding), but I would appreciate any comment on what am I doing wrong and how to solve it so I can have my code in different files and be able to use it. Thank you all in advance!

Alejandro de Haro
  • 955
  • 1
  • 8
  • 14
  • Possible duplicate of [ES6 Modules: Undefined onclick function after import](https://stackoverflow.com/questions/44590393/es6-modules-undefined-onclick-function-after-import) – Bergi May 26 '21 at 18:26

1 Answers1

81

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);
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 7
    Oh I didn't know that! It works flawlessly, but I was wondering if there is not a way of doing this the other way: instead of adding a global variable to access the function from the global scope, isn't there a way of accessing the module scope from the HTML? Or maybe some other way like adding the handler to the button from the module file directly, would that work? – Alejandro de Haro Dec 05 '18 at 10:46
  • Check out my recent update ;) To answer your first question: No, there isn't a way to directly access "scope" (including module scope) from "outside". – Felix Kling Dec 05 '18 at 10:47
  • I wrote the comment before reading the edit to your answer :) Exactly what I was looking for: easy to understand, stylish and good practice. Many thanks! – Alejandro de Haro Dec 05 '18 at 10:48
  • thank you! i've been searching for this exact solution – notsodev Dec 05 '19 at 16:37
  • Is it just me or doesn't this feel like its just more work. Is it bad practice to add onclick="hello()" to the actual html element? Feels like its easier that way. – Lewis Morris Feb 25 '21 at 20:27
  • 2
    @LewisMorris: The answer is: "it depends". If you just want to prototype something or are writing a very small application then do whatever you want. The larger the application gets, the more of a structured approach you might want. You can also change how you do things over time. Software is easy to adapt after all :) – Felix Kling Feb 26 '21 at 08:58
  • This is actually a shocking pattern if you are used to other languages with DAG imports... – iamanigeeit Mar 25 '22 at 13:50