最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Javascript keydown function conflict on input focus - Stack Overflow

programmeradmin6浏览0评论

How to prevent keydown event when input is focused?

I have a function that assigns some action to keyboard keys (mainly player control), but when using <input> all assigned keys trigger that action as well. Therefore it is impossible to use <input> properly when typing.

function keyboardShortcuts(e){
        
  //Do some stuff when keyboard keys are pressed
  
  switch(e.code){
  
    case 'KeyJ':
      console.log('KeyJ');
      break;
    
    //...
  }
}

document.addEventListener('keydown',keyboardShortcuts);
<input id="search" placeholder="search...">

How to prevent keydown event when input is focused?

I have a function that assigns some action to keyboard keys (mainly player control), but when using <input> all assigned keys trigger that action as well. Therefore it is impossible to use <input> properly when typing.

function keyboardShortcuts(e){
        
  //Do some stuff when keyboard keys are pressed
  
  switch(e.code){
  
    case 'KeyJ':
      console.log('KeyJ');
      break;
    
    //...
  }
}

document.addEventListener('keydown',keyboardShortcuts);
<input id="search" placeholder="search...">

How to fix it? Should I check if input is not focused?

function keyboardShortcuts(e){
    if(document.getElementById('search') !== document.activeElement){
        switch(e.code){
            //...
       }
    }
}

Or maybe there is some method similar to e.preventDefault()?

document.getElementById('search').addEventListener('focus',function(){
    //... prevent keyboardShortcuts frum running ?????
});
Share Improve this question asked Oct 15, 2019 at 23:38 Jakub MudaJakub Muda 6,81410 gold badges42 silver badges58 bronze badges 2
  • TBH I think this is more of a design problem. If you're just trying to make custom keyboard shortcuts on an input, think about how CTRL + V/C (copy/paste) works. I think the important thing is to make an irrelevant key (like CTRL) be the deciding factor of if a user is explicitly typing something or not. One could assume that if CTRL is being held and another key is pressed, you're trying to run a shortcut and not wanting to actually type a value in. – mwilson Commented Oct 15, 2019 at 23:59
  • 1 Jakub - I wanted to let you know I edited my answer again to provide a way to add a class to entire containers of elements. This way you don't have to keep adjusting your keydown code, or adding classes to each individual element. Work smarter, not harder :) – zfrisch Commented Oct 16, 2019 at 0:44
Add a ment  | 

1 Answer 1

Reset to default 10

Answer:

Since you're applying the keydown event handler to the entire document, the best way to handle this is to diffuse the function based upon what element initiated the event. You can find this out by using event.target

Combining this with HTMLElement.prototype.matches can allow you to avoid elements that match a selector, for instance any input elements.

  if(e.target.matches("input")) return;

Example:

function keyboardShortcuts(e){
        
  //Do some stuff when keyboard keys are pressed
  if(e.target.matches("input")) return;
  switch(e.code){
  
    case 'KeyJ':
      console.log('KeyJ');
      break;
    
    //...
  }
}

document.addEventListener('keydown',keyboardShortcuts);
<h3> Will Escape Custom KeyDown </h3>
<input id="search" placeholder="search...">
<hr />
<h3> Will Trigger Custom KeyDown </h3> 
<textarea></textarea>
<hr/>
  <small> Pressing <kbd>J</kbd> will fire <highlight>console.log</highlight> from Custom KeyDown Event</small>

You'll notice, in the above, input doesn't fire a console log when pressing j, but textarea still does.


Adding a Class:

I would suggest adding a specific class to your markup that is used for avoiding the global keydown handler. For instance something like defaultKeys


Example:

function keyboardShortcuts(e){
        
  //Do some stuff when keyboard keys are pressed
  if(e.target.matches(".defaultKeys")) return;
  switch(e.code){
  
    case 'KeyJ':
      console.log('KeyJ');
      break;
    
    //...
  }
}

document.addEventListener('keydown',keyboardShortcuts);
<h3> Defaulted KeyDown Events </h3>
<hr/>
<input id="search" class="defaultKeys" placeholder="search...">
<br>
<textarea class="defaultKeys"></textarea>
<hr/>
<h3> Custom KeyDown Events </h3>
<input />
<br>
<textarea></textarea>
<hr/>
<small> Pressing <kbd>J</kbd> will fire <highlight>console.log</highlight> from Custom KeyDown Event</small>


Avoiding Groups of Elements:

It may be beneficial to avoid entire containers of elements - a.e. if you have a submission form or a player chat, etc.

You can utilize the same basic principles, but because currently selectors cannot look up the tree( they can't say am I a great-grandchild of an element that does match .defaultKeys ? ) we have to recursively check up the tree.

This is done with a simple do...while loop that continues until it finds a match for the specified selector OR it hits the body tag.

The second out( the body tag check ) is normally not necessary as parentElement should only be able to go up to the HTML element, however, when dealing with IFrames, DOMParser, and, in the future, Realms - you may have multiple DOMs injected into one another, and therefore, multiple body tags.

Basically, we simply want to give a for-sure recursive out when it reaches the top of the current DOM.

function keyboardShortcuts(e) {
// get init element and set default flag
  let element = e.target,
    useDefault = false;

// if this element has defaultKeys class
// OR if any parent element has defaultKeys class
// set default flag to true

  do {
    if (element.matches(".defaultKeys")) {
      useDefault = true;
      break;
    }
    if ( element.tagName === "BODY" ) break;
  } while ((element = element.parentElement));

// if flag is true, escape function
  if (useDefault) return;

// check for custom key events
    switch (e.code) {
      case "KeyJ":
        console.log("KeyJ");
    }
}

document.addEventListener("keydown", keyboardShortcuts);
<div id="group" class="defaultKeys">
  <h3> These elements are in the same parent div </h3>
  <input />
  <br/>
  <textarea></textarea>
  
</div>
<h3> Outside of Group </h3>
<textarea></textarea>


Conclusion

Hope this helps! Happy coding!

发布评论

评论列表(0)

  1. 暂无评论