Use addEventListener() for most real websites. Use HTML onclick only for tiny tests, simple demos, or quick prototypes. addEventListener() lets one button run many click functions without smashing them together, as shown in the MDN docs [source: MDN]. That saves your future self from muttering at 11:43 p.m. because one tiny button broke three other things.
TLDR: HTML onclick puts JavaScript right inside your HTML, like <button onclick="save()">. addEventListener() keeps JavaScript in your script file, like button.addEventListener("click", save). For a small landing page with 5 buttons, onclick may be fine, but for a dashboard with 40 buttons, addEventListener() can cut messy edits by half. A shop team fixing one checkout bug can avoid touching 12 HTML files if click logic lives in one JavaScript file.
What is an onclick event?
A click event is simple.
The user clicks something. The browser says, “Hey, something happened.” Your code reacts.
That “something” might be:
- A button press.
- A menu opening.
- A form being submitted.
- A heart icon turning red.
- A cart total going up by one.
JavaScript handles these little moments. They make pages feel alive. Not fancy. Just useful.
There are two common ways to handle a click:
- HTML
onclick - JavaScript
addEventListener()
They both work. They are not equal.
The easy one: HTML onclick
HTML onclick means you place JavaScript right on the HTML element.
<button onclick="sayHi()">Click me</button>
<script>
function sayHi() {
alert("Hi there!");
}
</script>
This is friendly for beginners. You can see the button and the action in one spot. It feels clear.
That is why tutorials love it.
It is also handy for tiny pages. If you are making a quick test, go for it. No shame. We have all written “just one quick button” and then pretended it was not real code.
Good uses for onclick:
- Small demos.
- Learning JavaScript basics.
- One page with one or two actions.
- Fast experiments.
But it gets annoying fast.
HTML is supposed to describe the page. JavaScript is supposed to control behavior. When you mix them too much, your code turns into a junk drawer. It still opens. Barely.
The cleaner one: addEventListener()
addEventListener() attaches a click action from JavaScript.
<button id="helloBtn">Click me</button>
<script>
const button = document.querySelector("#helloBtn");
button.addEventListener("click", sayHi);
function sayHi() {
alert("Hi there!");
}
</script>
The HTML stays clean. The JavaScript handles the behavior.
This feels like a small change. It is not. On bigger projects, it is a sanity saver.
You can add more than one listener to the same button:
button.addEventListener("click", saveData);
button.addEventListener("click", showMessage);
button.addEventListener("click", trackClick);
Nice. No wrestling match inside one attribute.
With onclick, you often end up doing this:
<button onclick="saveData(); showMessage(); trackClick();">
Save
</button>
It works. But it looks like a spaghetti fork fight.
Why addEventListener() is usually better
1. Your HTML stays readable.
A button should look like a button. Not like a button carrying a backpack full of JavaScript.
<button id="saveBtn">Save</button>
That is clean. A designer can read it. A developer can target it. Nobody squints.
2. You can attach many actions.
This is a big deal.
One click might need to save data, show a loader, send tracking data, and disable the button. With addEventListener(), each job can live in its own function.
Small functions are easier to test. Easier to fix. Easier to delete when the product manager changes their mind again.
3. You can remove listeners.
Sometimes you need to stop listening.
button.removeEventListener("click", saveData);
This is useful for popups, games, sliders, and apps with changing screens.
HTML onclick is not as flexible. You can set it to null, sure. But it feels clunky.
4. It supports options.
addEventListener() can use extra settings, such as once.
button.addEventListener("click", launchConfetti, { once: true });
Now the confetti runs one time only.
That is perfect for signup success, payment success, or “you found the secret duck” moments.
Where HTML onclick gets messy
HTML onclick is not evil. It is just easy to overuse.
Here is the pain.
- It mixes structure and behavior. HTML and JavaScript get tangled.
- It is harder to reuse. You may copy the same code across many buttons.
- It can be harder to test. Logic is scattered through markup.
- It ages badly. A tiny page can become a not tiny page. Surprise.
Honestly, it feels like putting sticky notes on every kitchen cabinet instead of writing one shopping list.
Here is a classic mess:
<button onclick="openMenu(); changeIcon(); logMenuClick();">
Menu
</button>
Now imagine 30 buttons like that.
Expect to waste time on tiny edits. The kind that should take 20 seconds but somehow take 12 minutes because the same logic is hiding in six files.
What about performance?
For normal websites, both are fast enough.
Your user will not notice a speed difference between one onclick and one addEventListener(). The browser handles both quickly.
The real win is not raw speed. It is maintenance speed.
If a bug takes 8 minutes to find with clean event code, but 28 minutes with scattered inline clicks, that matters. Multiply that by 10 bugs each month. Now your team has lost over 3 hours to avoidable clicking chaos.
That number is not magic. It is just the normal tax of messy code.
Event delegation: the cooler trick
addEventListener() also works well with event delegation.
That means you listen on a parent element. Then you check what child was clicked.
<ul id="todoList">
<li>Buy milk</li>
<li>Feed cat</li>
<li>Write code</li>
</ul>
<script>
const list = document.querySelector("#todoList");
list.addEventListener("click", function(event) {
if (event.target.tagName === "LI") {
event.target.classList.toggle("done");
}
});
</script>
Now every list item can react to clicks. Even new ones added later.
This is great for:
- Todo lists.
- Product grids.
- Menus.
- Comment sections.
- Admin tables.
Instead of adding 100 listeners to 100 items, you add one listener to the parent. Clean. Calm. Pleasantly boring.
When should you use each one?
Use HTML onclick when:
- You are learning.
- You are building a tiny demo.
- You need a quick test.
- The code will probably be deleted soon.
Use addEventListener() when:
- You are building a real site.
- You have many buttons.
- You work with a team.
- You need clean files.
- You may add or remove actions later.
- You care about testing.
A simple rule
If the project has one button, onclick is fine.
If the project has many buttons, use addEventListener().
If other people will touch the code, use addEventListener().
If you will touch the code in three months, also use addEventListener(). Future you deserves snacks, not stress.
Best practice example
Here is a clean pattern:
<button id="buyBtn">Buy now</button>
<script>
const buyButton = document.querySelector("#buyBtn");
buyButton.addEventListener("click", handleBuyClick);
function handleBuyClick() {
showLoading();
addToCart();
showSuccessMessage();
}
</script>
This is easy to read. Each step can become its own function. If tracking is needed later, add one line. No HTML surgery required.
The final pick
Pick addEventListener() as your default. It keeps code clean. It scales better. It supports multiple handlers. It plays nicely with modern app patterns.
Keep onclick in your pocket for quick demos and tiny experiments.
Both tools can open the door. But addEventListener() gives you the key, the spare key, and a neat little hook to hang them on.