Enter, it screams... I mean, it streams
Tags: javascript, eventdriven, programming
Published: 2023-07-28 14:31:12

Have you ever thought to write an event listener like the following?

```js
const button = document.querySelector('button');

button.addEventListener(
    'click',
    () => {
        console.log('click');
    }
);
```

And then, gotten reports that the actual work that you listed (please don't ship `console.log()` statements to production) was happening way too often?

Maybe you wrote your listener like this, and got similar reports?

```js
const button = document.querySelector('button');

button.addEventListener(
    'keydown',
    ({ code }) => {
        if (code === 'Enter') {
            console.log('Enter'));
        }
    }
);
```

If you've already caught the context, don't ruin the surprise...

## Keyboard-based interactions

To mimic pointer interactions when navigating an application with the keyboard, when the `Space` key is pressed and a `<button>` element is in focus, the `keyup` event from that interaction is duplicated as a `click` event. Similarly, the `keydown` event (or `keypress` event, if you're into deprecated APIs) on the `Enter` key, in the same situation, will be duplicated as a `click` event.

However, the `Enter` key is special. The `Enter` key screams...I mean, streams its `keydown` event. That means as long as a visitor has their `Enter` key down the `keydown` event, which usually is tightly coupled to when the key _goes down_, will continue to fire. This means that our `keydown` listener AND the `click` listener in the above code sample will stream as well.

## My own haunted house

I was recently surprised by this reality when a listener like this that I have bound threw focus into a different element that also had a listener like this bound, which subsequently through focus back into the first element and created a loop 😱

```js
const button1 = document.querySelector('button.one');
const button2 = document.querySelector('button.two');

button1.addEventListener(
    'click',
    () => {
        button2.focus();
    }
);

button2.addEventListener(
    'click',
    () => {
        button1.focus();
    }
);
```

_Normally_, the event listeners worked exactly as planned, but then came the bumps in the night... luckily, I have great coworkers that helped catch this use case at code review time, before it went live to users. 😅

## Enter at your own risk

Maybe yelling the event listener at your visitor is the right thing to do in the context of your application, but, if it is not, key this reality in mind to ensure that your application delivers the expected experience.

If you're interested in coalescing the interaction with the `Enter` key to a one-time event, you might be interested in the `keyup` event:


```js
const button = document.querySelector('button');

button.addEventListener(
    'keyup',
    ({ code }) => {
        if (code === 'Enter') {
            console.log('Enter'));
        }
    }
);
```

If you're actually working with a `click` event, you may want to do a more complex form of gating:

```js
let enterKeydown = false;

const button = document.querySelector('button');

button.addEventListener(
    'keydown',
    (event) => {
        if (event.code === 'Enter') {
            if (enterKeydown) {
                event.preventDefault();
            }
            enterKeydown = true;
        }
    }
);

button.addEventListener(
    'keyup',
    (event) => {
        if (event.code === 'Enter') {
            enterKeydown = false;
        }
    }
);

button.addEventListener(
    'click',
    () => {
        console.log('click');
    }
);
```

Or, maybe you have an alternative way to not scare your visitors? If so, share it in the comments below! I'd love to hear what you've been doing to prevent the `Enter` key from screaming at anyone who has to listen to your application.