0

I have a node server for a webpage with the following folder structure:

server.js
public/
    |_ index.html
    |_ main.js
    |_ pages_content/
        |_contents.js

server.jsserves content in public/:

var express = require('express');
var app = express();
var path = require('path');
var public = path.join(__dirname, 'public');

app.get('/', function(req, res) {
    res.sendFile(path.join(public, 'index.html'));
});

app.use('/', express.static(public));

app.listen(8080);

html.js defines a navbar, whose items have a function for switching current active navItem:

<body>
        <nav id = "navbar">
            <div id = "projects" onClick="renderContent(this)" class = "navItem activeNavItem">PROJECTS</div>
            <div id = "experiences" onClick="renderContent(this)" class = "navItem">EXPERIENCE</div>
            <div id = "resume" onClick="renderContent(this)" class = "navItem">RESUME</div>
        </nav>

    <script  type="module" src="/pages_content/contents.js"></script>
    <script  type="module" src="main.js"></script> 
</body>

main.js defines the renderContent()function that will activate current nav item and lately insert content of correspondent nav item into body:

import {PROJECTS_CONTENT} from '/pages_content/contents.js'

const renderContent =  function(navItem){
    contentOptionId = navItem.id
    activateNavbar(contentOptionId)
    console.log(PROJECTS_CONTENT)
}

const activateNavbar = function(optionId){
    NAVBAR_SELECTOR = "#navbar"
    ACTIVE_ITEM_CLASS = "activeNavItem"
    const navBar = document.querySelector(NAVBAR_SELECTOR)
    for (navItem of navBar.children){
        if(navItem.id === optionId){
            navItem.classList.add(ACTIVE_ITEM_CLASS)
        } else {
            navItem.classList.remove(ACTIVE_ITEM_CLASS)
        }
    }
}

Finally, /page_content/contents.js holds an object with the contents to be rendered:

const PROJECTS_CONTENT = [
    {
        "title": "Certificate Generator",
        "firstParagraph": "Node.js batch script that parses a .csv file and generates .pdf certificates for each line/person",
        "firstImgUrl": "images/certificate0.png",
        "secondImgUrl": "images/certificate.png",
        "projectUrl": "https://github.com/USPCodeLabSanca/Node-Batch-Certificate-Generator",
        "secondParagraph": "Mentor of the project, responsible for idealizing, writing requirements, spliting requirements in features divided across team and coding",
        "thirdParagraph": "Used in real life recurrently for generating USPCodelab courses, hackathons and other events certificates."
    }
]

export {PROJECTS_CONTENT}

I'm having issues importing PROJECTS_CONTENT variable from a secondary file. I made both scripts a module, exported PROJECTS_CONTENT on its file and imported it at main.js.

This seems to have made PROJECTS_CONTENT visible at main.js because no more error are being thrown at console.

However, the function renderContent(), that used to work before messing up with modules, now throws an error:

Uncaught ReferenceError: renderContent is not defined
    onclick http://localhost:8080/:1

I've tried multiple things, such as exporting renderContent as another stackoverflow question suggested:

import {PROJECTS_CONTENT} from '/pages_content/contents.js'

export const renderContent =  function(navItem){
    contentOptionId = navItem.id
    activateNavbar(contentOptionId)
    console.log(PROJECTS_CONTENT)
}

export const activateNavbar = function(optionId){
    NAVBAR_SELECTOR = "#navbar"
    ACTIVE_ITEM_CLASS = "activeNavItem"
    const navBar = document.querySelector(NAVBAR_SELECTOR)
    for (navItem of navBar.children){
        if(navItem.id === optionId){
            navItem.classList.add(ACTIVE_ITEM_CLASS)
        } else {
            navItem.classList.remove(ACTIVE_ITEM_CLASS)
        }
    }
}

But none of them seem to work. How can this be fixed? Thanks a lot in advance

nluizsoliveira
  • 355
  • 1
  • 9
  • 1
    A module is "module scoped", functions, variables etc. that are defined in a module are only available from within that module (unless exported and then imported in another module). So you can't access `renderContent` in your HTML because `renderContent` is not available globally. You should use `addEventListener()` instead of `onClick` attribute inside of `main.js` which will mean that `renderContent` doesn't need to be accessed anymore globally. – Nick Parsons Jul 24 '22 at 05:28
  • 1
    @NickParsons Oh, I wasn't aware of that, thank you very much! I'll try it and tell you if it works ASAP – nluizsoliveira Jul 24 '22 at 05:29
  • @NickParsons It worked!! Thank you very very much! ```const NAVBAR_SELECTOR = "#navbar" const navBar = document.querySelector(NAVBAR_SELECTOR) for(const navItem of navBar.children){ navItem.addEventListener('click', (e)=>{ renderContent(e) }) } ``` This solved the issue, answer the question if you desire so I can mark you as accepted answer – nluizsoliveira Jul 24 '22 at 05:35
  • No worries, I think I've seen a similar question to this before, I'll try and find that and link it to this one instead of adding an answer :) – Nick Parsons Jul 24 '22 at 05:36
  • Also I needed to adapt renderContent() to handle target: ``` export const renderContent = function(clickedNavItem){ const contentOptionId = clickedNavItem.target.id activateNavbar(contentOptionId) console.log(PROJECTS_CONTENT) } ``` – nluizsoliveira Jul 24 '22 at 05:38
  • 1
    Btw, you can reduce the number of event listeners you're using by using [event-delegation](https://javascript.info/event-delegation), `navBar.addEventListener("click", e => {renderContent(e.target)})`. Passing `e.target` allows you to keep `renderContent` the same as it was in your question also. – Nick Parsons Jul 24 '22 at 05:46

0 Answers0