"Fossies" - the Fresh Open Source Software Archive

Member "angular.js-1.7.9/docs/content/guide/accessibility.ngdoc" (19 Nov 2019, 16682 Bytes) of package /linux/www/angular.js-1.7.9.tar.gz:


As a special service "Fossies" has tried to format the requested text file into HTML format (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

    1 @ngdoc overview
    2 @name  Accessibility
    3 @sortOrder 530
    4 @description
    5 
    6 
    7 # Accessibility with ngAria
    8 
    9 The goal of ngAria is to improve AngularJS's default accessibility by enabling common
   10 [ARIA](http://www.w3.org/TR/wai-aria/) attributes that convey state or semantic information for
   11 assistive technologies used by persons with disabilities.
   12 
   13 ## Including ngAria
   14 
   15 Using {@link ngAria ngAria} is as simple as requiring the ngAria module in your application. ngAria hooks into
   16 standard AngularJS directives and quietly injects accessibility support into your application
   17 at runtime.
   18 
   19 ```js
   20 angular.module('myApp', ['ngAria'])...
   21 ```
   22 
   23 ### Using ngAria
   24 Most of what ngAria does is only visible "under the hood". To see the module in action, once you've
   25 added it as a dependency, you can test a few things:
   26  * Using your favorite element inspector, look for attributes added by ngAria in your own code.
   27  * Test using your keyboard to ensure `tabindex` is used correctly.
   28  * Fire up a screen reader such as VoiceOver or NVDA to check for ARIA support.
   29 [Helpful screen reader tips.](http://webaim.org/articles/screenreader_testing/)
   30 
   31 ## Supported directives
   32 Currently, ngAria interfaces with the following directives:
   33 
   34  * {@link guide/accessibility#ngmodel ngModel}
   35  * {@link guide/accessibility#ngdisabled ngDisabled}
   36  * {@link guide/accessibility#ngrequired ngRequired}
   37  * {@link guide/accessibility#ngreadonly ngReadonly}
   38  * {@link guide/accessibility#ngvaluechecked ngChecked}
   39  * {@link guide/accessibility#ngvaluechecked ngValue}
   40  * {@link guide/accessibility#ngshow ngShow}
   41  * {@link guide/accessibility#nghide ngHide}
   42  * {@link guide/accessibility#ngclick ngClick}
   43  * {@link guide/accessibility#ngdblclick ngDblClick}
   44  * {@link guide/accessibility#ngmessages ngMessages}
   45 
   46 <h2 id="ngmodel">ngModel</h2>
   47 
   48 Much of ngAria's heavy lifting happens in the {@link ng.ngModel ngModel}
   49 directive. For elements using ngModel, special attention is paid by ngAria if that element also
   50 has a role or type of `checkbox`, `radio`, `range` or `textbox`.
   51 
   52 For those elements using ngModel, ngAria will dynamically bind and update the following ARIA
   53 attributes (if they have not been explicitly specified by the developer):
   54 
   55  * aria-checked
   56  * aria-valuemin
   57  * aria-valuemax
   58  * aria-valuenow
   59  * aria-invalid
   60  * aria-required
   61  * aria-readonly
   62  * aria-disabled
   63 
   64 ### Example
   65 
   66 <example module="ngAria_ngModelExample" deps="angular-aria.js" name="accessibility-ng-model">
   67   <file name="index.html">
   68     <form>
   69       <custom-checkbox role="checkbox" ng-model="checked" required
   70           aria-label="Custom checkbox" show-attrs>
   71         Custom checkbox
   72       </custom-checkbox>
   73     </form>
   74     <hr />
   75     <b>Is checked:</b> {{ !!checked }}
   76   </file>
   77   <file name="script.js">
   78     angular.
   79       module('ngAria_ngModelExample', ['ngAria']).
   80       directive('customCheckbox', customCheckboxDirective).
   81       directive('showAttrs', showAttrsDirective);
   82 
   83     function customCheckboxDirective() {
   84       return {
   85         restrict: 'E',
   86         require: 'ngModel',
   87         transclude: true,
   88         template:
   89             '<span class="icon" aria-hidden="true"></span> ' +
   90             '<ng-transclude></ng-transclude>',
   91         link: function(scope, elem, attrs, ctrl) {
   92           // Overwrite necessary `NgModelController` methods
   93           ctrl.$isEmpty = isEmpty;
   94           ctrl.$render = render;
   95 
   96           // Bind to events
   97           elem.on('click', function(event) {
   98             event.preventDefault();
   99             scope.$apply(toggleCheckbox);
  100           });
  101           elem.on('keypress', function(event) {
  102             event.preventDefault();
  103             if (event.keyCode === 32 || event.keyCode === 13) {
  104               scope.$apply(toggleCheckbox);
  105             }
  106           });
  107 
  108           // Helpers
  109           function isEmpty(value) {
  110             return !value;
  111           }
  112 
  113           function render() {
  114             elem[ctrl.$viewValue ? 'addClass' : 'removeClass']('checked');
  115           }
  116 
  117           function toggleCheckbox() {
  118             ctrl.$setViewValue(!ctrl.$viewValue);
  119             ctrl.$render();
  120           }
  121         }
  122       };
  123     }
  124 
  125     function showAttrsDirective($timeout) {
  126       return function(scope, elem, attrs) {
  127         var pre = document.createElement('pre');
  128         elem.after(pre);
  129 
  130         scope.$watchCollection(function() {
  131           return Array.prototype.slice.call(elem[0].attributes).reduce(function(aggr, attr) {
  132             if (attr.name !== attrs.$attr.showAttrs) aggr[attr.name] = attr.value;
  133             return aggr;
  134           }, {});
  135         }, function(newValues) {
  136           $timeout(function() {
  137             pre.textContent = angular.toJson(newValues, 2);
  138           });
  139         });
  140       };
  141     }
  142   </file>
  143   <file name="style.css">
  144     custom-checkbox {
  145       cursor: pointer;
  146       display: inline-block;
  147     }
  148 
  149     custom-checkbox .icon:before {
  150       content: '\2610';
  151       display: inline-block;
  152       font-size: 2em;
  153       line-height: 1;
  154       speak: none;
  155       vertical-align: middle;
  156     }
  157 
  158     custom-checkbox.checked .icon:before {
  159       content: '\2611';
  160     }
  161   </file>
  162   <file name="protractor.js" type="protractor">
  163     var checkbox = element(by.css('custom-checkbox'));
  164     var checkedCheckbox = element(by.css('custom-checkbox.checked'));
  165 
  166     it('should have the `checked` class only when checked', function() {
  167       expect(checkbox.isPresent()).toBe(true);
  168       expect(checkedCheckbox.isPresent()).toBe(false);
  169 
  170       checkbox.click();
  171       expect(checkedCheckbox.isPresent()).toBe(true);
  172 
  173       checkbox.click();
  174       expect(checkedCheckbox.isPresent()).toBe(false);
  175     });
  176 
  177     it('should have the `aria-checked` attribute set to the appropriate value', function() {
  178       expect(checkedCheckbox.isPresent()).toBe(false);
  179       expect(checkbox.getAttribute('aria-checked')).toBe('false');
  180 
  181       checkbox.click();
  182       expect(checkedCheckbox.isPresent()).toBe(true);
  183       expect(checkbox.getAttribute('aria-checked')).toBe('true');
  184 
  185       checkbox.click();
  186       expect(checkedCheckbox.isPresent()).toBe(false);
  187       expect(checkbox.getAttribute('aria-checked')).toBe('false');
  188     });
  189   </file>
  190 </example>
  191 
  192 ngAria will also add `tabIndex`, ensuring custom elements with these roles will be reachable from
  193 the keyboard. It is still up to **you** as a developer to **ensure custom controls will be
  194 accessible**. As a rule, any time you create a widget involving user interaction, be sure to test
  195 it with your keyboard and at least one mobile and desktop screen reader.
  196 
  197 <h2 id="ngvaluechecked">ngValue and ngChecked</h2>
  198 
  199 To ease the transition between native inputs and custom controls, ngAria now supports
  200 {@link ng.ngValue ngValue} and {@link ng.ngChecked ngChecked}.
  201 The original directives were created for native inputs only, so ngAria extends
  202 support to custom elements by managing `aria-checked` for accessibility.
  203 
  204 ### Example
  205 
  206 ```html
  207 <custom-checkbox ng-checked="val"></custom-checkbox>
  208 <custom-radio-button ng-value="val"></custom-radio-button>
  209 ```
  210 
  211 Becomes:
  212 
  213 ```html
  214 <custom-checkbox ng-checked="val" aria-checked="true"></custom-checkbox>
  215 <custom-radio-button ng-value="val" aria-checked="true"></custom-radio-button>
  216 ```
  217 
  218 <h2 id="ngdisabled">ngDisabled</h2>
  219 
  220 The `disabled` attribute is only valid for certain elements such as `button`, `input` and
  221 `textarea`. To properly disable custom element directives such as `<md-checkbox>` or `<taco-tab>`,
  222 using ngAria with {@link ng.ngDisabled ngDisabled} will also
  223 add `aria-disabled`. This tells assistive technologies when a non-native input is disabled, helping
  224 custom controls to be more accessible.
  225 
  226 ### Example
  227 
  228 ```html
  229 <md-checkbox ng-disabled="disabled"></md-checkbox>
  230 ```
  231 
  232 Becomes:
  233 
  234 ```html
  235 <md-checkbox disabled aria-disabled="true"></md-checkbox>
  236 ```
  237 
  238 <div class="alert alert-info">
  239 You can check whether a control is legitimately disabled for a screen reader by visiting
  240 [chrome://accessibility](chrome://accessibility) and inspecting [the accessibility tree](http://www.paciellogroup.com/blog/2015/01/the-browser-accessibility-tree/).
  241 </div>
  242 
  243 <h2 id="ngrequired">ngRequired</h2>
  244 
  245 The boolean `required` attribute is only valid for native form controls such as `input` and
  246 `textarea`. To properly indicate custom element directives such as `<md-checkbox>` or `<custom-input>`
  247 as required, using ngAria with {@link ng.ngRequired ngRequired} will also add
  248 `aria-required`. This tells accessibility APIs when a custom control is required.
  249 
  250 ### Example
  251 
  252 ```html
  253 <md-checkbox ng-required="val"></md-checkbox>
  254 ```
  255 
  256 Becomes:
  257 
  258 ```html
  259 <md-checkbox ng-required="val" aria-required="true"></md-checkbox>
  260 ```
  261 
  262 <h2 id="ngreadonly">ngReadonly</h2>
  263 
  264 The boolean `readonly` attribute is only valid for native form controls such as `input` and
  265 `textarea`. To properly indicate custom element directives such as `<md-checkbox>` or `<custom-input>`
  266 as required, using ngAria with {@link ng.ngReadonly ngReadonly} will also add
  267 `aria-readonly`. This tells accessibility APIs when a custom control is read-only.
  268 
  269 ### Example
  270 
  271 ```html
  272 <md-checkbox ng-readonly="val"></md-checkbox>
  273 ```
  274 
  275 Becomes:
  276 
  277 ```html
  278 <md-checkbox ng-readonly="val" aria-readonly="true"></md-checkbox>
  279 ```
  280 
  281 <h2 id="ngshow">ngShow</h2>
  282 
  283 The {@link ng.ngShow ngShow} directive shows or hides the
  284 given HTML element based on the expression provided to the `ngShow` attribute. The element is
  285 shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
  286 
  287 In its default setup, ngAria for `ngShow` is actually redundant. It toggles `aria-hidden` on the
  288 directive when it is hidden or shown. However, the default CSS of `display: none !important`,
  289 already hides child elements from a screen reader. It becomes more useful when the default
  290 CSS is overridden with properties that don’t affect assistive technologies, such as `opacity`
  291 or `transform`. By toggling `aria-hidden` dynamically with ngAria, we can ensure content visually
  292 hidden with this technique will not be read aloud in a screen reader.
  293 
  294 One caveat with this combination of CSS and `aria-hidden`: you must also remove links and other
  295 interactive child elements from the tab order using `tabIndex=“-1”` on each control. This ensures
  296 screen reader users won't accidentally focus on "mystery elements". Managing tab index on every
  297 child control can be complex and affect performance, so it’s best to just stick with the default
  298 `display: none` CSS. See the [fourth rule of ARIA use](http://www.w3.org/TR/aria-in-html/#fourth-rule-of-aria-use).
  299 
  300 ### Example
  301 ```css
  302 .ng-hide {
  303   display: block;
  304   opacity: 0;
  305 }
  306 ```
  307 ```html
  308 <div ng-show="false" class="ng-hide" aria-hidden="true"></div>
  309 ```
  310 
  311 Becomes:
  312 
  313 ```html
  314 <div ng-show="true" aria-hidden="false"></div>
  315 ```
  316 *Note: Child links, buttons or other interactive controls must also be removed from the tab order.*
  317 
  318 <h2 id="nghide">ngHide</h2>
  319 
  320 The {@link ng.ngHide ngHide} directive shows or hides the
  321 given HTML element based on the expression provided to the `ngHide` attribute. The element is
  322 shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
  323 
  324 The default CSS for `ngHide`, the inverse method to `ngShow`, makes ngAria redundant. It toggles
  325 `aria-hidden` on the directive when it is hidden or shown, but the content is already hidden with
  326 `display: none`. See explanation for {@link guide/accessibility#ngshow ngShow} when overriding the default CSS.
  327 
  328 <h2><span id="ngclick">ngClick</span> and <span id="ngdblclick">ngDblclick</span></h2>
  329 If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex="0"` to any element not in
  330 a node blacklist:
  331 
  332  * Button
  333  * Anchor
  334  * Input
  335  * Textarea
  336  * Select
  337  * Details/Summary
  338 
  339 To fix widespread accessibility problems with `ng-click` on `div` elements, ngAria will
  340 dynamically bind a keypress event by default as long as the element isn't in the node blacklist.
  341 You can turn this functionality on or off with the `bindKeypress` configuration option.
  342 
  343 ngAria will also add the `button` role to communicate to users of assistive technologies. This can
  344 be disabled with the `bindRoleForClick` configuration option.
  345 
  346 For `ng-dblclick`, you must still manually add `ng-keypress` and a role to non-interactive elements
  347 such as `div` or `taco-button` to enable keyboard access.
  348 
  349 <h3>Example</h3>
  350 ```html
  351 <div ng-click="toggleMenu()"></div>
  352 ```
  353 
  354 Becomes:
  355 ```html
  356 <div ng-click="toggleMenu()" tabindex="0"></div>
  357 ```
  358 
  359 <h2 id="ngmessages">ngMessages</h2>
  360 
  361 The ngMessages module makes it easy to display form validation or other messages with priority
  362 sequencing and animation. To expose these visual messages to screen readers,
  363 ngAria injects `aria-live="assertive"`, causing them to be read aloud any time a message is shown,
  364 regardless of the user's focus location.
  365 ### Example
  366 
  367 ```html
  368 <div ng-messages="myForm.myName.$error">
  369   <div ng-message="required">You did not enter a field</div>
  370   <div ng-message="maxlength">Your field is too long</div>
  371 </div>
  372 ```
  373 
  374 Becomes:
  375 
  376 ```html
  377 <div ng-messages="myForm.myName.$error" aria-live="assertive">
  378   <div ng-message="required">You did not enter a field</div>
  379   <div ng-message="maxlength">Your field is too long</div>
  380 </div>
  381 ```
  382 
  383 ## Disabling attributes
  384 The attribute magic of ngAria may not work for every scenario. To disable individual attributes,
  385 you can use the {@link ngAria.$ariaProvider#config config} method. Just keep in mind this will
  386 tell ngAria to ignore the attribute globally.
  387 
  388 <example module="ngAria_ngClickExample" deps="angular-aria.js" name="accessibility-ng-click">
  389  <file name="index.html">
  390   <div ng-click="someFunction" show-attrs>
  391     &lt;div&gt; with ng-click and bindRoleForClick, tabindex set to false
  392   </div>
  393  <script>
  394   angular.module('ngAria_ngClickExample', ['ngAria'], function config($ariaProvider) {
  395     $ariaProvider.config({
  396       bindRoleForClick: false,
  397       tabindex: false
  398     });
  399   })
  400   .directive('showAttrs', function() {
  401     return function(scope, el, attrs) {
  402       var pre = document.createElement('pre');
  403       el.after(pre);
  404       scope.$watch(function() {
  405         var attrs = {};
  406         Array.prototype.slice.call(el[0].attributes, 0).forEach(function(item) {
  407           if (item.name !== 'show-attrs') {
  408             attrs[item.name] = item.value;
  409           }
  410         });
  411         return attrs;
  412       }, function(newAttrs, oldAttrs) {
  413         pre.textContent = JSON.stringify(newAttrs, null, 2);
  414       }, true);
  415     }
  416   });
  417  </script>
  418  </file>
  419 </example>
  420 
  421 ## Common Accessibility Patterns
  422 
  423 Accessibility best practices that apply to web apps in general also apply to AngularJS.
  424 
  425  * **Text alternatives**: Add alternate text content to make visual information accessible using
  426  [these W3C guidelines](http://www.w3.org/TR/html-alt-techniques/). The appropriate technique
  427  depends on the specific markup but can be accomplished using offscreen spans, `aria-label` or
  428  label elements, image `alt` attributes, `figure`/`figcaption` elements and more.
  429  * **HTML Semantics**: If you're creating custom element directives, Web Components or HTML in
  430  general, use native elements wherever possible to utilize built-in events and properties.
  431  Alternatively, use ARIA to communicate semantic meaning. See [notes on ARIA use](http://www.w3.org/TR/aria-in-html/#notes-on-aria-use-in-html).
  432  * **Focus management**: Guide the user around the app as views are appended/removed.
  433  Focus should *never* be lost, as this causes unexpected behavior and much confusion (referred to
  434  as "freak-out mode").
  435  * **Announcing changes**: When filtering or other UI messaging happens away from the user's focus,
  436  notify with [ARIA Live Regions](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions).
  437  * **Color contrast and scale**: Make sure content is legible and interactive controls are usable
  438  at all screen sizes. Consider configurable UI themes for people with color blindness, low vision
  439  or other visual impairments.
  440  * **Progressive enhancement**: Some users do not browse with JavaScript enabled or do not have
  441  the latest browser. An accessible message about site requirements can inform users and improve
  442  the experience.
  443 
  444 ## Additional Resources
  445 
  446  * [Using ARIA in HTML](http://www.w3.org/TR/aria-in-html/)
  447  * [AngularJS Accessibility at ngEurope](https://www.youtube.com/watch?v=dmYDggEgU-s&list=UUEGUP3TJJfMsEM_1y8iviSQ)
  448  * [Testing with Screen Readers](http://webaim.org/articles/screenreader_testing/)
  449  * [Chrome Accessibility Developer Tools](https://chrome.google.com/webstore/detail/accessibility-developer-t/fpkknkljclfencbdbgkenhalefipecmb?hl=en)
  450  * [W3C Accessibility Testing](http://www.w3.org/wiki/Accessibility_testing)
  451  * [WebAIM](http://webaim.org)
  452  * [A11y Project](http://a11yproject.com)