Making browser environment modules work properly

Link to the pertinent lesson/article

Quotations from such article:

ES6 Named Export Syntax

A module must be entirely contained within a file. So, let’s first consider where a new module may be placed within the file system. Since it needs to be used by both of these projects, you may want to put it in a mutually accessible location. The entire file structure containing both projects and this new module, let’s call it dom-functions.js , could look like this:

secret-image/
|-- secret-image.html
|-- secret-image.js
secret-messages/
|-- secret-messages.html
|-- secret-messages.js
modules/
|-- dom-functions.js    <-- new module file

[…]

If you want to try this out on your own computer, you will need to spin up a local server or else you will run into CORS issues. Check out this article on creating a local server with VSCode and the Live Server extension.

[…]

ES6 Import Syntax

The ES6 syntax for importing named resources from modules is similar to the export syntax:

import { exportedResourceA, exportedResourceB } from '/path/to/module.js';

Let’s update the secret-messages program such that it now imports functionality from dom-functions.js . Doing so requires two important steps. First, update secret-messages.js :

/* secret-messages.js */
import { toggleHiddenElement, changeToFunkyColor } from '../modules/dom-functions.js'; 

const buttonElement = document.getElementById('secret-button');
const pElement = document.getElementById('secret-p'); 

buttonElement.addEventListener('click', () => {  
    toggleHiddenElement(pElement);  
    changeToFunkyColor(buttonElement);}
);

OK, having provided (I hope) enough context, here’s my problem:

I did follow and read the link about CORS and I am using VSCode and Live Server (actually, since this wasn’t working, I am also using now the live-server package of Node.js, but have the same problem with either).

However, insofar as the modules folder is outside of the project’s folder (in this case, those would be secret-image for one project and secret-messages for the other one), I get a console error (both in FF and Chrome, latest versions, and either using VSC with Live Server extension and using Terminal with live-server package of Node.js).

Console error on Chrome:

Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of “text/html”. Strict MIME type checking is enforced for module scripts per HTML spec.

This error message points to secret-messages.js:1 (please see above).

Console error on FF, first line:

Loading module from “http://127.0.0.1:5500/modules/dom-functions.js” was blocked because of a disallowed MIME type (“text/html”).

This error message points to the secret-messages.html file (no line indicated).

Console error on FF, 2nd line:
Loading failed for the module with source “http://127.0.0.1:5500/modules/dom-functions.js”.

This error message points to secret-messages.html:8:1 (please see above).
(That would be line 8 in the html, which is:

<script type="module" src="./secret-messages.js"> </script>

)

One thing I’ve noticed in FF is that the URL to the modules folder in the console error message does not seem to respect the ‘…/’ of the URL. Would that be part of the problem?

Now, I’ve shown here the problem. At one point, I think I tried to set Live Server extension of VSC to open server from the ‘projects’ folder and I think it worked (I’ve been trying things for hours and I may have forgotten some). But now I can’t do it anymore. Plus, if I do this, I would be able to see the other folders from the browser and I will have to browse until I get to the html. I don’t think that’s how you would test a front end?

Additionally, the lesson said the main benefit was this:

Instead, creating a single copy of toggleHiddenElement() within a module that exports it would allow these two websites to import and use the exact same function. With this approach, updates to the function only need to occur within the module that defines them, and all programs that import this function will receive the same update. Furthermore, additional functions could be exported by the module and used by both programs, further reducing repetition.

The thing is, I see what it means, but if it’s this difficult to make modules work, maybe it’s easier to just copy into another file? I mean, I can’t see the benefit because I am not understanding how the article author made this work and I can’t. What am I missing here?

Any piece of insight will be more than appreciated. Also, if I missed any other necessary contextual information, let me know and I’ll post it.

Update: I have since then tried other things. Among them, I installed ‘http-server’ with npm, and this only got things more funny, as I am now having a CORS issue (not before now with this package I do) with a very strange path in my own system where the server seems to be redirected by ‘secret-messages.js’ to find the modules file (?). Nothing to do with the path I indicated there.

Now, this is a beginner’s speculation, bear in mind, but could it be that the error really lies on Codecademy’s side here? I mean in the sense that in the previous lesson, we were explained about two possible environments, the browser and Node.

And now what this article seems to indicate (or what I understand, at least) is that we should use the browser environment’s import/export type to access files outside the website (i.e.: not in the window object, but instead in the filesystem of the actual server machine)?

