/* * JQuery zTree keyboard navigation extension * zTree v3.5.42 or later * http://www.xbrlquery.com/ * * Copyright (c) 2019 Bill Seddon * * Licensed same as jquery - MIT License * http://www.opensource.org/licenses/mit-license.php * * Date: 2020-02-18 */ ( function ($) { /** * Creates a function that adds keyboard navigation: * Home: home key (keycode 36) Goes to the first root element is visible * End: end key (keycode 35) Goes to the last leaf node and will expand nodes and scroll the element into view * Down: right cursor key (keycode 39) Goes to the next visible node in the tree following the hierarchy * Next: down cursor key (keycode 40) Goes to the next visible node at the same level * Up: up cursor key (keycode 37) Goes to the prior visible node at the same level * Previous: left cursor key (keycode 38) Goes to the prior visible node following the hierarchy * Toggle: space key (keycode 32) Toggles the expand/collapse state of a parent node */ $.fn.zTreeKeyboardNavigation = function(zTree, element) { if (typeof element === 'string' || element instanceof String) { element = $(element); } var rootNodes = zTree.getNodes(); if ( ! rootNodes ) return; var focusSelectedNode = function() { if( ( selectedNodes = zTree.getSelectedNodes() ) && selectedNodes.length ) { $("#" + selectedNodes[0].tId ).focus(); } } $(element).bind( 'keydown', function( e ) { var selectedNodes = zTree.getSelectedNodes(); var selectedNode = selectedNodes.length ? selectedNodes[0] : null; var processSpace = function() { // If there are no nodes or the selected node is not a parent, get out if ( selectedNode && selectedNode.isParent ) { // Toggle the node zTree.expandNode( selectedNode, null, null, null, false ); } } var processHome = function() { zTree.selectNode( rootNodes[0], false, true ); } var processEnd = function() { var nodes = zTree.transformToArray(rootNodes); // Select the last node zTree.selectNode( nodes[ nodes.length - 1 ] ); } var processUp = function() { var priorNode; if ( selectedNode ) { priorNode = selectedNode.getPreNode(); if ( ! priorNode ) return; } else { processEnd(); } if ( ! priorNode ) return; zTree.selectNode( priorNode ); } var processDown = function() { var nextNode; if ( selectedNode ) { nextNode = selectedNode.getNextNode(); if ( ! nextNode ) return; } else { processHome(); } if ( ! nextNode ) return; zTree.selectNode( nextNode ); } var processOut = function() { if ( ! selectedNode ) return; var parentNode = selectedNode.getParentNode(); var priorNode = selectedNode.getPreNode(); if ( ! parentNode && ! priorNode ) return; // Must have been the root node if ( priorNode ) { if ( priorNode.isParent ) { // There is a prior node, now the the question is where is the last open node? while ( priorNode ) { if ( ! priorNode.isParent || ! priorNode.open || ! priorNode.children ) break; priorNode = priorNode.children[ priorNode.children.length -1 ]; } zTree.selectNode( priorNode ); return; } else { zTree.selectNode( priorNode ); return; } } // Find the parent node with a valid prior sibling if ( parentNode ) { // This call should be silent otherwise (in my view a bug in) // selectNode causes the root node to blur zTree.selectNode( parentNode, false, true ); } } var processIn = function() { if ( ! selectedNode ) return; if ( selectedNode.isParent && selectedNode.open && selectedNode.children ) { zTree.selectNode( selectedNode.children[0] ); return; } var nextNode = selectedNode.getNextNode(); if ( nextNode ) { zTree.selectNode( nextNode ); } else { // Cannot be root if there is a selected node that is not a parent var node = selectedNode; // Find the parent node with a valid next sibling while( node = node.getParentNode() ) { var nextNode = node.getNextNode(); if ( nextNode ) { zTree.selectNode( nextNode ); break; } } } } var processLetter = function( keyCode ) { if ( ! Array.from( {length: 26}, (v, i) => i + 65 ).includes( keyCode & 95 ) ) return false; var nodes = zTree.transformToArray(rootNodes); nodes = nodes.filter( node => { return 'accesskey' in node && node.accesskey.length && ( node.accesskey.charCodeAt(0) & 95 ) == keyCode; } ); if ( ! nodes.length ) return false; var selectedNodes = zTree.getSelectedNodes(); if ( ! selectedNodes.length ) return false; if ( selectedNodes[0] == nodes[0] ) return false; zTree.selectNode( nodes[0] ); return true; } // console.log('before'); // console.log(document.activeElement); switch ( e.keyCode ) { case 32: /* Toggle parent nodes */ processSpace(); return; case 36: /* Home - go to the root node */ processHome(); break; case 35: /* End - go to the last node */ processEnd(); break; case 33: /* PageUp */ // Do nothing break; case 34: /* PageDown */ // Do nothing break; case 37: /* Left */ processOut(); break; case 38: /* Up */ processUp(); break; case 39: /* Right */ processIn(); break; case 40: /* Down */ processDown(); break; default: if ( ! processLetter( e.keyCode & 95 ) ) return; break; } // console.log('after'); // console.log(document.activeElement); focusSelectedNode(); } ); $(element).trigger({ type : 'keydown', which : 36, keyCode: 36 }); focusSelectedNode(); } } )(jQuery);