开发者

preventDefault(), scrolling and accessibility

开发者 https://www.devze.com 2023-01-24 04:55 出处:网络
I\'m adding a JavaScript-powered sub-window to a site. Basically, part of the UI is positioned off the left edge of the screen until the user triggers a link; then it\'s moved into a visible position.

I'm adding a JavaScript-powered sub-window to a site. Basically, part of the UI is positioned off the left edge of the screen until the user triggers a link; then it's moved into a visible position. I have a series of five test cases, but haven't got the reputation points to link them individually yet.

For accessibility purposes, I want to use a link containing a document fragment, thus:

<a href="#quicklinks" id="quicklinks-trigger">Quick Links</a>

With corresponding JavaScript (using jQuery):

$("#quicklinks-trigger").click(function(e){ 
 $("#shadow").removeClass("default");
 $("#shadow").addClass("active");
});

The #quicklinks HREF redirects the internal cursor (aka caret) of screen readers to the beginning of the newly-revealed UI. There's a corresponding link in the Quick Links sub-window, which redirects the cursor back to the main part of the document (<a href="#main" id="close-quicklinks"></a>). You can see this in action with Test Case 1. If you listen to it with a screen reader (I'm testing with NVDA), the screen reader happily skips to the Quick Links when the link is triggered, and back to the main document when the Quick Links closing link is triggered.

It also scrolls the page up and down in response to the document fragments, which is ugly and annoying. That can be suppressed using window.preventDefault() -- see Test Case 2, which works very smoothly and doesn't scroll around the document, thus:

$("#quicklinks-trigger").click(function(e){ 
 $("#shadow").removeClass("default");
 $("#shadow").addClass("active");

e.preventDefault(); });

Unfortunately, the call to preventDefault() also prevents the browser from moving the cursor to the right spot. A blind 开发者_StackOverflowuser can trigger the link there, and then the screen reader will proceed to the next item in source-code order, not the Quick Links UI. The easiest way to fix THAT would be to put the HTML defining the Quick Links sub-window immediately after the trigger link; but I can't do that, because the CMS that this is destined for is not playing nice with large blocks of HTML inserted into menus.

I've tried some other approaches to eliminating the scrolling. Test Case 3 scrolls the window back manually:

$("#quicklinks-trigger").click(function(e){ 
 $("#shadow").removeClass("default");
 $("#shadow").addClass("active");
 window.setTimeout(function(){ scrollTo(0,0); }, 1); 
});

... which works but has a visibly jerky effect because it scrolls down and then back up, so that's no better than test case 1.

In Test Case 4, I tried using preventDefault() in combination with focus() in the hopes of moving the internal cursor manually:

$("#quicklinks-trigger").click(function(e){ 
 $("#shadow").removeClass("default");
 $("#shadow").addClass("active");
 $("#quicklinks").focus();
 e.preventDefault();
});

But it didn't work, because #quicklinks is a DIV, not a focusable element. I suppose I could add a hidden focusable element in the Quick Links HTML, but that would be kludgy.

In Test Case 5 I tried removing the ID attribute from the targeted element, rewriting the fragment identifier with the onhashchange event, and then restoring the ID:

function cfl_hash(fragment){
 // Get the section the fragment refers to.
 var target = $(fragment);

 // Save its current ID.
 var id = target.attr("id");

 // Remove the ID so the browser won't scroll.
 target.attr("id","");

 // Rewrite the hash to the desired fragment, only if onhashchange event supported.
 if("onhashchange" in window){ location.hash = fragment; }

 // Put the ID back in place.
 target.attr("id", id);
}

$("#quicklinks-trigger").click(function(e){ 
 $("#shadow").removeClass("default");
 $("#shadow").addClass("active");
 cfl_hash("#quicklinks");
});

Which had the unhappy effect of allowing the scrolling but preventing the cursor move. I think I have the sequence of events wrong in the ID swap; this ought to work for suppressing the scrolling, though I'm doubtful that it will allow the cursor move.

It's really annoying that you can't cancel the scrolling for sighted users without also canceling the cursor redirect for screen reader users.

So far I've only tested with Firefox and NVDA. I have no idea how this would play out in other combinations of browser and screen reader.

Suggestions?


I've come up with a work-around which allows the use of document fragment links, allows for the caret redirect for screen readers, and doesn't scroll the viewport. The method is

  1. Place a hidden element at the top of the element that you are linking to
  2. Link to the hidden element instead of the content that follows it
  3. Use fixed positioning to move the hidden element flush with the top of the viewport

In this way, when you click the link targeting the hidden element, the browser tries to "scroll" the screen into place, but it's already at the top of the viewport, so no actual scrolling takes place. The caret redirect occurs, so screen reader users get to where the link was pointing.

There are a couple of quirks. In Opera, Safari, and Chrome, clicking a link arranged in this way will cause a scroll, but ONLY if the user has already scrolled down. I'm not sure why this is so; perhaps they are not updating the positions of fixed-position elements which are off the left of the screen? In any case, this issue affects only a highly specific set of circumstances which can be mostly avoided through sensible page layout. So I think the benefits (accessible, comparatively simple code) outweigh the disadvantages (minor visual quirk in some browsers and circumstances).

For a more complete discussion of this technique, see:

http://www.accessifyforum.com/viewtopic.php?p=77132

Hope this helps someone else.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号