【问题标题】:Make HTML form elements 'display only' - non-interactive使 HTML 表单元素“仅显示” - 非交互式
【发布时间】:2021-03-31 18:00:49
【问题描述】:

我正在开发一个动态表单构建器,用户可以在其中向页面添加不同的内容块(按钮、文本输入、文本段落、图像等),然后他们可以发布这些内容,这将是一个简单的他们可以使用的 HTML 表单。

对于此应用程序的“预览”部分(他们正在添加不同的内容块并修改内容和外观),我想到最简单的方法就是只显示最终产品HTML 本身,这样我或多或少地保证预览中显示的内容与实际表单中显示的内容相匹配。

这很重要的地方在于晦涩的样式怪癖,such as the behaviour of buttons with display:block.

但是,我不希望预览像真实表单那样真正具有交互性。也就是说,我不想要以下行为:

  • 输入接收焦点并能够插入内容
  • 具有点击动画的按钮
  • 点击链接导航
  • 出现默认悬停样式。

此外,就可访问性而言,如果屏幕上出现一堆不执行任何操作的输入,可能会非常混乱。

是否有浏览器本机/标准方式来执行此操作?

我的后备方案可能是禁用所有元素。但这不适用于例如链接。

另一种方法可能是在表单顶部放置一个不可见的 div,它会捕获所有点击事件。但我不喜欢这种无障碍 POV。

是否为此使用了role="presentation"

代码示例

作为示例,请在此处查看一些示例代码:https://codepen.io/dwjohnston/pen/qBrGPmr?editors=1111

现在,我有带有role="button" 的 div - 这些按钮旨在可点击, - 当您点击它们时,侧面板将显示内容配置,例如输入的默认值、标签等。

已经存在问题 - 按钮内不允许有交互式内容。

而且,我不应该能够对这些控件进行标签,(现在我可以手动添加tabindex = "-1",但这不是完整的画面)。我不想混淆屏幕阅读器等。

【问题讨论】:

  • 为什么不在预览上画一个透明的覆盖层,捕捉所有的点击和鼠标事件?
  • 嘿,你在寻找什么额外的信息,以便我可以修改我的,给你你需要的信息?
  • @GrahamRitchie Ha 好问题。我不确定,这个赏金可能有点冲动。可能只是代码示例来展示它的行为方式。我可能会用我正在查看的一些代码示例来更新我的问题。
  • @dwjohnston 呵呵,很公平。为了在您的示例中清楚起见,<button> 元素是“最终产品”,而带有role="button" 的包装器是编辑器界面?因此,您希望能够将该按钮嵌套在包装器中而不会造成混乱?
  • 对于<input>,我认为您想要这样做,这样您就无法单击该<input>(因为这可能会导致错误或与事件冒泡等混淆)。所以你想要一种方法来直观地显示这些项目,阻止它们对每个人都进行交互,并向屏幕阅读器用户解释事情以使它们有意义?我想你也被这种模式或非常相似的东西锁定了(即你想使用你拥有的东西而不是完全重新设计!)

标签: html accessibility wai-aria


【解决方案1】:

disabled 属性添加到您要禁用的所有表单元素。

【讨论】:

  • 来自 OP 的问题:我的后备可能是禁用所有元素。您的回答不会在此处添加任何有用的内容。
【解决方案2】:

信息

对此问题进行了编辑,解决了 OP 在当前实施中遇到的具体问题。我将第一部分留在这里作为我的原始答案。

我不希望预览像真实表单那样真正具有交互性。

为什么?让表单具有交互性对所有用户来说都很棒(考虑到某些功能需要在预览中不能完全发挥作用)。

让人们看到默认的悬停样式是什么样的,点击链接会做什么(即点击链接并提示“将导航到 xxx 页面”)等等。

我了解我不想导航到新页面并提交表单(任何“保存”或“更新”操作以及任何“导航”操作),但我认为其他一切都应该能够在预览中工作。

此外,就可访问性而言,如果屏幕上出现一堆不执行任何操作的输入,可能会非常混乱。

如果屏幕上出现预览但我无法导航到按钮、链接等,那会更加混乱。

这对于屏幕阅读器用户会有什么表现?即,您是否会使用 aria-hidden="true" 隐藏所有元素(在这种情况下,他们将如何测试页面?)。

我的后备方案可能是禁用所有元素。但这不适用于例如链接。

这确实是您可以处理的最糟糕的方法之一,disabled 输入通常无法获得焦点,因此键盘用户在测试时会受到影响。

另外,如果最终生产表单中的某些元素在某个字段被填写之前被禁用(例如),那么用户如何在您的最终表单中测试呢?

此外,如果您拦截了链接的导航但没有解释链接为什么不起作用,这也可能导致混淆。 (如果您向最终用户解释,该链接是否可以在“预览”模式下在新窗口中打开?显然取决于它是否是静态链接等,但只是考虑用户可以检查任何自定义链接指向的方式正确的地方)。

建议的解决方案

我会说您需要两个版本的完成表格。 “真实世界”的一个和“预览”的一个。

