【问题标题】:HTML Validation: Why is it not valid to put an interactive element inside an interactive element?HTML 验证:为什么将交互式元素放在交互式元素中是无效的?
【发布时间】:2020-09-17 09:24:44
【问题描述】:

免责声明:我知道它不是有效的 HTML。我想了解为什么不允许这样做?

W3C 建议像button 或a 这样的交互元素不得包含另一个交互元素。

我可以找到很多提到此规则和一些解决方法的资源,还有一些与此规则如何影响可访问性和屏幕阅读器相关的资源,但几乎所有这些资源都提到这是一项要求,但没有解释原因。

https://adrianroselli.com/2016/12/be-wary-of-nesting-roles.html

https://codepen.io/vloux/pen/wXGyOv

Nesting <a> inside <button> doesn't work in Firefox

https://github.com/dequelabs/axe-core/issues/601

我真的无法解释为什么不允许这样做?它会导致任何可用性问题吗?

这是一个相关的问题: Why should interactive element not be used within an anchor?

接受的答案令人满意,但不足以使此规则成为要求。使用适当的事件处理可以避免所描述的情况。

另外,如果嵌套的交互内容是无效的,我们怎么会有这样的东西:

一张整体可点击的卡片,里面还有一个可点击的辅助 CTA。 我知道一种解决方法是在卡内有一个主要和次要 CTA,但上述内容是否也应该被允许?

这是一个小提琴: https://jsfiddle.net/t9qbwas5/2/

<button type="button" class="card">
  The card itself is the primary CTA.
  <br/>
  <br/>
  some important content to read through.
  some important content to read through.
  some important content to read through.
  <div class="cta">
   Secondary CTA
  </div>
</button>

.cta {
  padding: 4px;
  background: #00a9a9;
  color: white;
  width: 80px;
  margin: auto;
  margin-top: 8px;
  margin-bottom: 8px;
}

.card {
  width: 200px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
}

在上面的示例中,我通过在按钮内使用可点击的 div 来实现这一点,但这不是语义(?)和功能方面的,它是另一个内部的交互式元素。我试图理解,即使我使用这种解决方法,嵌套交互式元素从根本上来说是错误的吗?这是一种糟糕的设计/可用性实践吗?

