Difference between revisions of "ARIA Tree"

From Level Access Web Labs
Jump to navigation Jump to search
 
(18 intermediate revisions by the same user not shown)
Line 1: Line 1:
 +
Please use this codepen example for testing.
 +
<html>
 +
<a href="http://codepen.io/team/universitysandbox/pen/gagXLm/"> ARIA Tree example</a>
 +
</html>
 +
 +
The content below this line is not functional.
 
<html>
 
<html>
 
<head>
 
<head>
<script type="text/javascript">
+
  <style type="text/css">
var g_focusHandled = false; // set to true if focus is handled
+
  ul {
 
+
     list-style: none;
//
+
  }
// Function resetFocusFlag() is a callback to reset the focusHandled flag. This is
+
   </style>
// called by the timer set in the treeview focus handler
 
//
 
function resetFocusFlag() {
 
  g_focusHandled = false;
 
}
 
 
 
$(document).ready(function() {
 
 
 
  var treeviewApp = new treeview('tree1');
 
 
 
}); // end ready
 
 
 
//
 
// Function keyCodes() is an object to define keycodes for the application
 
//
 
function keyCodes() {
 
 
 
  this.enter      = 13;
 
  this.space      = 32;
 
 
 
  this.pageup    = 33;
 
  this.pagedown  = 34;
 
  this.end        = 35;
 
  this.home      = 36;
 
  this.left      = 37;
 
  this.up        = 38;
 
  this.right     = 39;
 
  this.down      = 40;
 
  this.asterisk  = 106;
 
 
 
} // end keyCodes()
 
 
 
//
 
// Function treeview() is a class constructor for a treeview widget. The widget binds to an
 
// unordered list. The top-level <ul> must have role='tree'. All list items must have role='treeitem'.
 
//
 
// Tree groups must be embedded lists within the listitem that heads the group. the top <ul> of a group
 
// must have role='group'. aria-expanded is used to indicate whether a group is expanded or collapsed. This
 
// property must be set on the listitem the encapsulates the group.
 
//
 
// @param (treeID string) treeID is the html id of the top-level <ul> of the list to bind the widget to
 
//
 
// @return N/A
 
//
 
function treeview(treeID) {
 
 
 
  // define the object properties
 
  this.$id = $('#' + treeID);
 
  this.$listitems = this.$id.find('li'); // an array of list items
 
  this.$groups = undefined; // an array of the listitems that function as group headers
 
  this.$visibleItems = undefined; // an array of currently visible listitems (including headers)
 
  this.focusHandled = false; // Set to true when a focus event is handled and reset after a small delay
 
 
 
  this.keys = new keyCodes();
 
 
 
  // initialize the treeview
 
  this.init();
 
 
 
  // bind event handlers
 
  this.bindHandlers();
 
 
 
} // end treeview() constructor
 
 
 
//
 
// Function init() is a member function to initialize the treeview widget. It traverses the tree, identifying
 
// which listitems are headers for groups and applying initial collapsed are expanded styling
 
//
 
// @return N/A
 
//
 
treeview.prototype.init = function() {
 
 
 
  var thisObj = this;
 
 
 
  // iterate through the tree and apply the groupHeader class and styling to the group headers
 
  this.$id.find('li').each (function(index) {
 
 
 
    var $group = $(this).children('ul');
 
 
 
    if ($group.length > 0) {
 
      // this listitem is a group header
 
 
 
      // Apply the group header styling
 
      $(this).addClass('groupHeader');
 
 
 
      // insert the header image. Note: this method allows the widget to degrade gracefully
 
      // if javascript is disabled or there is some other error.
 
      $(this).prepend('<img class="headerImg" src="images/treeExpanded.gif" />');
 
 
 
      // If the aria-expanded is false, hide the group and display the collapsed state image
 
      if ($(this).attr('aria-expanded') == 'false') {
 
        $group.hide();
 
        $(this).find('img').attr('src', 'images/treeContracted.gif');
 
      }
 
    }
 
  });
 
 
 
  // create the group and initial visible item array
 
  this.$groups = $('li.groupHeader');
 
  this.$visibleItems = this.$id.find('li:visible');
 
 
 
} // end init()
 
 
 
//
 
// Function expandGroup() is a member function to expand a collapsed group
 
//
 
// @param($id object) $id is the jquery id of the group header of the group to expand
 
//
 
// @param(focus boolean) focus is true if the group header has focus, false otherwise
 
//
 
// @return N/A
 
//
 
