2

I've created an overlay that loads content dynamically after it opens, but am having issues with VoiceOver in Safari when trying to add ARIA attributes.

After only adding role='dialog' to the overlay container, it is announced as a dialog, but reads the text contents first ("close loading... dialog close button").

When adding a label to the dialog with either aria-label and aria-labelledby the real problem occurs. The overlay is announced nicely ("Overlay dialog close button"), but then the rest of the dialog's contents are inaccessible after being loaded, and it just appears that the close button is the last (and only) item available.

HTML:

<div id="page">
  <a id="opendialog" href="#" role="button">Open</a>
</div>

JavaScript:

jQuery(function ($) {
  $('#opendialog').click(function (event) {
    event.preventDefault();

    // Attach the dialog container to the page with a loading placeholder.
    $dialog = $('<div id="dialog" role="dialog" aria-labelledby="dialog-label">' +
                  '<span id="dialog-label">Overlay</span>' +
                  '<a href="#" class="close" role="button">close</a>' +
                  '<div class="content"><span class="loading">Loading...</span></div>' +
                '</div>')
      .insertAfter('#page');
    $dialog.find('.close').focus();

    // Hide the other page contents to trap the user in the dialog.
    $('#page').hide();

    $dialog.find('.close').click(function (event) {
      event.preventDefault();

      $dialog.remove();
      $('#page').show();
      $('#opendialog').focus();
    });

    // Simulate an asynchronous request for the dialog contents.
    setTimeout(function () {
      $dialog.find('.content .loading')
        .replaceWith('<div>Dialog Content</div>');
    }, 250);

  });
});

http://codepen.io/gapple/pen/FGhzl

Chrome also seems to have some weird issues with the codepen when in an iframe, but loading the iframe url directly seems to work correctly.

gapple
  • 3,463
  • 22
  • 27
  • 1
    My gut says to add `aria-live="polite"` or something to the `
    ` that's being replaced. TL;DR: the assistive tech doesn't know that `.loading` was updated.
    – Ryan B Jul 02 '14 at 14:00
  • [This question](http://stackoverflow.com/questions/21147384/voiceover-navigation-doesnt-work-on-tab-panels-if-wrapper-has-aria-label) appears to have a similar issue with role="tabpanel", but no solution – gapple Jul 02 '14 at 17:47
  • An once over of the question you linked to is a different issue. The other question is saying when a user clicks on a tab, the focus isn't shifted correctly. A once over of `replacewith()`leads me back to my TL;DR. I found this question on [reading new content](http://stackoverflow.com/q/3670570/1276124), which echos my first comment. – Ryan B Jul 02 '14 at 18:55
  • The other question doesn't deal with programmatically changing focus at all. The similarity is that VoiceOver doesn't recognize dynamic content updates once `aria-label` is used (via Shift + VO + left/right), making content completely inaccessible unless the user stops interacting with the HTML content (Shift + VO + up), then goes back to it. – gapple Jul 03 '14 at 19:14
  • I found http://blog.paciellogroup.com/2014/03/screen-reader-support-aria-live-regions/ - and _"VoiceOver in Safari 7.0.2 (OSX) fully supports the ARIA live region, but in Safari 7.0 (iOS) the test case only worked when the live region was assertive"_, so that might be it. You'll probably ask how can you fix it then, and off hand I don't know. – Ryan B Jul 03 '14 at 19:23

1 Answers1

1

VoiceOver is quite persnickety when it comes to dynamic content and focus. This (if run on a page of any size) will not work at all on iOS because the .focus() on dynamic content does not work unless executed in a timeout of at least 500 ms (look here http://unobfuscated.blogspot.com/2013/08/messed-up-ios-focus-management-with.html).

However, for OS X, you can fix your problem by focussing on the dialog itself rather than the close button. Here is the snippet of code modified so it works. Once the focus is on the dialog, hit CTRL-OPTION DOWN to interact with the dialog content

$dialog = $('<div id="dialog" role="dialog" aria-labelledby="dialog-label" tabindex="-1">' +
              '<span id="dialog-label">Overlay</span>' +
              '<a href="#" class="close" role="button">close</a>' +
              '<div class="content"><span class="loading">Loading...</span></div>' +
            '</div>')
  .insertAfter('#page')
  .focus();
unobf
  • 7,158
  • 1
  • 23
  • 36