0

I have created a dijit.Tree object where every node is a checkbox. When you select/deselect the parent node, the child nodes get selected/deselected; when one of the children is deselected, the parent gets deselected; when all the children are selected, the parent gets selected. It works perfectly fine.
However I need it to be keyboard accessible. When I navigate to the tree nodes and press spacebar or Enter, nothing happens.

I tried adding tabindex and aria-role to the checkbox (programmatically), but it did not work.

Here is the 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();


});

}

Any ideas as to how to make it keyboard accessible?

Thanks!

pdabade
  • 13
  • 3

3 Answers3

0

Do not add role, aria-checked, nor tabindex to the checkbox. Those are already built into the control, so you are adding risk of breaking it down the road. You can probably also get rid of every role="presentation" as those are on <div>s and <span>s which are presentational by nature. Finally, you need <label> on each block of text that is associated with a checkbox if you want this to be accessible. The aria-describedby is incorrect and is the less good option anyway.

I am getting the error: Uncaught TypeError: tree.checkedItems is not a function (line 159)

You also have a big focus management problem. Put the following in your CSS and you will see that it takes two presses of the Tab key for each single control (if starting at a focused checkbox): :focus {outline:2px solid #f00;}

It looks like you have the containing elements stealing any clicks, meaning the correct element never gets selected. The <span> with classes dijit dijitReset dijitInline dijitCheckBox keeps stealing focus as it toggles its tabindex from -1 to 0, taking itself in and out of the tab order. That may be a factor.

I suggest addressing the script error and then looking at focus management.

aardrian
  • 8,581
  • 30
  • 40
  • Fixed the error in http://jsfiddle.net/pdabade/pyz9Lcpv/65/ . I understood what you said about it taking 'two presses of Tab key for each single control'. But how do we address this issue of focus management? – pdabade Jun 13 '16 at 13:17
0

With dijit, there's all kinds of stuff going on in the background that might be out of your control. As aardrian said, there's lots of role=presentation and all the aria tags on the <input type='checkbox> are superfluous. dijit is probably (incorrectly) setting all that. An <input type='checkbox> already handles selections and it's role is inherently a checkbox. Those aria properties are for when you're making a custom checkbox out of div/span tags.

There is a native checkbox buried down in the code but it has opacity set to 0 so you can't see it. dijit is probably using it for the checkbox events.

The native checkbox also has data-dojo-attach-event="ondijitclick:_onClick". I'm not sure what that means but anytime I see "click" in an event name, I get suspicious that it might not work with a keyboard.

I tried the example on https://dojotoolkit.org/reference-guide/1.10/dijit/form/CheckBox.html and it works with the keyboard. Hit space will check and uncheck the box. Whether you can see the focus on the checkbox is another issue.

As a side note, it might be nice if your checkbox tree used aria-checked="mixed" for the parent branch. Anytime you have child checkboxes where some are selected and some are not, you can use "mixed" for the parent checkbox to indicate a mixture of selections. Not sure if dijit supports that.

slugolicious
  • 15,824
  • 2
  • 29
  • 43
  • I have implemented normal dijit/form/CheckBox and it works fine with keyboard. The problem is when I use checkbox within a dijit/Tree. – pdabade Jun 13 '16 at 13:20
0

Looking into the dijit/Tree source I see that it sets the function _onNodePress() as an event handler for keyboard events. You can override it (or add an aspect after it) and handle the key presses you want manually. It takes as argument the tree node and an event object that you can use to check specifically for the space and the enter key.

I forked your jsfiddle with an example: 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'));
    }
}
pgianna
  • 715
  • 1
  • 7
  • 16