Bug 22807: Add 'Skip to content' option
authorMartin Renvoize <martin.renvoize@ptfs-europe.com>
Tue, 9 Jun 2020 13:10:53 +0000 (14:10 +0100)
committerJonathan Druart <jonathan.druart@bugs.koha-community.org>
Wed, 24 Jun 2020 13:13:55 +0000 (15:13 +0200)
This patch adds a 'Skip to content' link to the header bar which will
only appear upon the first use of 'tab' to navigate after any fresh page
load in the OPAC.

Test plan
1/ Load any page in the OPAC
2/ Hit the `tab` key
3/ Note the new 'Skip to main content' link appears at the top left of
the screen.
4/ Hit `Enter` or Click the button
5/ Note the page scrolls to the area of the page that has the first block
   containing a .maincontent class.
6/ Note that the next available focusable element after the first .maincontent
   block has been given focus.
7/ Note that the link has been hidden

Signed-off-by: Hayley Mapley <hayleymapley@catalyst.net.nz>

Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>

koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc
koha-tmpl/opac-tmpl/bootstrap/js/global.js

index 88dd9a9..8de7ec6 100644 (file)
@@ -12,6 +12,7 @@
         <div class="navbar navbar-inverse navbar-static-top">
             <div class="navbar-inner">
                 <div class="container-fluid">
+                    <button id="scrolltocontent" class="sr-only sr-only-focusable pull-left nav btn btn-danger" type="button">Skip to main content</button>
                     <h1 id="logo">
                         <a class="brand" href="/cgi-bin/koha/opac-main.pl">
                             [% IF ( LibraryNameTitle ) %]
index 166c361..a5ad071 100644 (file)
@@ -113,3 +113,49 @@ function confirmModal(message, title, yes_label, no_label, callback) {
     $("#bootstrap-confirm-box-modal-cancel").text( no_label || 'Cancel' );
     $("#bootstrap-confirm-box-modal").modal('show');
 }
+
+//Add jQuery :focusable selector
+(function($) {
+    function visible(element) {
+        return $.expr.filters.visible(element) && !$(element).parents().addBack().filter(function() {
+            return $.css(this, 'visibility') === 'hidden';
+        }).length;
+    }
+
+    function focusable(element, isTabIndexNotNaN) {
+        var map, mapName, img, nodeName = element.nodeName.toLowerCase();
+        if ('area' === nodeName) {
+            map = element.parentNode;
+            mapName = map.name;
+            if (!element.href || !mapName || map.nodeName.toLowerCase() !== 'map') {
+                return false;
+            }
+            img = $('img[usemap=#' + mapName + ']')[0];
+            return !!img && visible(img);
+        }
+        return (/input|select|textarea|button|object/.test(nodeName) ?
+                !element.disabled :
+                'a' === nodeName ?
+                element.href || isTabIndexNotNaN :
+                isTabIndexNotNaN) &&
+            // the element and all of its ancestors must be visible
+            visible(element);
+    }
+
+    $.extend($.expr[':'], {
+        focusable: function(element) {
+            return focusable(element, !isNaN($.attr(element, 'tabindex')));
+        }
+    });
+})(jQuery);
+
+$("#scrolltocontent").click(function() {
+    var content = $(".maincontent");
+    if (content.length > 0) {
+        $('html,body').animate({
+                scrollTop: content.first().offset().top
+            },
+        'slow');
+        content.first().find(':focusable').eq(0).focus();
+    }
+});