不理会可用于焦点后代的 keydown 事件

Posted

技术标签:

【中文标题】不理会可用于焦点后代的 keydown 事件【英文标题】:Leave alone keydown events usable for focused descendants 【发布时间】:2018-02-11 14:34:26 【问题描述】:

我有一个(自制)表,其中包含一些物理行和许多逻辑行,我想(也)使用键盘滚动。如果后代关注理解键,我希望事件由后代处理并被我的表忽略。

也许代码说清楚了:

scroll.keydown = function(e) 
    if (e.shiftKey | e.controlKey | e.altKey) return;
    var isText = e.target.type === "text";
    var K = $.ui.keyCode;
    switch (e.keyCode) 
    case K.HOME: if (isText) return; scroll.move(-1000000); break;
    case K.END: if (isText) return; scroll.move(+1000000); break;
    case K.PAGE_UP: scroll.move(-10); break;
    case K.PAGE_DOWN: scroll.move(+10); break;
    case K.UP: scroll.move(-1); break;
    case K.DOWN: scroll.move(+1); break;
    default: return;
    
    e.preventDefault();
;


<table tabindex="0" ng-keydown="scroll.keydown($event)" ...>
      ... <input ...>

它工作得很好,但它很愚蠢:表知道它包含input元素,它知道这样一个元素可以使用例如HOME 键,因此它会进行测试以使其不理会。​​p>

这是非常错误的。现在,我添加了一个select,它需要在DOWN 案例中进行类似的测试才能使其正常工作。

有趣的是,我的表中还有另一个ng-keydown-using 组件,它处理事件的方式非常相似,但是没有问题:

当我在内部组件获得焦点时按 DOWN 时,它首先进入事件,一切都很好。 当我在关注 select 时按 DOWN 时,我的表格首先获取事件并滚动。我想要的是而不是,选择照常打开。

使用 angularjs v1.5.0 和 1.12.1 在 Chromium 版本 60.0.3112.113 中测试。

Plunkr

http://plnkr.co/edit/nr2JVJU1U16465F1WKkC?p=preview

澄清

任何对当前关注的后代无意义的事件都应由表处理。 例如,PAGE_UPinput 没有任何意义,因此应该滚动表格,即使焦点子进程处理一些其他事件,例如 HOME。 这就是我将if (isText) return; 添加到HOMEEND 的原因,它们仅在input 中有用,而不是在其他没有用的键中。

【问题讨论】:

您能简单地捕获&lt;select&gt; 元素上的keydown 事件,并阻止它传播/冒泡吗?这将阻止其父级捕获 keydown 事件:) 如果tableselect 元素之前获取事件,您将不得不使用useCapture,但是我不确定它是由angular 实现的:(。你确定这个是这样吗? @Terry 这意味着,我必须为所有选择添加处理程序......这也好不了多少。好的,我可以编写一个指令来添加一个监听器......但可能已经有其他监听器了。 你能创建一个简单的jsfiddle作为例子吗?即使有一个简单的表格,里面有一个选择...... 或者,在触发 keydown 事件时检查父级中的 e.target。如果它来自一个选择元素,则返回 false 或类似。 【参考方案1】:

我采用了您 plunkr 的代码并做了一个很小的更改来帮助您获得所需的内容。

$scope.keydown 函数的最顶部,我添加了一个条件来检查目标是输入还是选择,然后它将忽略滚动。

这适用于 nodeName 属性,如下所示(检查 $scope.keydown 函数):

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) 
  $scope.name = 'World';

  var data = [];
  for (var i = 0; i < 10000; ++i) data.push(
    id: i,
    value: i * i
  );


  var scroll = $scope.scroll = 
    start: 0,
    limit: 10,

    move: function(delta) 
      var start = scroll.start + delta;
      start = Math.max(start, 0);
      start = Math.min(start, data.length - scroll.limit);
      scroll.start = start;
    ,

    keydown: function(e) 
      // With the e.target.nodeName you can
      // can check if the keydown is being
      // triggered from the input or the select
      // and then avoid the table scroll
      if(e.target.nodeName === 'INPUT' || e.target.nodeName === 'SELECT') 
        return false;
      

      if (e.shiftKey | e.controlKey | e.altKey) return;
      var isText = e.target.type === "text";
      var K = $.ui.keyCode;
      switch (e.keyCode) 
        case K.HOME:
          if (isText) return;
          scroll.move(-1000000);
          break;
        case K.END:
          if (isText) return;
          scroll.move(+1000000);
          break;
        case K.PAGE_UP:
          scroll.move(-10);
          break;
        case K.PAGE_DOWN:
          scroll.move(+10);
          break;
        case K.UP:
          scroll.move(-1);
          break;
        case K.DOWN:
          scroll.move(+1);
          break;
        default:
          return;
      
      e.preventDefault();
    ,
  ;

  $scope.visibleData = [];
  $scope.$watchCollection("scroll",
    function() 
      for (var i = 0; i < scroll.limit; ++i) 
        $scope.visibleData[i] = data[i + scroll.start];
      
    );

);
table:focus 
  border: 1px solid red;


table 
  border: 1px dotted blue;
<!DOCTYPE html>
<html ng-app="plunker">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <script>
    document.write('<base href="' + document.location + '" />');
  </script>
  <link rel="stylesheet" href="style.css" />
  <script data-require="angular.js@1.5.x" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js" data-semver="1.5.11"></script>
  <script data-require="jquery@3.0.0" data-semver="3.0.0" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"></script>
  <script data-require="jquery-ui@1.10.4" data-semver="1.10.4" src="//code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
  <script src="app.js"></script>
</head>

<body ng-controller="MainCtrl">
  <ul>
    <li>Click in the input</li>
    <li>Press Shift-Tab</li>
    <li>Observe thar arrow-up and down control the outer select.</li>
    <li>Pres Tabl twice</li>
    <li>Observe that the the arrows scroll the table instead!</li>
  </ul>

  <select name="outer">
    <option>first</option>
    <option>second</option>
  </select>

  <table tabindex="1" ng-keydown="scroll.keydown($event)">
    <tr>
      <td>
        <input name="unused">
      </td>
      <td>
        <select name="select">
          <option>first</option>
          <option>second</option>
        </select>
      </td>
    </tr>
    <tr>
      <th>row</th>
      <th>value</th>
    </tr>
    <tr ng-repeat="x in visibleData">
      <td>x.id</td>
      <td>x.value</td>
    </tr>
  </table>

</body>

</html>

【讨论】:

抱歉,我不清楚我到底想要什么(并将“祖先”与“后代”混淆:D)。我已经添加了一个段落。 没问题,@maaartinus。你检查我的答案了吗?我认为它会帮助你解决问题。只需运行代码 sn-p。

以上是关于不理会可用于焦点后代的 keydown 事件的主要内容,如果未能解决你的问题,请参考以下文章

VB.NET如何使控件不能响应KeyDown事件

enter回车---焦点切换

捕获 DIV 元素上的按键(或 keydown)事件

jquery 键盘事件 keypress() keydown() keyup()

关于键盘KeyDown事件

键盘事件