[jQuery keydown()在关注后代交互元素时不会触发

问题描述 投票:0回答:2

为了使应用程序符合W3C feed pattern,我必须创建键盘命令,以帮助屏幕阅读器用户浏览通过无限滚动加载的内容。请参见工作示例here

在示例页面上,聚焦于列表中的一项,然后按PAGE_DOWN / PAGE_UP。看到?这样一来,您就可以浏览列表项目,同时跳过每个项目的内容。

如果您将重点放在其中一个项目中的按钮上并尝试从那里导航,则它仍将在文章之间正确导航。 这就是我希望我的应用程序表现出来的方式,但是不是。

我的代码与示例中的代码基本相同。 <article>中有多个<section role="feed">元素。使用jQuery,我将'keydown'事件附加到该<section>,称为#product-grid

$('#product-grid').keydown(function(event) {
  // Detect which key was pressed and execute appropriate action
});

HTML结构:

<section id="product-grid" role="feed">
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>

  (...) many other <article> elements

</section>

我的文章里面有锚点。如果您专注于它们并按下一个键,则“ keydown”事件将不会触发。如果您将重点放在本文的其他任何地方,它都可以。

据我了解,当后代项具有焦点时,父项(在这种情况下为#product-grid)也受到关注。我说得对吗?

我尝试过的事情:

  • 委派事件,使用on()来捕获#product-grid中某些元素(包括锚元素)的keydown事件。
  • 将keydown事件处理程序直接附加到<article>中的锚点。当我专注于它并按一个键时,什么也没有发生。根据我在SO上搜索得到的结果,这应该可以工作。

这是一个小提琴,您可以在其中重现问题: https://jsfiddle.net/fgzom4kw/

重现问题:

  1. 单击任何项​​目的灰色背景(而不是锚点)。红色轮廓将指示它已聚焦。
  2. 使用向上翻页和向下翻页浏览项目。
  3. 按TAB键,将焦点放在锚点之一上。
  4. 将锚定在焦点上,尝试使用PAGE UP和PAGE DOWN键再次导航。

将此行为与W3C example的行为进行比较。


问题已解决。有两种解决方案:

我的方式:https://stackoverflow.com/a/59449938/9811172

<matrixRef> 或公路! </matrixRef>

曲折的方式:https://stackoverflow.com/a/59448891/9811172

检查注释是否有任何警告。

jquery jquery-events
2个回答
1
投票

我无法按照您所说的重复该问题。也就是说,您需要一个更广泛的选择器。基本上,如果用户关注选择器后代的任何项目,则需要绑定keydown回调。考虑以下内容。

$(function() {
  $("#product-grid").children().keydown(function(e) {
    var el = $(e.target);
    var ch = e.which;
    console.log("Event:", e.type, "HTML Target:", el.prop("nodeName"), "Char:", ch);
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section id="product-grid" role="feed">
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>
  <article tabindex="-1">
    <a href="#"> having focus on this element prevents the 'keydown' event from firing
      <img src="..."/>
      <p>some text</p>
    </a>
    <div>
      if you click on this non-interactive section instead, the event fires correctly
    </div>
  </article>
</section>

使用$("#product-grid").children()作为选择器将获取所有子元素。然后,您可以根据需要绑定回调。

更新

根本问题是使用tabbable元素时的当前焦点。看一个新的例子:https://jsfiddle.net/Twisty/m1w2b7rv/39/

JavaScript

$(function() {
  function setFocus(i) {
    $("[aria-posinset='" + i + "']").focus();
  }

  function navFocus(c, i, a) {
    switch (c) {
      case 33: // PAGE_UP
        if (i > 1) {
          setFocus(--i);
        }
        break;
      case 34: // PAGE_DOWN
        if (i < a) {
          setFocus(++i);
        }
        break;
      case 35: // END
        if (event.ctrlKey) {
          if (i !== a) {
            setFocus(a);
          }
        }
        break;
      case 36: // HOME
        if (event.ctrlKey) {
          if (i !== 1) {
            setFocus(1);
          }
        }
        break;
    }
  }

  $("#product-grid").on("keydown", function(event) {
    var el = $(event.target);
    if (el.prop("nodeName") == "A") {
      el = el.closest("article");
      el.focus();
    }
    var itemIndex = el.attr('aria-posinset');
    var itemCount = el.attr('aria-setsize');
    navFocus(event.which, itemIndex, itemCount);
  });
});

keydown事件正在冒泡并被触发,但是当前文章没有focus,因此没有一种获取文章正确索引的好方法。因此,如果焦点位于链接上,则必须首先将焦点重新设置为article元素。


0
投票

我知道了。 And so did Twisty

答案在W3C示例的JavaScript代码中,就在这里:https://www.w3.org/TR/wai-aria-practices-1.1/examples/feed/js/feed.js

var focusedArticle =
  aria.Utils.matches(event.target, '[role="article"]') ?
    event.target :
    aria.Utils.getAncestorBySelector(event.target, '[role="article"]');

它试图找出在按键时焦点所在的元素。

  1. 如果event.target<article>或(如在示例中)<div role="article">,它将使用该元素。
  2. 如果event.target不是文章,它将尝试检索其可以找到的“最近”文章。

这意味着,如果文章中的交互式窗口小部件在我们按我们的键盘命令之一时具有焦点,则将使用包含该窗口小部件的文章来代替。 article元素之所以特别,是因为它们包含我的事件处理程序(和屏幕阅读器)中使用的元数据(aria-posinset,aria-setsize)。诀窍在于getAncestorBySelector方法。

现在是这样的:

$('#product-grid').keydown(function(event) {
  var $focusedItem = $(event.target);

  // if focused element is not <article>, find the closest <article>
  if ($focusedItem.is(':not(article)')) {
    $focusedItem = $focusedItem.closest('article');
  }

  var itemIndex = $focusedItem.attr('aria-posinset');
  var itemCount = $focusedItem.attr('aria-setsize');

  (...)

..并且解决了问题:D


摆弄我的解决方案: https://jsfiddle.net/5sta3o82/


0
投票

我知道了。 And so did Twisty

答案在W3C示例的JavaScript代码中,就在这里:https://www.w3.org/TR/wai-aria-practices-1.1/examples/feed/js/feed.js

var focusedArticle =
  aria.Utils.matches(event.target, '[role="article"]') ?
    event.target :
    aria.Utils.getAncestorBySelector(event.target, '[role="article"]');

它试图找出在按键时焦点所在的元素。

  1. 如果event.target<article>或(如在示例中)<div role="article">,它将使用该元素。
  2. 如果event.target不是文章,它将尝试检索其可以找到的“最近”文章。

这意味着,如果文章中的交互式窗口小部件在我们按我们的键盘命令之一时具有焦点,则将使用包含该窗口小部件的文章来代替。 article元素之所以特别,是因为它们包含我的事件处理程序(和屏幕阅读器)中使用的元数据(aria-posinset,aria-setsize)。诀窍在于getAncestorBySelector方法。

..并且解决了问题:D


摆弄我的解决方案: https://jsfiddle.net/5sta3o82/

© www.soinside.com 2019 - 2024. All rights reserved.