【问题讨论】:

    标签: html accessibility wai-aria semantic-markup


    【解决方案1】:

    一个整体可点击的卡片,里面还有一个可点击的辅助CTA。

    虽然视觉上可以想象并且在技术上是可行的,但辅助技术无法访问它,例如屏幕阅读器

    我们举个简单的例子:

    <button>
        Click for action 1
        <button>Click for action 2</button>
    </button>
    

    第一个&lt;button&gt; 的可访问名称将是“点击操作1 点击操作2”。如果你定义了一个aria-label="Click for action 1",那么内部的button 元素根本不会被读取。

    如果你真的想让整个元素可以点击,你可以完美地使用 javascript 并且仍然可以访问

    <div class="outer">
      <button type="button" class="card">
        The card itself is the primary CTA.
      </button>
      <br/>
      <br/>
      some important content to read through.
      some important content to read through.
      some important content to read through.
    
      <button class="cta">
       Secondary CTA
      </button>
    </div>
    
    <script>
    $(".outer").on("click", function() {$(".card").click()});
    </script>
    
    <style>
    .outer {cursor: pointer}
    </style>
    

    在此示例中,您将正确地向屏幕阅读器呈现两个按钮,第一个是“卡片本身是主要 CTA”,第二个是“次要 CTA”,而在整个卡片上单击鼠标会导致相同的操作作为第一个按钮。

    【讨论】:

    • 我知道有多种有效的变通方法,但从根本上说,设计方面我们仍然有嵌套的交互式元素,如果变通方法有效,并且如果没有基本可预见的情况,HTML 可以默认允许嵌套交互元素嵌套交互元素的问题
    • 我的意思是,仅仅因为我们使用的是 div 而不是按钮,屏幕阅读器和 html 验证器突然就可以了吗?还是我们在作弊,做一些标准根本不希望我们做的事情
    • 我们没有作弊,因为我们通过标准按钮提供了一种可访问的方式,这些方式将被屏幕阅读器等辅助技术理解和正确处理。 onclick 事件是鼠标用户实现与默认按钮相同的操作(对屏幕阅读器用户透明)的另一种补充方式
    • 实际上是的,在您的示例中这是有道理的,但这仅适用于单击,卡片不会具有按钮具有的焦点/悬停/禁用和其他交互,除非我们找出将其传递给按钮的方法,或者编写一些代码使其看起来像处于这些状态的按钮,这并非不可能,但随后会变得混乱
    • @gaurav5430 键盘用户将使用这两个按钮,识别良好,因此非常适合他们。您可以为鼠标用户提供.outer:focus CSS 类。我看不出有什么困难。您在第一个按钮上的键盘焦点应该在按钮上,而不是在完整的外部 div 上。这就是我们想要的可访问性
    【解决方案2】:

    一个重要的问题与事件捕获有关;如果您单击嵌套在另一个交互式元素内的交互式元素(例如,可点击的button 内的select)会在此处产生干扰,并且可能会发生两种情况,具体取决于浏览器;

    情况 1 是两个元素都会引发该事件(例如click)事件

    情况 2 是父元素将捕获事件,而嵌套元素不会引发 event

    事实上,这两种情况都会导致不确定的行为;

    这实际上不仅限于click事件,而是click事件更具体;屏幕阅读器也将无法解析标记;键盘交互无法按预期工作;在下面的 sn-p 中尝试:

    del.addEventListener('click', function(){
      console.log('deleting ...')
    })
    
    save.addEventListener('click', function(){
      console.log('saving ...')
    })
    
    sel.addEventListener('change', function(){
      console.log('changing to', sel.value)
    })
    <div id='del'>
    delete 
    <button id='save'> save 
    <select id='sel'>
      <option>foo</option>
      <option>bar</option>
    <select>
    <input name='a' type='radio' />
    <input name='a' type='radio' />
    <input name='a' type='radio' />
    
    </button>
    </div>

    【讨论】:

    • 屏幕阅读器将无法解析标记,是因为它们在编写时牢记了此规则,还是即使此规则根本不存在,它们也无法解析标记?
    • 一旦我们为嵌套交互元素的行为方式设定了标准,键盘导航会出现什么问题。比如,目前如果你有嵌套元素,焦点会转移到父元素,然后在下一个选项卡中它会转移到嵌套的子元素,这还不够标准吗?
    • @gaurav5430 键盘导航在select 内button 情况下并不明显;我添加了radio 以获得更好的演示;对于你的第一个问题;我不知道确切的答案,但我猜它失败是因为结构不当而不是因为它违反了规则;您实际上可以通过在 sn-p 打开时运行画外音来按需测试它,并查看它是如何解析的
    • 我猜屏幕阅读器可能会失败,因为它不希望违反此规则,如果明天我们删除此规则,新的屏幕阅读器可能不会有任何问题。我试图了解如果放宽此规则,是否有任何问题根本无法处理,在这种情况下,此规则可以保持原样严格,否则它可以是 SHOULD 规则而不是 MUST 规则
    • 问题不是无法解决,更多的是解决问题变得更加复杂、耗时且更难在广泛的用户代理之间实现互操作(即浏览器)和辅助技术,如果这种事情是“合法的”。在规范中使 UI 可预测和可互操作已经够难的了。这是一个必须的规则,以便将故障空间减少到合理的限制。
    【解决方案3】:

    很难回答“为什么”的问题,因为要考虑的因素很多,归根结底是规范规定的,但我会试一试。

    当这种行为被指定时,这种设计风格并不常见。链接通常是单个图像或一小部分文本。看看this link to an article from the year 2000:
    ]
    只有标题和图像是交互的。剩下的就是简单的文字。

    即使在今天,这种情况也并不常见。还请看Microsoft 365 pricing page:

    请注意卡片本身不是交互式的,只有里面的东西。您可以看到按钮形式的主要 CTA“立即购买”和超链接形式的次要 CTA。


    现在,关于您的示例:那张卡片真的是一个按钮吗?这可能是主观的,但对我来说这不是一个按钮。按钮通常以与周围页面形成对比的配色方案出现。我会将卡片设为 &lt;div&gt;,将辅助 CTA 设为 &lt;button&gt;。

    但它可能会让用户感到困惑,因为这张卡片对我来说似乎没有太多互动性。考虑将cursor: pointer 添加到&lt;div&gt;(除了所有必要的可访问性)`。


    我注意到你标记了。我认为这对于使用屏幕阅读器的人来说不是一个好主意,而且我认为大多数屏幕阅读器在解释按钮内的按钮时会遇到问题(如果浏览器完全接受的话)。

    我会改用“Microsoft 365 定价页面方法”。它更简单,并且适用于 HTML。

    【讨论】:

    • 我理解这个例子,但我认为当前的标准不会/应该只是基于古老的用法,而不是根据新的用例而改变。如果我们只允许该规则,是否还有其他问题无法处理?
    • 我认为大多数人仍然认为在另一个元素中包含交互元素是没有意义的。当然,使用&lt;div&gt; 确实很难提供可访问性。
    • 如果我们使用 div 并且能够提供所需的可访问性,那么它从根本上不仍然是一个嵌套的交互元素吗?只是现在它将是有效的 HTML。我试图质疑在这种使用有效 HTML 的情况下,是否突然可以使用嵌套交互元素
    • 我刚刚检查过,Firefox 在&lt;div role="button"&gt; 中接受&lt;button&gt;,所以是的,这是可能的。但是为&lt;div&gt; 提供必要的可访问性是一场噩梦:你需要监听click 和keydown 事件,你需要tabindex="0" 而disabled 属性不起作用。我会考虑一个没有嵌套交互元素的设计。
    • 是的,较新的浏览器确实接受这一点,但根据 html 验证器,它仍然是无效的 html。是的,我同意,如果可能的话,我宁愿使用按钮而不是 div,但无论哪种方式,我们都在使用嵌套的交互式元素,无论我们是否有有效的 html
    【解决方案4】:

    原则上答案其实很简单。当您单击另一个交互元素中的交互元素时,您应该触发哪个功能?

    在您的示例中,如果我单击辅助 CTA,它应该触发辅助 CTA 的功能还是触发卡片的功能?

    下面的小提琴应该演示问题,进入第一个按钮并按 enter,然后进入 CTA 并按 Enter。

    显然你可以解决这个问题,但我认为它证明了这一点。

    $('.card').on('click', function(){
        console.log("card");
    });
    $('.cta').on('click', function(){
        console.log("cta");
    });
    .cta {
      padding: 4px;
      background: #00a9a9;
      color: white;
      width: 80px;
      margin: auto;
      margin-top: 8px;
      margin-bottom: 8px;
    }
    
    .card {
      width: 200px;
      display: flex;
      flex-direction: column;
      justify-content: center;
      text-align: center;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <button type="button" class="card">
      The card itself is the primary CTA.
      <br/>
      <br/>
      some important content to read through.
      some important content to read through.
      some important content to read through.
      <div class="cta" tabindex="0">
       Secondary CTA
      </div>
    </button>

    然后,此原则将延续到屏幕阅读器和其他 Augmentative and alternative communication (AAC) 设备。

    他们在描述子元素时是否应该考虑到父元素?如果您在&lt;button&gt; 中嵌套一个复选框,他们是否应该允许使用 Space 来激活,应该 Enter 然后只影响按钮还是同时影响两者?

    【讨论】:

    • 我可以看到上述几点是一个问题,但我也可以看到可用的解决方法不是hackish。如果我们只是更改规则以允许嵌套交互内容,并让开发人员实现当前行为,是否仍然无法实现 ATs 行为的这种更改?通过上面的例子,看起来最好不要这样做,但没有足够的理由让它成为必须的规则
    • 不,这不是不可能的。然而,屏幕阅读器已经有数百件事情在努力解决,所以添加如此复杂的东西(例如,如何使用点击处理程序处理 div 内的按钮内的超链接)将是可怕的。您必须记住,有效的 HTML 也是一种最佳实践,如果您尝试了上面的示例,并使用每个屏幕阅读器和浏览器组合对其进行了测试,并且很明显它是如何工作的,那么 HTML 不会成为问题。 t 有效。在处理可访问性时,它有助于使您的 HTML 有效。
    • 看看 w3 的 ARIA 小部件角色 - 他们已经非常考虑在规范中操作的各种“嵌套交互”模式,包括在这种情况下可预测的一组键盘操作。撰写本文时文档的最新版本是 here - 这是一篇非常有趣的文章。
    • @brennanyoung 抱歉,错过了这条评论。你能指出文档中有助于理解的部分吗
    猜你喜欢
    • 2018-04-13
    • 1970-01-01
    • 2021-08-19
    • 2021-03-31
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    相关资源
    最近更新 更多