WindowsMaker
WindowsMaker

Reputation: 3400

Javascript onkeydown event fire only once?

I want to have a onkeydown event fire a function only once. for that function to fire again, the user has to release the key and press/hold again. I know its fairly simple but I'm new at JS. Also I prefer to avoid using jQuery or other libs. One more thing, this should work for both ie and firefox.

Upvotes: 32

Views: 44829

Answers (8)

karmaral
karmaral

Reputation: 542

I'm surprised it's not mentioned, there's also event.repeat:

document.addEventListener('keydown', (e) => {
  if (e.repeat) return;

  console.log(e.key);
});

This will only fire once per each keypress, since event.repeat turns true after holding the key down.

https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key#keyboardevent_sequence

Upvotes: 43

Alistair R
Alistair R

Reputation: 926

There's a "once" parameter you can use

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

Eg:

element.addEventListener('keydown', function(event) {  
    doSomething()
}, {once: true});

It'll remove it as soon as it's been called.

Alternatively you can use removeEventListener if it's a named function

Upvotes: 6

V. Rubinetti
V. Rubinetti

Reputation: 1716

Here is my solution that will only run the function you pass it when a key is FIRST pressed on the target (eg window or some input field). If the user wants to trigger a key again, they'll have to release it and press it again.


Vanilla JS

const onKeyPress = (func, target = window) => {
  // persistent "store" to track what keys are being pressed
  let pressed = {};

  // whenever a keydown event is fired ontarget element
  const onKeyDown = (event) => {
    // if key isn't already pressed, run func
    if (!pressed[event.which])
      func(event);

    // add key to store
    pressed = { ...pressed, [event.which]: true };
  };

  // whenever a keyup event is fired on the window element
  const onKeyUp = (event) => {
    const { [event.which]: id, ...rest } = pressed;
    // remove key from store
    pressed = rest;
  };

  // add listeners
  target.addEventListener('keydown', onKeyDown);
  window.addEventListener('keyup', onKeyUp);

  // return a function that can be called to remove listeners
  return () => {
    target.removeEventListener('keydown', onKeyDown);
    window.removeEventListener('keyup', onKeyUp);
  };
};

And then to use it:

const removeListener = onKeyPress((event) => console.log(event.which + ' key pressed'))

removeListener(); // when you want to remove listeners later

React and React Hooks

import { useState } from 'react';
import { useEffect } from 'react';
import { useCallback } from 'react';

export const useKeyPress = (func, target = window) => {
  // persistent "store" to track what keys are being pressed
  const [pressed, setPressed] = useState({});

  // whenever a keydown event is fired ontarget element
  const onKeyDown = useCallback(
    (event) => {
      // if key isn't already pressed, run func
      if (!pressed[event.which])
        func(event);

      // add key to store
      setPressed({ ...pressed, [event.which]: true });
    },
    [func, pressed]
  );

  // whenever a keyup event is fired on the window element
  const onKeyUp = useCallback((event) => {
    // remove key from store
    const { [event.which]: id, ...rest } = pressed;
    setPressed(rest);
  }, [pressed]);

  useEffect(() => {
    // add listeners when component mounts/changes
    target.addEventListener('keydown', onKeyDown);
    window.addEventListener('keyup', onKeyUp);

    // cleanup/remove listeners when component unmounts/changes
    return () => {
      target.removeEventListener('keydown', onKeyDown);
      window.removeEventListener('keyup', onKeyUp);
    };
  }, [target, onKeyDown, onKeyUp]);
};

And then to use it:

import { useKeyPress } from 'wherever';

useKeyPress((event) => console.log(event.which + ' key pressed'))

Upvotes: 0

josh
josh

Reputation: 384

as stated in the other answers, there is no 'onkeyfirstdown' or similar event to listen for.

the best solution is to keep track of which keys are already down in a js-object:

var keysdown = {};

element.addEventListener('keydown', function(evt) {
  if(!(evt.key in keysdown)) {
    keysdown[evt.key] = true;
    // key first pressed
  }
});

element.addEventListener('keyup', function(evt) {
  delete keysdown[evt.key];
});

this way, you will not be skipping 'keyfirstpressed' events if more than one key is held down.

(many of the other solutions posted here will only fire when no other keys are down).

Upvotes: 0

Mark Coleman
Mark Coleman

Reputation: 40863

Here is a method that uses addEventListener and removeEventListener

var textBox = document.getElementById("textBox");
function oneKeyDown(){
    $("body").append("<h1>KeyDown<h1>"); //just to show the keypress
    textBox.removeEventListener('keydown', oneKeyDown, false);
}
function bindKeyDown(){
  textBox.addEventListener('keydown', oneKeyDown, false);
}
textBox.addEventListener('keyup', bindKeyDown, false)   
bindKeyDown();

Code example on jsfiddle.

One note, for IE you will need to use attachEvent, detachEvent.

Upvotes: 2

Felix Kling
Felix Kling

Reputation: 816462

You could set a flag:

var fired = false;

element.onkeydown = function() {
    if(!fired) {
        fired = true;
        // do something
    }
};

element.onkeyup = function() {
    fired = false;
};

Or unbind and rebind the event handler (might be better):

function keyHandler() {
     this.onkeydown = null;
     // do something
}

element.onkeydown = keyHandler;

element.onkeyup = function() {
    this.onkeydown = keyHandler;
};

More information about "traditional" event handling.

You might also want to use addEventListener and attachEvent to bind the event handlers. For more information about that, have a look at quirksmode.org - Advanced event registration models.

Upvotes: 30

Šime Vidas
Šime Vidas

Reputation: 185923

Here you go:

test.onkeydown = function() {
    if ( this.className === 'hold' ) { return false; }
    this.className = 'hold';

    // call your function here
};

test.onkeyup = function() {
    this.className = '';
};

Live demo: http://jsfiddle.net/simevidas/xAReL/2/

Upvotes: 1

simplyharsh
simplyharsh

Reputation: 36373

JQuery's one will help you.

What it does is, bind the eventHandler to event, and when event occurs, it runs the eventHandler and unbinds it, so that its not fired at next event.

Upvotes: 0

Related Questions