Defining variables outside of event listener breaks my code

Hi,

I have been working on a little project to practice DOM manipulation - https://glitch.com/~toggle-password-visibility

There are two forms on the page with one or two password fields and a checkbox. My goal was to write a code that toggles visibility of the password fields depending on the state of the corresponding checkboxes.

I managed to write a working code, but when I started refactoring it, I noticed that it stops working when I move the loginForm and updatePassword form declarations outside of the event listener.

It works fine here on glitch only when I ‘unlink’ the external script.js file and move the code inside script tags at the bottom of the index.html.

I would like to understand why this happens.

You have to wait to an event when the page loads:

window.addEventListener("load", () => {})

Otherwise, all your variables will be undefined.
P.S. If I made this demo I would listen for updates from actual DOM elements:

let loginCheckbox = document.getElementById("show-pwd-login")
let updateCheckbox = document.getElementById("show-pwds-update")

loginCheckbox.addEventListener("click", event => {
  showPassword({ 
    el: loginCheckbox, 
    state: event.checked
  })
})
...
1 Like

Hey @Vladimir to add to what @jarvis394 says, I’ve only looked at this for a bit but for a little more detail I think the root issue you’re seeing is related to scope and closures.

When you put the declarations inside of the event handler and include the script before the body of the page, the declarations are evaluated when the event handler fires, and loginForm and updatePasswordForm are properly set because when you click and trigger the event handler the page’s html is available for access by document.querySelector.

When you move them outside of the event handler, loginForm and updatePasswordForm are “closed over” inside the event handler - that is to say that the values of those variables are captured in the state they’re in when the event handler is defined, and because they’re above the page’s html, document.querySelector can’t find those fields and those values are null.

When you move everything to the bottom of the file, when the variables are defined they have values because the html that document.querySelector is trying to access already exists. Then you capture those values when you define the event handler and they’re available for the handler code to use.

The MDN article on Closures is pretty detailed but might provide some valuable information.

What I haven’t dug into is why they work on codepen or on your laptop.

1 Like

Thank you @cori and @jarvis394. I understand what the problem was now. When I move the script tag (that links to the external file) from the head to the bottom of the body, it works fine.

As for refactoring the code, I know that this is not the optimal way of doing this and I plan on improving it. At first, my goal was to get it to work and then I came across the problem I mentioned above and got a bit sidetracked by trying to understand what’s going on.

1 Like