HTML 可以(读作“应该”)相同,但您需要实现 JavaScript 处理程序以禁用您不想激活的功能。

让编辑工具仅在您担心的情况下使用 JavaScript 并没有错,只要您向最终用户发出警告,说明该工具需要 JavaScript 才能工作。 (发布工具和最终用户体验之间的区别在于您可以更加依赖 JavaScript。)

例如: 假设您有一个<button>,单击该<button> 应该在表单中添加一个新行(在表单的生产版本中),无论出于何种原因,您都不想要这个在预览版中启用的功能。

在这种情况下,在表单的预览/演示版本中,当您单击此按钮时,可能会显示alert,上面写着“将为表单添加新行”。

这样,屏幕阅读器用户和仅键盘用户就可以测试表单,而无需您做太多额外的工作(还可以查看表单在键盘用户使用时是否存在可用性问题!)

我认为这对每个人来说都是最清楚的,并且用户体验会更好。

您只需要考虑导航进出预览模式(例如,Esc 关闭)和快速编辑(当您预览表单时,如果表单特别长,只有键盘的用户会为此感谢您!当您返回编辑时,将焦点重新放在最后编辑的项目上)。

进一步阅读

从可访问性的角度来看,WCAG 不是您在这种情况下要寻找的,Authoring Tool Accessibility Guidelines (ATAG) 是。

这是一套鲜为人知的关于如何创建编辑器、所见即所得等的指南。

与所有 W3C 指南一样,这是一本繁重的读物,但如果您了解核心原则,它将指导您做出正确的决定,您可以随时在此处询问您是否需要澄清一些不清楚的个别要点:- )。



编辑:您如何使用当前设计实现可访问的界面

您在codepen you linked中遇到的问题及其解决方法如下:

停止接收焦点的交互项目

当您包装每个元素时,您已正确诊断出最大的问题,即嵌套交互元素。

解决方案非常简单:如您所说,给每个交互元素一个tabindex="-1"。这样可以确保它们不会被错误地聚焦。

确保实际的包装项目可以被聚焦和激活

然后我们只需要确保包装器.content-item 具有tabindex="0" 以便将其添加到焦点顺序中。

我们还需要捕获 Enter Space 键,并确保我们的面板也被它们激活。

视觉焦点指示器

我还在包装“按钮”中添加了一个outline,以便可以使用视觉焦点指示器。

所以基本上解决了正常的键盘导航问题,下一个问题是屏幕阅读器将如何解释嵌套按钮、输入等。

屏幕阅读器更正

在这里我也很简单,从屏幕阅读器完全隐藏按钮、输入等,而是解释里面是什么元素。

实际上,我们使按钮不可见,而是描述.content-item 的内容。

为了隐藏我们使用aria-hidden="true"的元素

要宣布一些有意义的事情,我们可以在<span> 中使用一些visually-hidden (screen reader only) text。这将比在如此复杂的应用程序中使用aria-label 更加强大。

使用鼠标使应用程序表现良好

最终挑战 - 停止鼠标事件。

幸运的是pointer-events: nonereally good support,即使在少数不支持它的浏览器上能够点击输入也不是世界末日(我们已经为键盘用户修复了它)

已解决所有问题的示例:

这解决了您选择方法的所有问题。

请注意,它没有考虑组件本身的可访问性错误(例如 <input> 没有 <label> 等),但我认为这只是因为它是一个示例。

您唯一需要解决的是如何生成描述项目和.content-info“按钮”操作的视觉隐藏文本。

document.addEventListener("DOMContentLoaded", function () {
 
  const items = document.getElementsByClassName("content-item"); 
  const rightPanel = document.getElementById('right-panel');
  
  for (let i = 0; i< items.length; i++) {
    const item = items[i]; 
    
    item.addEventListener("click", (e) => {
      console.log(e.target);
      const id = e.target.dataset.id; 
      rightPanel.innerHTML = id; 

    });
  
  // ADDED this to capture the Enter and Space presses on our wrapper button
    item.addEventListener("keydown", function(e){
      if(e.keyCode == "32" || e.keyCode == "13"){
      console.log(e.target);
         const id = e.target.dataset.id; 
         rightPanel.innerHTML = id; 
      }
    });
  
  }
});
.main {
  display: flex; 
  flex-flow: row nowrap; 
  
}

.left {
  flex: 1 1 auto; 
}

.right {
  flex: 1 1 auto;
}

.content-item {
  margin-top: 20px;
  width: 600;
  border: solid 2px blue; 
  cursor: pointer;   
}

/* ADDED a focus indicator so keyboard users know where they are */
.content-item:focus{
    outline: 4px solid #333;
}

/* ADDED to stop pointer events reaching the input, button etc. */
input, button{
    pointer-events: none;
}