treeview.prototype.expandGroup = function($id, focus) {
 
 
 
  var $group = $id.children('ul');
 
 
 
  // expand the group
 
  $group.show();
 
 
 
  $id.attr('aria-expanded', 'true');
 
 
 
  if (focus == true) {
 
    $id.children('img').attr('src', 'images/treeExpandedFocus.gif');
 
  }
 
  else {
 
    $id.children('img').attr('src', 'images/treeExpanded.gif');
 
  }
 
 
 
  // refresh the list of visible items
 
  this.$visibleItems = this.$id.find('li:visible');
 
 
 
} // end expandGroup()
 
 
 
//
 
// Function collapseGroup() is a member function to collapse an expanded group
 
//
 
// @param($id object) $id is the jquery id of the group header of the group to collapse
 
//
 
// @param(focus boolean) focus is true if the group header has focus, false otherwise
 
//
 
// @return N/A
 
//
 
treeview.prototype.collapseGroup = function($id, focus) {
 
 
 
  var $group = $id.children('ul');
 
 
 
  // collapse the group
 
  $group.hide();
 
 
 
  $id.attr('aria-expanded', 'false');
 
 
 
  if (focus == true) {
 
    $id.children('img').attr('src', 'images/treeContractedFocus.gif');
 
  }
 
  else {
 
    $id.children('img').attr('src', 'images/treeContracted.gif');
 
  }
 
 
 
  // refresh the list of visible items
 
  this.$visibleItems = this.$id.find('li:visible');
 
 
 
} // end collapseGroup()
 
 
 
//
 
// Function toggleGroup() is a member function to toggle the display state of a group
 
//
 
// @param($id object) $id is the jquery id of the group header of the group to toggle
 
//
 
// @param(focus boolean) focus is true if the group header has focus, false otherwise
 
//
 
// @return N/A
 
//
 
treeview.prototype.toggleGroup = function($id, focus) {
 
 
 
   var $group = $id.children('ul');
 
 
 
  if ($id.attr('aria-expanded') == 'true') {
 
    // collapse the group
 
    this.collapseGroup($id, focus);
 
  }
 
  else {
 
    // expand the group
 
    this.expandGroup($id, focus);
 
  }
 
 
 
} // end toggleGroup()
 
 
 
//
 
// Function bindHandlers() is a member function to bind event handlers to the listitems
 
//
 
// return N/A
 
//
 
treeview.prototype.bindHandlers = function() {
 
 
 
  var thisObj = this;
 
 
 
  // bind a dblclick handler to the group headers
 
  this.$groups.dblclick(function(e) {
 
    return thisObj.handleDblClick($(this), e);
 
  });
 
 
 
  // bind a click handler
 
  this.$listitems.click(function(e) {
 
    return thisObj.handleClick($(this), e);
 
  });
 
 
 
  // bind a keydown handler
 
  this.$listitems.keydown(function(e) {
 
    return thisObj.handleKeyDown($(this), e);
 
  });
 
 
 
  // bind a keypress handler
 
  this.$listitems.keypress(function(e) {
 
    return thisObj.handleKeyPress($(this), e);
 
  });
 
 
 
  // bind a focus handler
 
  this.$listitems.focus(function(e) {
 
    return thisObj.handleFocus($(this), e);
 
  });
 
 
 
  // bind a blur handler
 
  this.$listitems.blur(function(e) {
 
    return thisObj.handleBlur($(this), e);
 
  });
 
 
 
} // end bindHandlers()
 
 
 
//
 
// Function doHighlight() is a member function to remove the highlighting from
 
// other treeview items and apply it to the passed element
 
//
 
// @param ($id object) $id is the jQuery object of the element to highlight
 
//
 
// @param (isHeader boolean) isHeader is true if $id points to a group header
 
//
 
// @return N/A
 
//
 
treeview.prototype.doHighlight = function($id, isHeader) {
 
 
 
  // remove the focus highlighting from the treeview items
 
  // and remove them from the tab order.
 
  this.$listitems.removeClass('focus').attr('tabindex', '-1');
 
 
 
  // remove the focus image from group headers
 
  this.$groups.each(function() {
 
    // add the focus image
 
    if ($(this).attr('aria-expanded') == 'true') {
 
      $(this).children('img').attr('src', 'images/treeExpanded.gif');
 
    }
 
    else {
 
      $(this).children('img').attr('src', 'images/treeContracted.gif');
 
    }
 
  });
 
 
 
  if (isHeader == true) {
 
    // add the focus image
 
    if ($id.attr('aria-expanded') == 'true') {
 
      $id.children('img').attr('src', 'images/treeExpandedFocus.gif');
 
    }
 
    else {
 
      $id.children('img').attr('src', 'images/treeContractedFocus.gif');
 
    }
 
  }
 
 
 
 
 
  // apply the focus highlighting and place the element in the tab order
 
  $id.addClass('focus').attr('tabindex', '0');
 
 
 
} // end doHighlight()
 
 
 
