4
\$\begingroup\$

I wanted to detect when a user stops typing. I encountered this answer from SO (Not the selected answer, but the second one which seem to be better):

https://stackoverflow.com/a/5926782/17746636

But I edited it slightly to use keydown instead of keyup. Also the target is window for my example (I need to detect any key strokes on the window):

let typingTimer;
let doneTypingInterval = 1000;

window.addEventListener('keydown', () => {
  clearTimeout(typingTimer);
  typingTimer = setTimeout(doneTyping, doneTypingInterval);
});

function doneTyping() {
  console.log("Stopped typing");
}

I changed from keyup to keydown because there might be cases where a user can click on another window while typing, and then the timeout will never be released. Using keydown seem to work better and ensures that if a user is typing and then clicks on another window, then it will trigger the doneTyping function

\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

Structurally, there is nothing wrong with the approach.

  • You could consider const for doneTypingInterval, I would put a comment that this is in milliseconds
  • I would create a self-executing function to hide typingTimer from the global scope.
    • doneTypingInterval could then be a parameter or remain a constant
    • doneTyping could be a local function, or could be a parameter
  • I am not the biggest fan of anonymous functions, you could consider naming it

function doneTyping() {
  console.log("Stopped typing");
}

(function setupTypingDetection(element, maximumDelayBetweenKeystrokes, postTypingFunction) {
  let typingTimer;

  element.addEventListener('keydown', () => {
    clearTimeout(typingTimer);
    typingTimer = setTimeout(postTypingFunction, maximumDelayBetweenKeystrokes);
  });
})(window, 1000, doneTyping);

\$\endgroup\$
1
  • 3
    \$\begingroup\$ Instead of a comment, you could rename doneTypingInterval to something that includes the unit, such as maxIdleMilliseconds or something. \$\endgroup\$ Commented Oct 16, 2023 at 14:45
2
\$\begingroup\$
let typingTimer;
let doneTypingInterval = 1000;

window.addEventListener('keydown', () => {
  clearTimeout(typingTimer);
  typingTimer = setTimeout(doneTyping, doneTypingInterval);
});

If you need this behaviour more than once it might be a good idea to abstract the general idea of delayed execution and resetting the timer when called early.

function fnDelay(fn, msDelay) {
  let timeoutID;
  return function (...args) {
    clearTimeout(timeoutID);
    timeoutID = setTimeout(fn, msDelay, ...args);
  };
}

This abstraction would allow you to transform your code into:

window.addEventListener('keydown', fnDelay(doneTypeing, 1000));

function doneTyping() {
  console.log("Stopped typing");
}

ps. fnDelay is probably not the best name for this function, but I currently can't come up with a better one.


I personally also like to define time units as constants.

//           [------- unit definition ------] [--------- alias ---------]
export const MILLISECOND =    1             , MILLISECONDS = MILLISECOND;
export const SECOND      = 1000*MILLISECONDS, SECONDS      = SECOND     ;
export const MINUTE      =   60*SECONDS     , MINUTES      = MINUTE     ;
export const HOUR        =   60*MINUTES     , HOURS        = HOUR       ;
export const DAY         =   24*HOURS       , DAYS         = DAY        ;
export const WEEK        =    7*DAYS        , WEEKS        = WEEK       ;

This might seem overkill, but these constants are used quite often. I find fnDelay(doneTypeing, 1*SECOND) more expressive/readable than fnDelay(doneTypeing, 1000).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.