/* ADDED the visually hidden class so we can provide meaningful text to screen reader users */
.visually-hidden { 
    border: 0;
    padding: 0;
    margin: 0;
    position: absolute !important;
    height: 1px; 
    width: 1px;
    overflow: hidden;
    clip: rect(1px 1px 1px 1px); /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
    clip: rect(1px, 1px, 1px, 1px); /*maybe deprecated but we need to support legacy browsers */
    clip-path: inset(50%); /*modern browsers, clip-path works inwards from each corner*/
    white-space: nowrap; /* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */
}
<div class ="main"> 
  <div class ="left">
<!-- Added `tabindex="0"` so the wrapper is focusable by keyboard -->
<div class="content-item" role="button" data-id="item-1" tabindex="0">
  <!-- added `aria-hidden="true"` and `tabindex="-1"` to hide the input from screen readers and make sure it cannot be focused by keyboard, I also added `role="presentation"` just for good measure, although it shouldn't be needed. -->
  <input type="text" aria-hidden="true" tabindex="-1" role="presentation">
  <!-- Added this span that explains the action that the "content-item button" would perform if clicked. You need to come up with something meaningful for the contents of this -->
  <span class="visually-hidden">Edit input [input identifier]</span>
</div>

<div class="content-item" role="button" data-id="item-2" tabindex="0">
  <button aria-hidden="true" tabindex="-1" `role="presentation"`> I am a button</button>
  <span class="visually-hidden">Edit button [button identifier]</span>
</div>
  </div>
  
  <div class ="right" > 
    
    <h2> Right panel</h2>
    <div id ="right-panel"> 
      
    </div>
    
  </div>   
</div>

【讨论】:

  • 您可以在每个控件上使用 readonly 属性,使用 pointer: none 然后禁用任何控件上的键控。然后读取标签,用户可以选择但不能更改或与任何字段交互。段落中的文本说明可以使用 labelledby 链接到此表单,以告知屏幕阅读器用户正在发生的事情。
  • 您将如何测试最大长度是否正确实现作为使用该方法的示例?还是电子邮件验证?并不是说这不是一个可能/好的解决方案(在这种情况下为什么不将其添加为答案)只是想知道这将是一个更好的体验吗?实施的工作量相似,所以只是想看看为什么您认为这是一个更好的解决方案?
  • 或者这纯粹是为了指出“屏幕阅读器用户的行为如何?”可以很容易地通过这种方式解决,而不是使用aria-hidden="true",因为这有点半开玩笑地说这种体验可能会在翻译中丢失(我是英国人,所以讽刺是内置的!)
  • 我很想正确回答这个问题,但目前没有时间。考虑您的表单不仅仅是一个表单,而是一个具有特殊字段的表单,用于验证和属性等内容。通常,数字控件会自行读取,但如果您希望用户了解更多信息,则可以提供;但是一旦这种形式上线,你就不会了。例如,您以他们在遇到错误之前不会看到的几种方式让用户深入了解表单。 (我在新西兰是个大佬,所以完全理解皇室的幽默。哈。)
【解决方案3】:

您可能想查看inert 属性。目前没有任何浏览器支持,但是有 polyfill 库。

这个想法是,在任何元素上标记它都会使其及其所有子元素非交互且不可见,以实现可访问性目的。

您可以阅读一些背景资料并在此处下载库:

https://github.com/WICG/inert

【讨论】:

  • 打败了我。即使它是一个奇特的用例,但它显然是惰性的用例。
【解决方案4】:

试试这个

$(document).ready(function(){
  
  var demoPage = $('div.demoPage');
demoPage.find('a').attr('href','#');
demoPage.find('button').attr("disabled", true)
  
})
div {
  padding: 20px;
  margin: 20px;
  background: #eee;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div class="no-copy">
    <p>You can't click me.</p>
</div> 
<div class="demoPage">
  
  <a href="www.google.com" readonly="" class="no-copy">
    It is a link
  
</a>
  
  <button> It is a button</button>
  
</div>

【讨论】:

  • 我不确定这是否解决了可访问性问题,例如按钮内的按钮。
  • 我编辑了我的代码,检查一下,我的 JS 禁用了链接和按钮!
【解决方案5】:
<!--you can do this-->
<input type="text" id="input" disabled>
<script>
document.getElementById('input').disabled = true;
</script>

【讨论】:

  • 欢迎来到 Stack Overflow。没有任何解释的代码很少有帮助。 Stack Overflow 是关于学习的,而不是提供 sn-ps 来盲目复制和粘贴。请编辑您的问题并解释它如何回答所提出的具体问题。见How to Answer
【解决方案6】:

您可以通过添加一些 css 类来非常简单地做到这一点:

<div class="content-item disable-control" role="button" data-id="item-1">
  <input type="text" />
</div>

<div class="content-item disable-control" role="button" data-id="item-2">
  <button> I am a button</button>
</div>

^^ 我添加了一个额外的“禁用控制”类

.content-item.disable-control * {
  pointer-events: none;
  cursor: inherit;
  user-select: none;
}
.content-item.disable-control *::selection { 
  background: transparent;
} 

如果需要,请确保添加一些浏览器特定的 css,例如

*::-moz-selection

.. 当然,您可以修改我的代码以仅应用一次类(用于整个左窗格),而不是单独应用每个控件

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-17
    • 2011-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多