"Fossies" - the Fresh Open Source Software Archive

Member "angular.js-1.8.2/docs/content/guide/accessibility.ngdoc" (21 Oct 2020, 16722 Bytes) of package /linux/www/angular.js-1.8.2.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. See also the last Fossies "Diffs" side-by-side code changes report for "accessibility.ngdoc": 1.8.0_vs_1.8.1.

    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 the list of built in aria nodes:
  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 a node from the list of
  341 built in aria nodes.
  342 You can turn this functionality on or off with the `bindKeypress` configuration option.
  343 
  344 ngAria will also add the `button` role to communicate to users of assistive technologies. This can
  345 be disabled with the `bindRoleForClick` configuration option.
  346 
  347 For `ng-dblclick`, you must still manually add `ng-keypress` and a role to non-interactive elements
  348 such as `div` or `taco-button` to enable keyboard access.
  349 
  350 <h3>Example</h3>
  351 ```html
  352 <div ng-click="toggleMenu()"></div>
  353 ```
  354 
  355 Becomes:
  356 ```html
  357 <div ng-click="toggleMenu()" tabindex="0"></div>
  358 ```
  359 
  360 <h2 id="ngmessages">ngMessages</h2>
  361 
  362 The ngMessages module makes it easy to display form validation or other messages with priority
  363 sequencing and animation. To expose these visual messages to screen readers,
  364 ngAria injects `aria-live="assertive"`, causing them to be read aloud any time a message is shown,
  365 regardless of the user's focus location.
  366 ### Example
  367 
  368 ```html
  369 <div ng-messages="myForm.myName.$error">
  370   <div ng-message="required">You did not enter a field</div>
  371   <div ng-message="maxlength">Your field is too long</div>
  372 </div>
  373 ```
  374 
  375 Becomes:
  376 
  377 ```html
  378 <div ng-messages="myForm.myName.$error" aria-live="assertive">
  379   <div ng-message="required">You did not enter a field</div>
  380   <div ng-message="maxlength">Your field is too long</div>
  381 </div>
  382 ```
  383 
  384 ## Disabling attributes
  385 The attribute magic of ngAria may not work for every scenario. To disable individual attributes,
  386 you can use the {@link ngAria.$ariaProvider#config config} method. Just keep in mind this will
  387 tell ngAria to ignore the attribute globally.
  388 
  389 <example module="ngAria_ngClickExample" deps="angular-aria.js" name="accessibility-ng-click">
  390  <file name="index.html">
  391   <div ng-click="someFunction" show-attrs>
  392     &lt;div&gt; with ng-click and bindRoleForClick, tabindex set to false
  393   </div>
  394  <script>
  395   angular.module('ngAria_ngClickExample', ['ngAria'], function config($ariaProvider) {
  396     $ariaProvider.config({
  397       bindRoleForClick: false,
  398       tabindex: false
  399     });
  400   })
  401   .directive('showAttrs', function() {
  402     return function(scope, el, attrs) {
  403       var pre = document.createElement('pre');
  404       el.after(pre);
  405       scope.$watch(function() {
  406         var attrs = {};
  407         Array.prototype.slice.call(el[0].attributes, 0).forEach(function(item) {
  408           if (item.name !== 'show-attrs') {
  409             attrs[item.name] = item.value;
  410           }
  411         });
  412         return attrs;
  413       }, function(newAttrs, oldAttrs) {
  414         pre.textContent = JSON.stringify(newAttrs, null, 2);
  415       }, true);
  416     }
  417   });
  418  </script>
  419  </file>
  420 </example>
  421 
  422 ## Common Accessibility Patterns
  423 
  424 Accessibility best practices that apply to web apps in general also apply to AngularJS.
  425 
  426  * **Text alternatives**: Add alternate text content to make visual information accessible using
  427  [these W3C guidelines](http://www.w3.org/TR/html-alt-techniques/). The appropriate technique
  428  depends on the specific markup but can be accomplished using offscreen spans, `aria-label` or
  429  label elements, image `alt` attributes, `figure`/`figcaption` elements and more.
  430  * **HTML Semantics**: If you're creating custom element directives, Web Components or HTML in
  431  general, use native elements wherever possible to utilize built-in events and properties.
  432  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).
  433  * **Focus management**: Guide the user around the app as views are appended/removed.
  434  Focus should *never* be lost, as this causes unexpected behavior and much confusion (referred to
  435  as "freak-out mode").
  436  * **Announcing changes**: When filtering or other UI messaging happens away from the user's focus,
  437  notify with [ARIA Live Regions](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions).
  438  * **Color contrast and scale**: Make sure content is legible and interactive controls are usable
  439  at all screen sizes. Consider configurable UI themes for people with color blindness, low vision
  440  or other visual impairments.
  441  * **Progressive enhancement**: Some users do not browse with JavaScript enabled or do not have
  442  the latest browser. An accessible message about site requirements can inform users and improve
  443  the experience.
  444 
  445 ## Additional Resources
  446 
  447  * [Using ARIA in HTML](http://www.w3.org/TR/aria-in-html/)
  448  * [AngularJS Accessibility at ngEurope](https://www.youtube.com/watch?v=dmYDggEgU-s&list=UUEGUP3TJJfMsEM_1y8iviSQ)
  449  * [Testing with Screen Readers](http://webaim.org/articles/screenreader_testing/)
  450  * [Chrome Accessibility Developer Tools](https://chrome.google.com/webstore/detail/accessibility-developer-t/fpkknkljclfencbdbgkenhalefipecmb?hl=en)
  451  * [W3C Accessibility Testing](http://www.w3.org/wiki/Accessibility_testing)
  452  * [WebAIM](http://webaim.org)
  453  * [A11y Project](http://a11yproject.com)