Import module functions not defined

I’m trying to define an array in a separate file and import it into my main js file.

I was getting the error “Uncaught SyntaxError: import declarations may only appear at top level of a module” and I found some forum posts and docs that said I need to make both js files modules in the HTML:

    <script src="script.js" type="module"></script>
    <script src="module.js" type="module"></script>

This worked in a small example project, but when I tried to do this to my real project the functions stop working and come back as “undefined”.

Does anyone have any insight here?

This is my small test project that seems to work fine:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title></title>
    <meta name="description" content="" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="" />
  </head>
  <body>


    <script src="script.js" type="module"></script>
    <script src="module.js" type="module"></script>

  </body>
</html>

script.js

import { arrayFour } from "./module.js";

console.log(arrayFour);

const functionOne = () => {
  console.log("funcion one");
  console.log(arrayFour);
};

function functionTwo() {
  console.log("funcion two");
  console.log(arrayFour);
}

functionOne();
functionTwo();

module.js

const arrayFour = [1, 2, 3, 4];

export { arrayFour };

This all worked as expected, and the functions work. However when I do the same thing with my larger project the functions become undefined. I think it might have something to do with global scope vs script scope, but my small test project works fine.

Here’s the current code for the larger project:
https://github.com/pkdvalis/MBC-Search

I’m trying to import the long array previously from another file. When I implement the import locally I get these errors:

Uncaught ReferenceError: searchFor is not defined
    onclick http://127.0.0.1:3000/index.html:1
index.html:1:1

Uncaught ReferenceError: getDetails is not defined
    onclick http://127.0.0.1:3000/index.html:1
index.html:1:1

These are the changes I make:
script.js

import { previouslyList } from './previouslylist.js'  
let previously = previouslyList;

//comment out the existing let previously

index.html

    <script src="previouslylist.js" type="module"></script>
    <script src="script.js" type="module"></script>

previouslylist.js

const previouslyList = [
  { title: "The Pelican Brief", author: "John Grisham" },
  { title: "Eat, Pray, Love", author: "Elizabeth Gilbert" },
// etc etc
  { title: "L.A. Candy", author: "Lauren Conrad" },
];

export { previouslyList };

Everything is working fine, and when I make these changes all of the sudden the functions stop working. If I remove type="module" it’s ok but then I get the "Uncaught SyntaxError: import declarations may only appear at top level of a module" error.

Cannot understand why this is happening, any help appreciated!

An import should be the first thing at the top of the file? I can see you have that here but I couldn’t find it in your github code.
Undefined is a variable that doesn’t have a value assigned to it so you may have two issues here. Good luck

The top, 1st line. However, it seems that the more important part of this error is “of a module”. The js file needs to be defined as a module in the HTML script tag in order to use import.

Once this is done, my previously working and defined functions are suddenly “undefined”. It’s a little odd that the error refers to index.html:1:1 the first line of the html doc as well.

Thanks

I looked at some code they have type listed first before the source attr, might be worth a try,
also putting this in the head of the html might work.

I tried both now, same result:

Uncaught ReferenceError: getDetails is not defined
    onclick http://127.0.0.1:3000/index.html:1
index.html:1:1

Thanks though

<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title></title>
    <meta name="description" content="" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="stylesheet" href="styles.css" />
    <script type="module" src="previouslylist.js" async defer></script>
    <script type="module" src="script.js" async defer></script>
  </head>
1 Like

Oof, I think I have it :face_with_monocle:

I needed to make those functions global scope, instead of module scope.

https://stackoverflow.com/questions/52785120/function-in-my-javascript-module-is-not-defined

window.searchFor = searchFor;
window.getDetails = getDetails;

Yet I have another function that works just fine? I wonder why I don’t need to do the same thing to this one.

const previouslyOn = () => {

As well, why didn’t I need to do this to my smaller test project? That worked fine.

Crazy to me that I need to do this to a function defined in the same file in which I’m working. Why do you need to define the importing js as a module? Like most things, it’s just the way it is…

Solved for now, but if anyone cares to share any insight or explanation, I’m all ears!

It is specific to the function you use as inline handlers (i.e. onclick="") in your string templates (or when used directly in an HTML file). You also can’t call the functions defined inside the module scope from the browser console.

This post has a more detailed run down of the “scope” part of modules.


BTW, you do not need modules for this use case. If you declare the previouslyList array and load previouslylist.js before script.js, the code inside script.js has access to the array.

Also, modules are already deferred by default, so you do not need the defer attribute.

1 Like

Aha! Thank you! It’s because they are being called from the HTML file but the script is now module scope, this makes perfect sense now! This is also why functions called from in the script worked fine.

:sob: :sob: :sob: Thanks again, laughing on the outside, crying on the inside.

1 Like

Don’t cry. I do think the module import/export is cleaner, I just wanted to make sure you knew it wasn’t really needed.

It is a little annoying that you can’t use inline handlers in templates with modules without adding them to the global. I will say, as much as I value plain JS apps, your app might also be at the point where thinking about using a “view” framework starts to make sense.

1 Like

I’m genuinely happy I understand all of that a bit better, just sad I spent so much time on something that I didn’t need at all. It reminds me of implementing an image gallery with a dynamic columns system in JS and then reading about CSS grids the next week.

I implemented a local result caching in this app that I’m really proud of but I’m sure there’s a library or framework that might already do this better. It is a learning project though.

When I complete the new React curriculum on FCC I can see implementing v2.0 of this in React. Is that what you mean, React, Angluar or something like it?

There’s one last feature I’ll write, which is moving the search terms into URL variables so it’s easy to share search results (?author=“this”&title=“that”) then I’ll leave it alone until I can implement in React. EDIT: and search auto-complete! and…

Sure, it’s never fun when you feel like you have waisted time. The upshot is you are more likely to remember it. So from a learning perspective, getting a little “triggered” by a bug will just make it easier to remember. And in the end, no matter what you code, even if it turns out to be unnecessary, will have taught you something. Filling your bucket of knowledge is always time well spent.

It is mainly the DOM manipulation part. It is often just easier and a bit cleaner using a framework. The templating is built-in and more robust than template strings, adding handlers is much cleaner, and you get other stuff for free as well (like lifetime hooks and whatnot).

Edit: also, meta-frameworks now provide easy access to a backend. So you can keep all your API interactions private. Having the client hitting a third-party API directly is usually not a good idea.

Yes, that is what I ment by “view” framework.

URL state is awesome, and is often way too undervalued in apps. It has become more popular, but I think it is still underappreciated considering just how powerful it can be.

1 Like