Enter, it screams... I mean, it streams

Westbrook Johnson - Jul 28 '23 - - Dev Community

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

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

button.addEventListener(
    'click',
    () => {
        console.log('click');
    }
);
Enter fullscreen mode Exit fullscreen mode

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?

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

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

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 😱

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

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

button2.addEventListener(
    'click',
    () => {
        button1.focus();
    }
);
Enter fullscreen mode Exit fullscreen mode

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:

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

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

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

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');
    }
);
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player