9

I'm building an accessible website and trying to manage focus. I need to open a modal and then put focus on the first element in the modal then trap the focus until the modal is closed ("canceled" or "accepted").

HTML

<a href="" ng-click="modalshow = !modalshow; modal.open();">Open Modal</a>
  <div ng-show="modalshow" id="modal">
    <h3 id="tofs" >Terms of Service</h3>
    <p>Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum </p>
    <span>Cancel</span> 
    <span>Accept/span> 
  </div>
  <h2>Seprate Content</h2>

Javascript

angular.module('app')
    .controller('Controller', modalCtrl);

  function modalCtrl() {
     $scope.modal = {
       open: function() {
         angular.element('#tofs').focus();

       }
     }
  }
im_benton
  • 2,228
  • 3
  • 18
  • 30

5 Answers5

2
angular.element("html").on("focusin", "body", function (event) {
   if(angular.element("#modal:visible").length>0){
       event.preventDefault();
       event.stopPropagation();
       angular.element('#tofs').focus();
   }
});

you can add this code to trap all focus events to h3 tag when modal is visible.

1

There's a directive called ngBlur, that fires an event when an element loses focus. Try executing your focus function on ng-blur

Here: https://docs.angularjs.org/api/ng/directive/ngBlur

An example: http://jsbin.com/hemaye/1/edit?html,js,output

You cannot ever select the 2nd input box because of the ng-blur statement

siddhant
  • 639
  • 7
  • 22
  • ng-blur would work if the event included the direction of the blur. For example going down the page flow instead of back up within the modal/dialog box. Because there could be multiple elements within the modal/dialog box, you cannot just send the focus to the beginning based on blur of an element. – im_benton Jul 06 '15 at 07:21
1

There is multiple possibility to trap the focus.

One solution is to manually set tabindex="-1" temporarily on all the elements in the background of your modal when it is showed (and remove this tabindex, or revert to original tabindex when leaving modal).

Another solution is to look at how angular-bootstrap plan to fix this issue : https://github.com/angular-ui/bootstrap/issues/738

You can also look at the WAI ARIA page, they have a related content about it : http://www.w3.org/TR/wai-aria-practices/#trap_focus_div

Jscti
  • 12,924
  • 4
  • 57
  • 83
1

Okay after way too much searching, this is what I came up with. Basically I created ng-focus functions on the last element in the modal and the next element following the modal. This way I could check if the focus existed within the modal and if not, I would loop the focus back to the top.

      <a href="" ng-click="modalshow = !modalshow; modal.open();">Open Modal</a>
      <div ng-show="modalshow" id="modal">
        <h3 id="tofs" >Terms of Service</h3>
        <p>Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum </p>
        <a href="" ng-focus="modalshow.onFocus($event)">Cancel</a> 
        <a href="" ng-focus="modalshow.onFocus($event)">Accept</a> 
      </div>
      <h2>Seprate Content</h2>
      <a href="" ng-focus="modalshow.onFocus($event)">next link<a>

Javascript

angular.module('app')
    .controller('Controller', modalCtrl);

  function modalCtrl() {
     $scope.modal = {
       open: function() {
         angular.element('#tofs').focus();

       },
       onFocus: function(){
         var modal = angular.element('#modal')[0];

         if (!modal.contains( event.target ) && $scope.modalIsOpen) {
           event.stopPropagation();
           angular.element('#tofs').focus();
         }
       }
     }
  }
im_benton
  • 2,228
  • 3
  • 18
  • 30
0

Don't use any solution requiring you to look up "tabbable" elements. Instead, use keydown and either click events or a backdrop in an effective manor.

What you need to do is have a general window.keydown event with preventDefault() to stop all tabbing when the modal is displayed and a keydown.tab event with stopPropagation() to handle tabbing for the modal.

You can use click events in a similar manor or you can just use a backdrop to prevent clicking elements behind it.

For Angular1, I'd imagine this would look similar to Asheesh Kumar's answer. You can see my Angular2-x solution here: https://stackoverflow.com/a/46738489/1754995

Kody
  • 700
  • 5
  • 18