Am I understanding this wrong?

I would hugely appreciate any guide or insight here.

Thanks again!

You should be using Live-Server extension for VS-code here and not the http-server npm-package. I have not built the project locally but it doesn’t look like it’s using node.js. It is a basic .html file using some JS in another file. There are some JS functions which directly manipulate the DOM. The JS runs in the browser-environment. Keep in mind that server can mean many different things and there is a server delivering even a hosted static website (with or without JS) but that is different from a node.js-server which is typically used in building the back-end-server in a full-stack website. If you are still having issues when using Live-Server extension it would be helpful if you posted the code from all the files you are using. Also, make sure to open the .html-file with Live Server.

Hi Mike,

First of all, thanks for taking the time to answer.

Yes, like I said above, I am, among other things I’ve tried.

That’s the thing. Please kindly read again the part I quote from the lesson, and you’ll see they don’t talk about one project/website, but different projects/websites using this external (I mean external to them) folder for modules as a shared modules folder (sounds to me like a back-endish Node thingy). It’s probably clearer if you read it in the link to CodeCademy’s lesson I provided above. It’s a file structure for a multi-website configuration.

I added no code whatsoever. I used the exact brief code used in that lesson. Which has two folders, each one the root of a website which is within, and another folder for the modules. And then used import and export syntax to bring the module file into the js file. Although I think it’s clearer and faster if you follow the link to the lesson above. If, for some reason, you need to me to paste all the code from all the files here, let me know and I’ll do it, although it’s the same.


I’d like to add something I found in a book that I just started and it seems clearer to me, although hey, maybe I am confused there too. It’s an O’Reilly book written by Eric Elliott called “Programming JavaScript Applications

In Chapter 4 (‘Modules’), it states this:

ES6 Modules
None of the module systems discussed so far are included in the official ECMAScript specifications. In the future, a module system will be built in to all ECMA-compliant JavaScript engines.
The ES6 module specification isn’t widely usable yet, but when it is, it should be possible to write modules and compile using build tools to fall back on the module pattern, AMD, and CommonJS where required. ES6 module build tools are already beginning to appear, but you should probably wait a bit longer before you start to rely on them for your production applications

Now, what I take from that (at least what I understand) is that I should be using some module compiler (like Bable, Webpack or something like that, which I haven’t learned or tried yet anyway, but I’ve searched around after reading this and they call them bundlers) if I want to access files (i.e. modules) outside of the website’s root folder (i.e.: external).

Otherwise, I am at a lost as to how they achieved that in that lesson, and certainly, with all of this struggle just to make the first introduction exercise even get to work, I can not understand the use of modules (or any topic) so much.

If you (or anyone else) feel like it, let me know if or what did I misunderstand here or misused, etc. Or how do you use them in production. Thanks again!

I would first find a browser that is updated enough to support this. Write valid code for that browser. The polyfills can deal with the rest,

Just in case, I will paste all of the files’ code here, in case this helps clarify the situation. Please bear in mind none of it is my own code: I literally copy-pasted from CodeCademy’s website all of it.

Folder structure:

<!-- secret-messages.html --> 
<html>
  <head>
    <title>Secret Messages</title>
  </head>
  <body>
    <button id="secret-button"> Press me... if you dare </button>
    <p id="secret-p" style="display: none"> Modules are fancy! </p>
    <script type="module" src="./secret-messages.js"> </script>
  </body>
</html>
/* secret-messages.js */
import { toggleHiddenElement, changeToFunkyColor } from "../modules/dom-functions.js";

const buttonElement = document.getElementById('secret-button');
const pElement = document.getElementById('secret-p');
 
buttonElement.addEventListener('click', () => {
    toggleHiddenElement(pElement);
    changeToFunkyColor(buttonElement);
});

NOTE: the sscret-image folder follows a similar logic and code (again, if you want to see it and/or reproduce the issue, follow the first link at the beginning of the initial message of this thread).

Within modules folder, just one file:

/* dom-functions.js */
const toggleHiddenElement = (domElement) => {
    if (domElement.style.display === 'none') {
      domElement.style.display = 'block';
    } else {
      domElement.style.display = 'none';
    }
}
 
