preventDefault is an anti-pattern

On a large number of sites and in quite a few code bases, I’ve seen an html/js combination that looks something like this:

<a href="#" id="unique">Click Me</a>

<script>
var ele = document.getElementById('unique');
ele.addEventListener('click', function(event) {
  event.preventDefault();
  window.alert('Thanks for clicking!');
});
</script>

This use of preventDefault is tempting thing to write if the element you want to use for javascript interactivity is something that you want to have styled like the rest of the links on the page. However, to the person considering using that pattern, I would ask the question “If you don’t want the user to be taken to a new URI, why are you using an anchor tag?”

I would argue the following code is no more cumbersome to write, avoids some tricky bugs, and has the benefit of being more correct:

<style>
.link-like {
  color: blue;
  text-decoration: underline;
  cursor: pointer;
}
</style>

<span id="unique" class="link-like">Click Me</a>

<script>
var ele = document.getElementById('unique');
ele.addEventListener('click', function(event) {
  window.alert('Thanks for clicking!');
});
</script>

A guiding principle I use when I design front-end projects is:

  • HTML is what it “is”.
  • CSS is what it looks like.
  • JS is how it should behave.

In the first example we were letting how we wanted an element to look drive what it “is” and as a result we were complicating our code for how it should behave. By separating our concerns, we allow each part of our HTML, CSS, and JS code do what it was meant to do.

Another common time when preventDefault is used is to override the submit button in a form. This is where things get a little more tricky, because the developer might be using javascript to perform some client-side validation before allowing the form to be submitted. I think a good way of determining if this is a good time to be using preventDefault is asking “what should happen to the user that has javascript disabled?”.

Even in that instance, preventDefault may not be what we want though. For the cases where preventDefault allows us to provide progressive enhancement, we could use javascript to disable the submit button and provide helpful feedback to the user about why the form contents aren’t yet valid.

Thanks for reading! If you have thoughts (particularly if there is a valid use-case I haven’t thought of), I’d love to hear them.