Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Accessible Javascript Patterns

Garance
July 19, 2019

Accessible Javascript Patterns

It is estimated that 20% of the population has a disability of some kind. That’s a lot of users to leave on the curb, if your web app is inaccessible! When talking about web accessibility, we often focus on using the right HTML for the right purpose, setting up the right colour palette. Did you know that the way you write JavaScript also makes or breaks the accessibility of a website ? This talk will show you how great JavaScript can improve the accessibility of your website. We’ll walk through common interaction patterns such as navigation menus, modals, or custom form elements.

Garance

July 19, 2019
Tweet

Other Decks in Technology

Transcript

  1. 4

  2. 5

  3. 6

  4. 7

  5. 8

  6. 9

  7. 10

  8. 11

  9. 12

  10. 13

  11. 13

  12. 13

  13. 14

  14. 14

  15. 14

  16. 15

  17. HTML BUTTON <button id="btn_save">Save!</button> <script> function start() { const actionButton

    = document.querySelector('#btn_save'); actionButton.addEventListener('click', activateActionButton); } function activateActionButton() { alert('Real button clicked!'); } document.addEventListener("DOMContentLoaded", start, false); !</script> 18
  18. JAVASCRIPT BUTTON <div id="div_save" tabindex="0" role="button" aria-pressed="false">Save!</div> <script> function start()

    { const actionButton = document.querySelector('#div_save'); actionButton.addEventListener('click', activateActionButton); actionButton.addEventListener('keydown', actionButtonKeydownHandler); actionButton.addEventListener('keyup', actionButtonKeyupHandler); } function actionButtonKeydownHandler(event) { if (event.keyCode !!=== 32) { event.preventDefault(); } else if (event.keyCode !!=== 13) { event.preventDefault(); activateActionButton(); } } function actionButtonKeyupHandler(event) { if (event.keyCode !!=== 32) { event.preventDefault(); activateActionButton(); } } function activateActionButton() { alert('Fake button clicked!'); } document.addEventListener("DOMContentLoaded", start, false); !</script> 19
  19. 23

  20. 23

  21. 24

  22. 25

  23. 25

  24. 25

  25. 26

  26. 26

  27. 26

  28. 27

  29. 27

  30. 27

  31. 28

  32. 28

  33. 28

  34. 31

  35. 32

  36. 33

  37. 33

  38. 35

  39. 37 handleMouseover() { this.hasHover = true; this.popupMenu.open(); } handleFocus() {

    this.menu.hasFocus = true; } this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
  40. handleKeydown(event) { let endEvent = false; switch (event.keyCode) { case

    …: endEvent = true; break; case …: !// spoilers break; } if (endEvent) { event.stopPropagation(); event.preventDefault(); } } 38
  41. case KEY_CODES.RETURN: const clickEvent = new MouseEvent('click', { 'view': window,

    'bubbles': true, 'cancelable': true }); event.currentTarget.dispatchEvent(clickEvent); endEvent = true; break; 39
  42. handleKeydown(event) { let endEvent = false; switch (event.keyCode) { case

    KEY_CODES.SPACE: case KEY_CODES.DOWN: if (this.popupMenu) { this.popupMenu.open(); this.popupMenu.setFocusToFirstItem(); endEvent = true; } break; case KEY_CODES.RETURN: !// Create simulated mouse event to mimic the behaviour of ATs !// and let the event handler handleClick do the housekeeping. const clickEvent = new MouseEvent('click', { 'view': window, 'bubbles': true, 'cancelable': true }); event.currentTarget.dispatchEvent(clickEvent); endEvent = true; break; case KEY_CODES.LEFT: this.menu.setFocusToPreviousItem(this); endEvent = true; break; case KEY_CODES.RIGHT: this.menu.setFocusToNextItem(this); endEvent = true; break; case KEY_CODES.UP: if (this.popupMenu) { this.popupMenu.open(); this.popupMenu.setFocusToLastItem(); endEvent = true; } break; 48 case KEY_CODES.HOME: case KEY_CODES.PAGEUP: this.menu.setFocusToFirstItem(); endEvent = true; break; case KEY_CODES.END: case KEY_CODES.PAGEDOWN: this.menu.setFocusToLastItem(); endEvent = true; break; case KEY_CODES.TAB: this.popupMenu.close(true); break; case KEY_CODES.ESC: this.popupMenu.close(true); break; default: if (isPrintableCharacter(char)) { this.menu.setFocusByFirstCharacter(this, char); endEvent = true; } break; } if (endEvent) { event.stopPropagation(); event.preventDefault(); } }
  43. Find the demo on garance.dev & garance.dev/basic Find the code

    on github.com/garancev/accessible-javascript-patterns 49
  44. 52

  45. 52

  46. 53

  47. 53

  48. 53

  49. 53

  50. openButton.addEventListener('click', )); moreButton.addEventListener('click', )); okButton.addEventListener('click', )); 56 event !=> this.openDialog(

    'mainDialog', event.target event !=> this.openDialog( 'moreInfoDialog', event.target, ‘more_para_1' AccessibleModal
  51. openButton.addEventListener('click', )); moreButton.addEventListener('click', )); okButton.addEventListener('click', )); 56 event !=> this.openDialog(

    'mainDialog', event.target event !=> this.openDialog( 'moreInfoDialog', event.target, ‘more_para_1' event !=> this.replaceDialog( 'confirmDialog', undefined, ‘confirm-close’ AccessibleModal
  52. 57 replaceDialog(newDialogId, newFocusAfterClosed, newFocusFirst) { const topDialog = this.focus.getCurrentDialog(); if

    (topDialog.dialogNode.contains(document.activeElement)) { topDialog.replace( newDialogId, newFocusAfterClosed, newFocusFirst ); } } AccessibleModal
  53. 58

  54. 59

  55. 61

  56. 62

  57. 63

  58. 64

  59. THANK YOU! More code examples: 
 www.w3.org/TR/wai-aria-practices/examples @GaranceVallat for links

    Birds from Audubon’s birds of America All other pictures from unsplash.com 65