I ran into a silly typo that cost me way too much time, I wrote oneclick
instead of onclick
and VS Code didn’t complain. Nothing happened in the browser, no console error, and I kept staring at the code wondering what on earth I broke. If you’ve been there, this post is exactly what I wish I had read first.
Below, I’ll show the original buggy snippet, explain why VS Code stayed quiet, and then walk through a sturdier, practice-ready refactor that’s easier to maintain, more accessible, and much better at surfacing mistakes. I’ll also add some “try this at home” practice ideas and a final checklist so you don’t get tripped up by this again.
The Original Snippet
<button type="button"
oneclick="document.getElementById('a').style.visibility='visible'">
Click
</button>
<img id="a" style="visibility:hidden" src="pico.png" alt="src not found">
What’s The Error
I typed oneclick
. The valid HTML event attribute is onclick
.
Because oneclick
isn’t a recognized attribute, the browser just ignores it. That means no JavaScript runs, and there’s no error it’s perfectly legal (if useless) HTML to the browser.
Why Didn’t VS Code Flag it
I assumed “surely my editor will tell me.” It didn’t here’s why:
- HTML is permissive. Unknown attributes aren’t illegal; they’re ignored. So linters have to opt into warning you.
- VS Code’s built-in HTML support isn’t a strict validator. It helps with hints and completions but won’t fail your file for unknown attributes.
- Common HTML linters don’t warn by default. Tools like HTMLHint often focus on formatting, duplicates, etc. Checking attribute names requires stricter rules or a different validator.
How I Make this Class of Bugs Much Harder to Write
There are two fixes: process and code.
Process Use a Stricter Validator
Install a validator that knows real HTML attributes and will bark at unknown ones:
- HTML Validate (a.k.a.
html-validate
) has a VS Code extension and CLI. - Turn on rules for unknown attributes so
oneclick
gets flagged.
Quick start (CLI):
npm i -D html-validate
npx html-validate "**/*.html"
Minimal .htmlvalidate.json
:
{
"extends": ["html-validate:recommended"],
"rules": {
"attr-unknown": "error",
"no-inline-style": "warn",
"no-inline-event-handler": "warn"
}
}
With that, oneclick
becomes a red, obvious error.
Prefer JS Listeners Over Inline Attributes
Inline onclick
is easy to mistype and hard to lint. When I move behavior into JavaScript (addEventListener
), I get editor/TypeScript/ESLint help, autocomplete, and real errors for typos like 'clcik'
.
My Fix + Enhance Version
This refactor:
- Removes inline JS (
onclick
) and usesaddEventListener
. - Uses a
.hidden
class instead of inlinestyle
. - Adds Show / Hide / Toggle buttons.
- Improves accessibility (keyboard + ARIA).
- Handles image load errors visibly.
Copy this into an index.html
and open it in the browser:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Show/Hide Image Practice</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
.hidden { visibility: hidden; }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0, 0, 1px, 1px); white-space: nowrap; border: 0;
}
.toolbar { display: flex; gap: .5rem; margin-bottom: .75rem; }
button { padding: .5rem .75rem; }
#status { margin-top: .5rem; color: #b00020; }
</style>
</head>
<body>
<h1>Show/Hide Image (Practice)</h1>
<div class="toolbar" role="group" aria-label="Image visibility controls">
<button type="button" id="btn-show">Show</button>
<button type="button" id="btn-hide">Hide</button>
<button type="button" id="btn-toggle">Toggle</button>
</div>
<figure>
<img id="a" class="hidden" src="pico.png" alt="A small demo image" aria-hidden="true" />
<figcaption class="sr-only" id="img-caption">Demo image visibility example</figcaption>
</figure>
<p id="status" role="status" aria-live="polite"></p>
<script>
const img = document.getElementById('a');
const status = document.getElementById('status');
const setVisible = (visible) => {
img.classList.toggle('hidden', !visible);
img.setAttribute('aria-hidden', String(!visible));
status.textContent = visible ? 'Image is now visible.' : 'Image is now hidden.';
};
document.getElementById('btn-show').addEventListener('click', () => setVisible(true));
document.getElementById('btn-hide').addEventListener('click', () => setVisible(false));
document.getElementById('btn-toggle').addEventListener('click', () => {
const isHidden = img.classList.contains('hidden');
setVisible(isHidden);
});
// Keyboard: press Enter on image to toggle visibility
img.setAttribute('tabindex', '0');
img.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
const isHidden = img.classList.contains('hidden');
setVisible(isHidden);
}
});
// Handle load errors (e.g., wrong src filename)
img.addEventListener('error', () => {
setVisible(false);
status.textContent = 'Could not load the image (check the src path).';
});
</script>
</body>
</html>
What I Change
- Replaced
oneclick/onclick
withaddEventListener
- Safer and testable. Editor autocompletes valid event names; TypeScript/ESLint can catch typos.
- Swapped inline style for a CSS class
.hidden
is easier to toggle and keeps structure/behavior/presentation separated.
- Added status feedback
<p id="status" role="status" aria-live="polite">
announces changes for screen readers and gives me visible feedback for debugging.
- ARIA & keyboard support
aria-hidden
reflects the visual state.tabindex="0"
and Enter to toggle helps keyboard users and is great for testing.
- Error handling for broken
src
- If
pico.png
is wrong, I get a clear message instead of guessing.
- If
Make VS Code Actually Complain
If you want the editor to catch these earlier:
HTML Validate extension (or CLI)
- Install HTML Validate in VS Code.
- Add
.htmlvalidate.json
(shown earlier). - Reopen your HTML file now
oneclick
is a real error.
ESLint + TypeScript for JS logic
Even if you keep plain JS, ESLint helps a lot. With TypeScript, typos in event names or DOM IDs get much harder to miss.
Quick start:
npm i -D typescript eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
npx tsc --init
Minimal .eslintrc.json
:
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"env": { "browser": true, "es6": true }
}
Move your script to main.ts
, then compile with tsc
. If you mistype 'click'
as 'clcik'
, TypeScript/ESLint will complain.
Practice Drills I Use
- Intentional typo drill
Changeid="a"
toid="imgA"
but forget to update the JS selector once. Watch how your console and linter react. - Visibility vs layout
Swap.hidden { visibility: hidden; }
for.hidden { display: none; }
. Notice how layout reflows when the element is removed from the layout. - CSS fade-in/out
Add a transition for opacity:img { transition: opacity .2s ease; } .hidden { opacity: 0; visibility: hidden; }
And toggle bothopacity
andvisibility
for smooth animations. - Validator check
Reintroduceoneclick
on purpose and confirm your validator flags it now. - Type-check events
Convert the script to TypeScript and tryaddEventListener('clcik', ...)
. Enjoy the immediate red squiggle.
Final Thought
I blamed VS Code for not yelling at me but the real fix was tightening my toolchain and changing how I write behavior. Browsers are relaxed about unknown attributes, so editors won’t always save me. By moving logic to JavaScript with addEventListener
, adding a real HTML validator, and leaning on TypeScript/ESLint, I turned a silent failure into an immediate, obvious error.