//
 
// Function handleKeyDown() is a member function to process keydown events for the treeview items
 
//
 
// @param ($id object) $id is the jQuery id of the group header firing event
 
//
 
// @param (e object) e is the associated event object
 
//
 
// @return (boolean) returns false if consuming event; true if not
 
//
 
treeview.prototype.handleKeyDown = function($id, e) {
 
 
 
  var curNdx = this.$visibleItems.index($id);
 
  var isHeader = false;
 
 
 
  // determine if this is a group header
 
  if (this.$groups.index($prev) != -1) {
 
    isHeader = true;
 
  }
 
 
 
  if (e.altKey || e.ctrlKey || e.shiftKey) {
 
    // do nothing
 
    return true;
 
  }
 
 
 
  switch (e.keyCode) {
 
    case this.keys.home: {
 
      this.$groups.first().focus();
 
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.end: {
 
      this.$visibleItems.last().focus();
 
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.enter: {
 
 
 
      if (isHeader == false) {
 
        // do nothing
 
        return true;
 
      }
 
 
 
      this.toggleGroup($id, true);
 
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.left: {
 
     
 
      if (isHeader == false) {
 
        // do nothing
 
        return true;
 
      }
 
 
 
      if ($id.attr('aria-expanded') == 'true') {
 
        this.collapseGroup($id, true);
 
      }
 
      else {
 
        // move to previous group header
 
        var prevNdx = this.$groups.index($id) - 1;
 
 
 
        if (prevNdx >= 0) {
 
          var $prev = this.$groups.eq(prevNdx);
 
          var parentFound = false;
 
 
 
          while (parentFound == false) {
 
            if ($prev.find('#' + $id.attr('id')).length > 0) {
 
              parentFound = true;
 
              $prev.focus();
 
              break;
 
            }
 
            else {
 
              // decrement prevNdx to reference the previous
 
              // group header in the $groups array
 
              prevNdx--;
 
 
 
              $prev = this.$groups.eq(prevNdx);
 
 
 
              if (prevNdx < 0) {
 
                // no parent group header
 
                break;
 
              }
 
            }
 
 
 
          } // end while
 
        }
 
      }
 
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.right: {
 
     
 
      if (isHeader == false) {
 
        // do nothing
 
        return true;
 
      }
 
 
 
      if ($id.attr('aria-expanded') == 'false') {
 
        this.expandGroup($id, true);
 
      }
 
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.up: {
 
 
 
      if (curNdx > 0) {
 
        var $prev = this.$visibleItems.eq(curNdx - 1);
 
 
 
        $prev.focus();
 
      }
 
 
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.down: {
 
 
 
      if (curNdx < this.$visibleItems.length - 1) {
 
        var $next = this.$visibleItems.eq(curNdx + 1);
 
 
 
        $next.focus();
 
      }
 
      e.stopPropagation();
 
      return false;
 
    }
 
    case this.keys.asterisk: {
 
      // expand all groups
 
 
 
      var thisObj = this;
 
 
 
      this.$groups.each(function() {
 
        thisObj.expandGroup($(this), false);
 
      });
 
  
       e.stopPropagation();
+
  <script type="text/javascript">
       return false;
+
    function test() {
 +
       var col = document.querySelectorAll("#tree li");
 +
       document.getElementById("in").value = col.length;
 
     }
 
     }
  }
 
 
  return true;
 
 
} // end handleKeyDown
 
 
//
 
// Function handleKeyPress() is a member function to process keypress events for the treeview items
 
// This function is needed for browsers, such as Opera, that perform window manipulation on kepress events
 
// rather than keydown. The function simply consumes the event.
 
//
 
// @param ($id object) $id is the jQuery id of the group header firing event
 
//
 
// @param (e object) e is the associated event object
 
//
 
// @return (boolean) returns false if consuming event; true if not
 
//
 
treeview.prototype.handleKeyPress = function($id, e) {
 
 
  if (e.altKey || e.ctrlKey || e.shiftKey) {
 
    // do nothing
 
    return true;
 
  }
 
 
  switch (e.keyCode) {
 
    case this.keys.enter:
 
    case this.keys.home:
 
    case this.keys.end:
 
    case this.keys.left:
 
    case this.keys.right:
 
    case this.keys.up:
 
    case this.keys.down: {
 
      e.stopPropagation();
 
      return false;
 
    }
 
  }
 
 
  return true;
 
 
} // end handleKeyPress
 
 
//
 
// Function handleDblClick() is a member function to process double-click events for group headers.
 
// Double-click expands or collapses a group.
 
//
 
// @param ($id object) $id is the jQuery id of the group header firing event
 
//
 
// @param (e object) e is the associated event object
 
//
 
// @return (boolean) returns false if consuming event; true if not
 
//
 
treeview.prototype.handleDblClick = function($id, e) {
 
 
  if (e.altKey || e.ctrlKey || e.shiftKey) {
 
    // do nothing
 
    return true;
 
  }
 
 
  // apply the focus highlighting
 
  this.doHighlight($id, true);
 
 
  // expand or collapse the group
 
  this.toggleGroup($id, true);
 
 
  e.stopPropagation();
 
  return false;
 
 
} // end handleDblClick
 
 
//
 
// Function handleClick() is a member function to process click events.
 
//
 
// @param ($id object) $id is the jQuery id of the group header firing event
 
//
 
// @param (e object) e is the associated event object
 
//
 
// @return (boolean) returns false if consuming event; true if not
 
//
 
treeview.prototype.handleClick = function($id, e) {
 
 
  if (e.altKey || e.ctrlKey || e.shiftKey) {
 
    // do nothing
 
    return true;
 
  }
 
 
  if (this.$groups.index($id) == -1) {
 
    // this is a list item
 
 
    // apply the focus highlighting
 
    this.doHighlight($id, false);
 
  }
 
  else {
 
    // this is a group header
 
 
    // apply the focus highlighting
 
    this.doHighlight($id, true);
 
  }
 
 
  e.stopPropagation();
 
  return false;
 
 
} // end handleClick
 
 
//
 
// Function handleFocus() is a member function to process focus events.
 
//
 
// @param ($id object) $id is the jQuery id of the group header firing event
 
//
 
// @param (e object) e is the associated event object
 
//
 
// @return (boolean) returns true
 
//
 
treeview.prototype.handleFocus = function($id, e) {
 
 
  // only process the event if the focusHandled flag is false
 
  if (g_focusHandled == false) {
 
 
    // prevent encapsulating group headers from responding to
 
    // the focus event.
 
    g_focusHandled = true;
 
 
 
    if (this.$groups.index($id) == -1) {
 
      this.doHighlight($id, false);
 
    }
 
    else {
 
      // this is a group header
 
      this.doHighlight($id, true);
 
    }
 
 
    window.setTimeout(resetFocusFlag, 10);
 
  }
 
 
  return true;
 
 
} // end handleFocus
 
 
//
 
// Function handleBlur() is a member function to process blur events.
 
//
 
// @param ($id object) $id is the jQuery id of the group header firing event
 
//
 
// @param (e object) e is the associated event object
 
//
 
// @return (boolean) returns true
 
//
 
treeview.prototype.handleBlur = function($id, e) {
 
 
  if (this.$groups.index($id) != -1) {
 
    // this is a group header
 
 
    // remove the focus image
 
    if ($id.attr('aria-expanded') == 'true') {
 
      $id.children('img').attr('src', 'images/treeExpanded.gif');
 
    }
 
    else {
 
      $id.children('img').attr('src', 'images/treeContracted.gif');
 
    }
 
  }
 
 
  // remove the focus highlighting
 
  $id.removeClass('focus');
 
 
  return true;
 
 
} // end handleBlur
 
 
   </script>
 
   </script>
<style type="text/css">
 
ul.tree {
 
  width: 10em;
 
}
 
ul.tree, ul.tree ul {
 
  list-style: none;
 
  margin: 0;
 
  padding-left: 20px;
 
  font-weight: normal;
 
  background-color: white;
 
  color: black;
 
}
 
 
ul.tree li {
 
  margin-left: 17px;
 
}
 
 
ul.tree li.groupHeader {
 
  font-weight: bold;
 
  margin-left: 0px;
 
}
 
 
img.headerImg {
 
  margin-right: 5px;
 
}
 
 
li.focus {
 
  color: white;
 
  background: black;
 
}
 
  </style>
 
 
</head>
 
</head>
 
<body>
 
<body>
<div role="application">
 
  
<h2 id="label_1">Foods</h2>
+
<label id="courses">Courses 2</label>
<ul id="tree1" class="tree" role="tree" aria-labelledby="label_1">
+
<ul id="tree" class="tree" role="tree" aria-labelledby="courses">
   <li id="fruits" role="treeitem" tabindex="0" aria-expanded="true">Fruits
+
   <li aria-label="web" aria-level="1" id="web" role="treeitem" tabindex="0" aria-expanded="true">Web
 
     <ul role="group">
 
     <ul role="group">
       <li id="oranges" role="treeitem" tabindex="-1">Oranges</li>
+
       <li aria-label="web basics" aria-level="2" id="webbasic" role="treeitem" tabindex="0">Web Basic</li>
      <li id="pinapples" role="treeitem" tabindex="-1">Pineapples</li>
+
      <li aria-label="web advanced" aria-level="2" id="webadvanced" role="treeitem" tabindex="0">Web Advanced</li>
      <li id="apples" role="treeitem" tabindex="-1" aria-expanded="false">Apples
+
    </ul>
        <ul role="group">
+
  </li>
          <li id="macintosh" role="treeitem" tabindex="-1">Macintosh</li>
+
  <li aria-level="1" id="pdf" role="treeitem" tabindex="0" aria-expanded="true">PDF
          <li id="granny_smith" role="treeitem" tabindex="-1" aria-expanded="false">Granny Smith
 
            <ul role="group">
 
              <li id="Washington" role="treeitem" tabindex="-1">Washington State</li>
 
              <li id="Michigan" role="treeitem" tabindex="-1">Michigan</li>
 
              <li id="New_York" role="treeitem" tabindex="-1">New York</li>
 
            </ul>
 
          </li>
 
          <li id="fuji" role="treeitem" tabindex="-1">Fuji</li>
 
        </ul>
 
      </li>
 
      <li id="bananas" role="treeitem" tabindex="-1">Bananas</li>   
 
      <li id="pears" role="treeitem" tabindex="-1">Pears</li>   
 
    </ul>
 
  </li>
 
  <li id="vegetables" role="treeitem" tabindex="-1" aria-expanded="true">Vegetables
 
 
     <ul role="group">
 
     <ul role="group">
       <li id="broccoli" role="treeitem" tabindex="-1">Broccoli</li>
+
       <li aria-level="2" id="webbasic" role="treeitem" tabindex="0">PDF Basic</li>
      <li id="carrots" role="treeitem" tabindex="-1">Carrots</li>
+
       <li aria-level="2" id="webadvanced" role="treeitem" tabindex="0">PDF Advanced</li>
      <li id="lettuce" role="treeitem" tabindex="-1" aria-expanded="false">Lettuce
+
    </ul>
      <ul role="group">
+
  </li>
          <li id="romaine" role="treeitem" tabindex="-1">Romaine</li>
 
          <li id="iceberg" role="treeitem" tabindex="-1">Iceberg</li>
 
          <li id="butterhead" role="treeitem" tabindex="-1">Butterhead</li>
 
      </ul>
 
      </li>
 
      <li id="spinach" role="treeitem" tabindex="-1">Spinach</li>   
 
       <li id="squash" role="treeitem" tabindex="-1" aria-expanded="true">Squash
 
        <ul role="group" >
 
          <li id="acorn" role="treeitem" tabindex="-1">Acorn</li>
 
          <li id="ambercup" role="treeitem" tabindex="-1">Ambercup</li>
 
          <li id="autumn_cup" role="treeitem" tabindex="-1">Autumn Cup</li>
 
          <li id="hubbard" role="treeitem" tabindex="-1">Hubbard</li>
 
          <li id="kobacha" role="treeitem" tabindex="-1">Kabocha</li>
 
          <li id="butternut" role="treeitem" tabindex="-1">Butternut</li>
 
          <li id="spaghetti" role="treeitem" tabindex="-1">Spaghetti</li>
 
          <li id="sweet_dumpling" role="treeitem" tabindex="-1">Sweet Dumpling</li>
 
          <li id="turban" role="treeitem" tabindex="-1">Turban</li>
 
        </ul>
 
      </li>
 
    </ul>
 
  </li>
 
 
</ul>
 
</ul>
  
</div>  
+
<div>
 +
  <input type="text" id="in" value="" />
 +
  <button onclick="test();"> test </button>
 +
</div>
 +
 
 
</body>
 
</body>
 
</html>
 
</html>
  
 
[[Category:ARIA]]
 
[[Category:ARIA]]

Latest revision as of 18:38, 20 June 2018

Please use this codepen example for testing. ARIA Tree example

The content below this line is not functional.

  • Web
    • Web Basic
    • Web Advanced
  • PDF
    • PDF Basic
    • PDF Advanced