带有复选框的 Dojo dijit 树无法通过键盘访问
Dojo dijit tree with checkbox is not keyboard accessible
我创建了一个 dijit.Tree object,其中每个节点都是一个复选框。当你 select/deselect parent 节点时, child 节点得到 selected/deselected;
当 children 中的一个被取消选择时,parent 被取消选择;当所有 children 被选中时,parent 被选中。它工作得很好。
但是我需要它可以通过键盘访问。当我导航到树节点并按空格键或 Enter 时,没有任何反应。
我尝试将 tabindex 和 aria-role 添加到复选框(以编程方式),但没有成功。
这里是 fiddle - http://jsfiddle.net/pdabade/pyz9Lcpv/65/
require([
"dojo/_base/window", "dojo/store/Memory",
"dijit/tree/ObjectStoreModel",
"dijit/Tree", "dijit/form/CheckBox", "dojo/dom",
"dojo/domReady!"
], function(win, Memory, ObjectStoreModel, Tree, checkBox, dom) {
// Create test store, adding the getChildren() method required by ObjectStoreModel
var myStore = new Memory({
data: [{
id: 'allDocuments',
name: 'All Documents'
}, {
id: 'inboxDocuments',
name: 'Inbox Documents',
parent: 'allDocuments'
}, {
id: 'outboxDocuments',
name: 'Outbox Documents',
parent: 'allDocuments'
}, {
id: 'draftDocuments',
name: 'Draft Documents',
parent: 'allDocuments'
}, {
id: 'finalDocuments',
name: 'Final Documents',
parent: 'allDocuments'
}],
getChildren: function(object) {
return this.query({
parent: object.id
});
}
});
// Create the model
var myModel = new ObjectStoreModel({
store: myStore,
query: {
id: 'allDocuments'
}
});
// Create the Tree.
var tree = new Tree({
model: myModel,
autoExpand: true,
getIconClass: function(item, opened) {
// console.log('tree getIconClass', item, opened);
// console.log('tree item type', item.id);
},
onClick: function(item, node, event) {
//node._iconClass= "dijitFolderClosed";
//node.iconNode.className = "dijitFolderClosed";
var _this = this;
console.log(item.id);
var id = node.domNode.id,
isNodeSelected = node.checkBox.get('checked');
dojo.query('#' + id + ' .dijitCheckBox').forEach(function(node) {
dijit.getEnclosingWidget(node).set('checked', isNodeSelected);
});
if (item.id != 'allComments') {
if (!isNodeSelected) {
var parent = node.tree.rootNode; // parent node id
//console.log(node);
parent.checkBox.set('checked', false);
} else {
var parent = node.tree.rootNode;
var selected = true;
var i = 0;
dojo.query('#' + parent.id + '.dijitCheckBox').forEach(function(node) {
if (i > 0) {
var isSet = dijit.getEnclosingWidget(node).get('checked');
console.log(isSet);
if (isSet == false) {
selected = false;
}
}
i++;
});
if (selected) {
parent.checkBox.set('checked', true);
}
}
}
//console.log(node.id);
},
_createTreeNode: function(args) {
var tnode = new dijit._TreeNode(args);
tnode.labelNode.innerHTML = args.label;
console.log(args);
var cb = new dijit.form.CheckBox({
"aria-checked": "false",
"aria-describedby": args.label
});
cb.placeAt(tnode.labelNode, "first");
tnode.checkBox = cb;
return tnode;
}
});
tree.placeAt(contentHere);
tree.startup();
tree.checkedItems();
//tree.expandAll();
});
}
关于如何使其可通过键盘访问有什么想法吗?
谢谢!
不要在复选框中添加 role
、aria-checked
或 tabindex
。这些已经内置到控件中,因此您增加了在路上破坏它的风险。您可能还可以摆脱每个 role="presentation"
,因为它们在 <div>
和 <span>
上,它们本质上是表象。最后,如果您希望复选框可以访问,则需要在与复选框关联的每个文本块上使用 <label>
。 aria-describedby
不正确,无论如何都是不太好的选择。
我收到错误:Uncaught TypeError: tree.checkedItems is not a function
(第 159 行)
你的注意力管理也有很大的问题。将以下内容放入 CSS 中,您将看到每个控件需要按两次 Tab
键(如果从聚焦的复选框开始)::focus {outline:2px solid #f00;}
看起来您的包含元素窃取了所有点击,这意味着永远不会选择正确的元素。带有 类 dijit dijitReset dijitInline dijitCheckBox
的 <span>
在将其 tabindex
从 -1
切换到 0
时不断窃取焦点,使自己进出选项卡命令。这可能是一个因素。
我建议解决脚本错误,然后查看焦点管理。
使用 dijit 时,后台会发生各种您无法控制的事情。正如 aardrian 所说,有很多 role=presentation 并且 已经处理了选择,它的角色本质上是一个复选框。这些 aria 属性适用于您使用 div/span 标签创建自定义复选框的情况。
代码中隐藏了一个本机复选框,但它的不透明度设置为 0,因此您看不到它。 dijit 可能将其用于复选框事件。
原生checkbox还有data-dojo-attach-event="ondijitclick:_onClick"。我不确定那是什么意思,但每当我在事件名称中看到 "click" 时,我都会怀疑它可能不适用于键盘。
我在 https://dojotoolkit.org/reference-guide/1.10/dijit/form/CheckBox.html 上试过这个例子,它适用于键盘。点击 space 将选中和取消选中该框。是否可以看到复选框上的焦点是另一个问题。
附带说明一下,如果您的复选框树使用 aria-checked="mixed" 作为父分支可能会更好。任何时候你有子复选框,其中一些被选中而一些没有,你可以使用 "mixed" 作为父复选框来指示选择的混合。不确定 dijit 是否支持。
查看 dijit/Tree 源代码,我发现它将函数 _onNodePress() 设置为键盘事件的事件处理程序。您可以覆盖它(或在它之后添加一个方面)并手动处理您想要的按键操作。它以树节点和事件对象作为参数,您可以使用它们专门检查 space 和回车键。
我用一个例子 fork 你的 jsfiddle:https://jsfiddle.net/pgianna/jjore5sm/1/
_onNodePress: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
// This is the original implementation of _onNodePress:
this.focusNode(nodeWidget);
// This requires "dojo/keys"
if (e.keyCode == keys.ENTER || e.keyCode == keys.SPACE)
{
var cb = nodeWidget.checkBox;
cb.set('checked', !cb.get('checked'));
}
}
我创建了一个 dijit.Tree object,其中每个节点都是一个复选框。当你 select/deselect parent 节点时, child 节点得到 selected/deselected;
当 children 中的一个被取消选择时,parent 被取消选择;当所有 children 被选中时,parent 被选中。它工作得很好。
但是我需要它可以通过键盘访问。当我导航到树节点并按空格键或 Enter 时,没有任何反应。
我尝试将 tabindex 和 aria-role 添加到复选框(以编程方式),但没有成功。
这里是 fiddle - http://jsfiddle.net/pdabade/pyz9Lcpv/65/
require([
"dojo/_base/window", "dojo/store/Memory",
"dijit/tree/ObjectStoreModel",
"dijit/Tree", "dijit/form/CheckBox", "dojo/dom",
"dojo/domReady!"
], function(win, Memory, ObjectStoreModel, Tree, checkBox, dom) {
// Create test store, adding the getChildren() method required by ObjectStoreModel
var myStore = new Memory({
data: [{
id: 'allDocuments',
name: 'All Documents'
}, {
id: 'inboxDocuments',
name: 'Inbox Documents',
parent: 'allDocuments'
}, {
id: 'outboxDocuments',
name: 'Outbox Documents',
parent: 'allDocuments'
}, {
id: 'draftDocuments',
name: 'Draft Documents',
parent: 'allDocuments'
}, {
id: 'finalDocuments',
name: 'Final Documents',
parent: 'allDocuments'
}],
getChildren: function(object) {
return this.query({
parent: object.id
});
}
});
// Create the model
var myModel = new ObjectStoreModel({
store: myStore,
query: {
id: 'allDocuments'
}
});
// Create the Tree.
var tree = new Tree({
model: myModel,
autoExpand: true,
getIconClass: function(item, opened) {
// console.log('tree getIconClass', item, opened);
// console.log('tree item type', item.id);
},
onClick: function(item, node, event) {
//node._iconClass= "dijitFolderClosed";
//node.iconNode.className = "dijitFolderClosed";
var _this = this;
console.log(item.id);
var id = node.domNode.id,
isNodeSelected = node.checkBox.get('checked');
dojo.query('#' + id + ' .dijitCheckBox').forEach(function(node) {
dijit.getEnclosingWidget(node).set('checked', isNodeSelected);
});
if (item.id != 'allComments') {
if (!isNodeSelected) {
var parent = node.tree.rootNode; // parent node id
//console.log(node);
parent.checkBox.set('checked', false);
} else {
var parent = node.tree.rootNode;
var selected = true;
var i = 0;
dojo.query('#' + parent.id + '.dijitCheckBox').forEach(function(node) {
if (i > 0) {
var isSet = dijit.getEnclosingWidget(node).get('checked');
console.log(isSet);
if (isSet == false) {
selected = false;
}
}
i++;
});
if (selected) {
parent.checkBox.set('checked', true);
}
}
}
//console.log(node.id);
},
_createTreeNode: function(args) {
var tnode = new dijit._TreeNode(args);
tnode.labelNode.innerHTML = args.label;
console.log(args);
var cb = new dijit.form.CheckBox({
"aria-checked": "false",
"aria-describedby": args.label
});
cb.placeAt(tnode.labelNode, "first");
tnode.checkBox = cb;
return tnode;
}
});
tree.placeAt(contentHere);
tree.startup();
tree.checkedItems();
//tree.expandAll();
});
}
关于如何使其可通过键盘访问有什么想法吗?
谢谢!
不要在复选框中添加 role
、aria-checked
或 tabindex
。这些已经内置到控件中,因此您增加了在路上破坏它的风险。您可能还可以摆脱每个 role="presentation"
,因为它们在 <div>
和 <span>
上,它们本质上是表象。最后,如果您希望复选框可以访问,则需要在与复选框关联的每个文本块上使用 <label>
。 aria-describedby
不正确,无论如何都是不太好的选择。
我收到错误:Uncaught TypeError: tree.checkedItems is not a function
(第 159 行)
你的注意力管理也有很大的问题。将以下内容放入 CSS 中,您将看到每个控件需要按两次 Tab
键(如果从聚焦的复选框开始)::focus {outline:2px solid #f00;}
看起来您的包含元素窃取了所有点击,这意味着永远不会选择正确的元素。带有 类 dijit dijitReset dijitInline dijitCheckBox
的 <span>
在将其 tabindex
从 -1
切换到 0
时不断窃取焦点,使自己进出选项卡命令。这可能是一个因素。
我建议解决脚本错误,然后查看焦点管理。
使用 dijit 时,后台会发生各种您无法控制的事情。正如 aardrian 所说,有很多 role=presentation 并且 已经处理了选择,它的角色本质上是一个复选框。这些 aria 属性适用于您使用 div/span 标签创建自定义复选框的情况。
代码中隐藏了一个本机复选框,但它的不透明度设置为 0,因此您看不到它。 dijit 可能将其用于复选框事件。
原生checkbox还有data-dojo-attach-event="ondijitclick:_onClick"。我不确定那是什么意思,但每当我在事件名称中看到 "click" 时,我都会怀疑它可能不适用于键盘。
我在 https://dojotoolkit.org/reference-guide/1.10/dijit/form/CheckBox.html 上试过这个例子,它适用于键盘。点击 space 将选中和取消选中该框。是否可以看到复选框上的焦点是另一个问题。
附带说明一下,如果您的复选框树使用 aria-checked="mixed" 作为父分支可能会更好。任何时候你有子复选框,其中一些被选中而一些没有,你可以使用 "mixed" 作为父复选框来指示选择的混合。不确定 dijit 是否支持。
查看 dijit/Tree 源代码,我发现它将函数 _onNodePress() 设置为键盘事件的事件处理程序。您可以覆盖它(或在它之后添加一个方面)并手动处理您想要的按键操作。它以树节点和事件对象作为参数,您可以使用它们专门检查 space 和回车键。
我用一个例子 fork 你的 jsfiddle:https://jsfiddle.net/pgianna/jjore5sm/1/
_onNodePress: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
// This is the original implementation of _onNodePress:
this.focusNode(nodeWidget);
// This requires "dojo/keys"
if (e.keyCode == keys.ENTER || e.keyCode == keys.SPACE)
{
var cb = nodeWidget.checkBox;
cb.set('checked', !cb.get('checked'));
}
}