Skip to main content
whitep4nth3r logo

Ramblings about JavaScript scope, weird errors and frameworks

I did learn the thing. But I forgot to remember the thing.

My website had a silly bug in production for a year.

It was quite inconsequential (I think), and I only found it when I (finally) added Sentry error monitoring to the stack (yay!). But it got me thinking about some of the things we’re starting to take for granted in the web framework world — and how we might get tripped up as a result.

SyntaxError: Identifier 'button' has already been declared

My website is built with Eleventy. I’m using the Eleventy JavaScript template language and this is mainly because I moved over from Next.js and wanted to retain much of the React-based component patterns I was used to at the time.

In my header component, which is an HTML file added as an “include” on my base layout using Liquid syntax, there’s a block of inline JavaScript that powers the dark and light theme toggle, selecting the toggle button and assigning it to the variable name button.

const button = document.querySelector("[data-theme-toggle]");

On my blog index page, I import a separate JavaScript file that powers search functionality powered by Algolia. In this file, I manually create the “reset search” button for the search form, and assign it to the variable name — yes, you guessed it — button.

const button = document.createElement("button");

On the blog index page is where this error triggered, given that both button variables were declared with the same name. But what’s interesting is that I didn’t see this error in the browser console in development because the variables were assigned in different scopes: the theme toggle button was in global scope, and the clear search button was in function scope. (Get the scoop on scopes on MDN.)

<!-- theme toggle button defined in global scope in header.html -->

<script>
const button = document.querySelector("[data-theme-toggle]");
</script>
// reset search button defined in function scope in app_search.js

const renderSearchBox = (renderOptions, isFirstRender) => {
// ...
if (isFirstRender) {
// ...
const button = document.createElement("button");
button.textContent = "Clear";
// ...
}
}

Given that I saw no errors in the browser console in development or production pertaining to this issue, and given that the button variables were scoped differently, I inspected the issue further in Sentry to discover that what was actually picked up was a bit of a weird issue in development whilst the Eleventy dev server was reloading some changes, as demonstrated by the stack trace.

Sentry issue view showing Syntax Error, identifier button has already been declared. The stack trace shows minified files from the eleventy dev server.

But this still got me thinking. Whilst this definitely supports the “don’t use global scope” in JavaScript argument just in case you run into real issues like this, I think it highlights a different issue.

Framework patterns obfuscate the fundamentals

As developers using modern web frameworks, we’ve become accustomed to our JavaScript being scoped and encapsulated by default to the file in which we’re working. Maybe I’m just speaking for myself, but after years of working with libraries such as React, and meta-frameworks that use libraries such as React, I didn’t stop to consider at this point that the inline JavaScript I was writing in my small header component, in global scope, could have an impact on any other JavaScript included anywhere else.

In 2022, I wrote a blog post where I detailed my journey of refactoring my blog from Next.js to Eleventy, and I said this:

It took a little while to shift my thinking from Next.js to Eleventy. Next.js has a justifiably opinionated way of architecting a front end application — and that's fine — but in moving to Eleventy, it showed me I had perhaps become too reliant on the patterns of Next.js. In going back to web basics with Eleventy, and focussing on shipping plain HTML, CSS and JavaScript to the browser, I feel like I've refreshed and reinvigorated my knowledge of how the web works natively.

So I did learn the thing. But I forgot to remember the thing. If you’ve read this far, why not go back in time and see what you forgot to remember you learned? I need to do it more often.

Like weird newsletters?

Join 338+ subscribers in the Weird Wide Web Hole to find no answers to questions you didn't know you had.

Subscribe

Salma is looking at you, with a rather large smile. She's pointing across herself up to her left, with a very tatooed arm. She's wearing a black shirt and black rimmed glasses.

Salma Alam-Naylor

I'm a live streamer, software engineer, and developer educator. I help developers build cool stuff with blog posts, videos, live coding and open source projects.

Related posts

29 Mar 2022

How I massively improved my website performance by using the right tool for the job

I rebuilt my website AGAIN with the aim of using as little JavaScript as possible to improve performance. Did I succeed? And what did I learn?

Web Dev 10 min read →

19 Jun 2023

The best light/dark mode theme toggle in JavaScript

Learn how to build The Ultimate Theme Toggle™️ for your website using JavaScript, CSS custom properties, local storage and system settings.

CSS 4 min read →