const changeToFunkyColor = (domElement) => {
  const r = Math.random() * 255;
  const g = Math.random() * 255;
  const b = Math.random() * 255;
 
  domElement.style.background = `rgb(${r}, ${g}, ${b})`;
}

export { toggleHiddenElement, changeToFunkyColor };

I hope that would help clarify the issue I have. Thanks again!

Hi mtf,

Thanks for your answer!

Well, that is not what the lesson says. Could you check?

Also, as I have already explained above,

Just in case, this is what the lesson says (although I think I have already quoted this in my first message in this thread, as I am warned by the forum when trying to post this):

If you want to try this out on your own computer, you will need to spin up a local server or else you will run into CORS issues. Check out this article on creating a local server with VSCode and the Live Server extension.

We don’t have a link to the lesson/exercise in front of us so can only imagine what the narrative is. Transpiling code is a polyfill.

Maybe it’s not immediately obvious because I think the color of the link and the color of the username are pretty much similar, but I did include a link to the lesson. It’s at the top, in the first message of the thread. Here:

1 Like

Must defer to @mike7127143742 who appears to be more familiar with this topic. Having a pain management day and won’t be very active or much help.

I read through this lesson/article about 1 week ago so I was somewhat familiar with it when responding previously. At that time I did not build the project locally (or serve it with Live Server). There is always the possibility of something being modified from the instructions when building it locally so I appreciate that you posted the code you actually used and it looks fine.

I went ahead and built the project locally now and I used the code you pasted in. It works fine for me using both Firefox and Chrome (version 90) when started with Live Server from VS Code. No other tools or packages required. Just to confirm, I used the Live Server 5.6.1 VS-Code extension by Ritwick Dey. Is this the same one you used? And then after that one is installed, right-click on the HTML file in VS-code and select Open with Live Server. The secret-messages.html is really the file to test since this is the one that the article walks through converting to using modules.

2 more things. You can serve this project (or other html-files) locally in other ways (such as using a node.js server) as well but it is just so much easier to do it with Live Server from VS Code. Also, the lesson is more concerned with showing how to use modules than making sure that they work in older browsers so there is no reason to use a transpiler such as babel here.

3 Likes

Not sure what you mean or if I misunderstood there, but just in case, please take care and get better soon.

1 Like

Hi again Mike,

Well, right now I am both at a shock and feeling completely stupid :smiley:

Upon reading your latest response, I returned to the projects and opened them again to try it the way you said it, sure I was going to prove you wrong and show you how I still have the same problem, except… now, as you said, it worked!

Next, I was trying like for an hour to replicate my situation again, until I finally found a way. Apparently, if I Open Folder in VSC pointing at the root of one of the projects (like, in our case, say, secret-messages folder), when I try to access files outside that folder (like the one in modules folder) the extension (or the browser, or both!) will go awry giving the error I mentioned above (about MIME types!?).

Now, if I Open Folder from one level higher (and, like you said!, open Live Server from the html file, not from outside) there will be no problem.

Weird quirks that I may understand in depth some year in the future.

If you want, just for the sake of replicating the issue, try it yourself, and you’ll see the weird MIME type error you get. What’s more even. I have now checked a little deeper and if you check in Dev Tools, in Network tab, the source of that problem is actually double:

  1. It’s trying to serve the dom-functions.js from a level under the root (not outside); and
  2. It’s serving the dom-functions.js as an HTML file (see Type tab).

I write this explanation to thank you Mike for the explanation and also explain you why I was so clueless. Also, someone else might find themselves in a similar situation and, hey! it would have been so cool for me to have read something like this among the many, many searches I read since a couple of days ago but I found none. :man_facepalming: I even tried with --mount-path options of settings and I don’t even remember what things that made things worse if anything.

Thank you once again to both of you!

1 Like

Sorry, yet another brief comment. In addition to what I commented above, I now had the same problem again, but this time I was doing the same that worked above with the folders. The error was the MIME Type thing again.

I added ‘.js’ at the end of the import’s path and that solved it in this instance. Little quirks that might stumble beginners (like me).

Great that it works now. Just speculation, but it probably has to do with the scope of directories the Live-Server plug-in has access to (restricted for security reasons). It is very possible that configuration settings can change this but I wouldn’t spend time on that as opening the project root-directory works. It is fairly common to have an issue in development but not same issue in production (since the environments are different) or the other way around and this is probably the case here.

1 Like