From 1b8af17e7ca2881463bf06b1ec06f83c69e2da10 Mon Sep 17 00:00:00 2001 From: Stephan Fischer Date: Thu, 5 Apr 2018 17:52:14 +0200 Subject: [PATCH] feat(navigation): remove focus if button is not available --- README.md | 30 ++++++++++++++++++++++++-- dist/js/owl.carousel.aria.js | 31 ++++++++++++++++++++------- dist/js/owl.carousel.aria.min.js | 2 +- package.json | 2 +- src/js/owl.carousel.aria.js | 36 +++++++++++++++++++++++--------- 5 files changed, 79 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index d7ed5e4..a34b043 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,36 @@ -# owl-aria +# Owl-Aria An Owl Carousel v2 accessibility layer +## Authorship +Written by [Stephan Fischer](mailto:stephan@mrfischer.de) ## License ### Commercial license -If you want to use owl-arai to develop commercial sites, themes, projects, and applications, the Commercial license is the appropriate license. +If you want to use owl-aria to develop commercial sites, themes, projects, and applications, the Commercial license is the appropriate license. + +## Requirements + +* jQuery +* Owl Carousel v2 + +## Installation + +In the `` of your page, after you set up your jQuery, Owl Carousel and jquery-throttle-debounce ` +``` + +## Features + +* Adds WAI-ARIA visibility and role hinting attributes +* Adds keyboard navigation (arrow keys for previous/next, enter keys on controls) +* Controls the focus of each element within the carousel, for a correct tabindex sequence +* Works with nested carousels + +## Usage + +Once you've installed the accessibility layer plugin, it gets used automatically when you instantiate Owl Carousel. diff --git a/dist/js/owl.carousel.aria.js b/dist/js/owl.carousel.aria.js index 146edce..7acf1e4 100644 --- a/dist/js/owl.carousel.aria.js +++ b/dist/js/owl.carousel.aria.js @@ -49,7 +49,9 @@ this.$nav = $('.' + this.options.navContainerClass + ', .' + this.options.dotsClass, this.$element); var noButtonSel = ":not(button):not(input[type='submit'])"; - this.$nav.children().attr("role", "button").attr("tabindex", "0").storeTabindex().filter(noButtonSel).each(function (i, e) { + this.setNavAria(); + + this.$nav.children().attr("role", "button").storeTabindex().filter(noButtonSel).each(function (i, e) { var $el = $(e); $el.on('keydown', function (e) { @@ -108,6 +110,24 @@ } }; + Aria.prototype.setNavAria = function () { + this.$nav.children().each(function (i, el) { + var $el = $(el); + var isDisabled = $el.hasClass('disabled') || $el.hasClass('active'); + + $el.attr('aria-disabled', isDisabled ? "true" : "false"); + + if (isDisabled) { + $el.attr("tabindex", "-1"); + $el.attr("data-tabindex", $el.attr('tabindex')); + } else { + + $el.attr("tabindex", "0"); + $el.attr("data-tabindex", $el.attr('tabindex')); + } + }); + }; + Aria.prototype.setAria = function () { var _this3 = this; @@ -115,14 +135,9 @@ return false; } - setTimeout(function () { - _this3.$nav.children().each(function (i, el) { - var $item = $(el); - var isDisabled = $item.hasClass('disabled'); - var isActive = $item.hasClass('active'); + this.setNavAria(); - $item.attr('aria-disabled', isDisabled || isActive ? "true" : "false"); - }); + setTimeout(function () { _this3.$stage.children().each(function (i, el) { var $item = $(el); diff --git a/dist/js/owl.carousel.aria.min.js b/dist/js/owl.carousel.aria.min.js index cfe147e..7e44e27 100644 --- a/dist/js/owl.carousel.aria.min.js +++ b/dist/js/owl.carousel.aria.min.js @@ -1 +1 @@ -"use strict";!function(t,e,n,i){var o=function e(n){var i=this;if(this._core=n,this.options=t.extend({},e.Defaults,this._core.options),!this.options.aria)return!1;this.setup(),this.$element.on({"initialized.owl.carousel":function(t){t.namespace&&!i._init&&(i.$stage=i._core.$stage,i.navigation(),i.bind(),i.setAria(),i._init=!0)},"changed.owl.carousel":function(t){return i.setAria()}})};o.Defaults={aria:!0},o.prototype.setup=function(){this._init=!1,this.$element=this._core.$element,this.$element.attr("tabindex","0").storeTabindex(),this.$element.find("*").storeTabindex()},o.prototype.navigation=function(){this.$nav=t("."+this.options.navContainerClass+", ."+this.options.dotsClass,this.$element);this.$nav.children().attr("role","button").attr("tabindex","0").storeTabindex().filter(":not(button):not(input[type='submit'])").each(function(e,n){var i=t(n);i.on("keydown",function(t){if(32===t.keyCode||13===t.keyCode)return i.trigger("click"),!1})})},o.prototype.bind=function(){var t=this;this.$element.on("to.owl.carousel",function(t){return t.stopPropagation()}),this.$element.on("next.owl.carousel",function(t){return t.stopPropagation()}),this.$element.on("prev.owl.carousel",function(t){return t.stopPropagation()}),this.$element.on("destroy.owl.carousel",function(t){return t.stopPropagation()}),this.$element.on("focusin",function(e){return t.focus(e)}).on("focusout",function(e){return t.blur(e)}).on("keydown",function(e){return t.keydown(e)})},o.prototype.focus=function(){this.$element.attr({"aria-live":"polite"})},o.prototype.blur=function(){this.$element.attr({"aria-live":"off"})},o.prototype.keydown=function(t){var e=null;if(37==t.keyCode||38==t.keyCode?e="prev.owl.carousel":39!=t.keyCode&&40!=t.keyCode||(e="next.owl.carousel"),null!==e)return this.$element.trigger(e),!1},o.prototype.setAria=function(){var e=this;if(!this.$stage||!this.$stage.length)return!1;setTimeout(function(){e.$nav.children().each(function(e,n){var i=t(n),o=i.hasClass("disabled"),r=i.hasClass("active");i.attr("aria-disabled",o||r?"true":"false")}),e.$stage.children().each(function(e,n){var i=t(n),o=i.hasClass("active");i.attr("aria-hidden",o?"false":"true"),i.find("*").each(function(e,n){var i=t(n);!1===o?(i.storeTabindex(),i.attr("tabindex","-1")):i.restoreTabindex()})})})},o.prototype.destroy=function(){var t=this;this.$element.removeAttr("aria-live"),this.$element.removeAttr("tabindex"),this.$element.children().removeAttr("aria-hidden"),this.$element.find("[data-tabindex]").restoreTabindex(),this.$element.off("focusin",function(e){return t.focus(e)}).off("focusout",function(e){return t.blur(e)}).off("keydown",function(e){return t.keydown(e)})},t.fn.extend({restoreTabindex:function(){return this.each(function(){var e=t(this);e.is("[data-tabindex]")?e.attr("tabindex",e.attr("data-tabindex")):e.removeAttr("tabindex")})},storeTabindex:function(){return this.each(function(){var e=t(this);e.is("[tabindex]")&&!e.is("[data-tabindex]")&&e.attr("data-tabindex",e.attr("tabindex"))})}}),t.fn.owlCarousel.Constructor.Plugins.Aria=o}(window.Zepto||window.jQuery,window,document); \ No newline at end of file +"use strict";!function(t,e,n,i){var o=function e(n){var i=this;if(this._core=n,this.options=t.extend({},e.Defaults,this._core.options),!this.options.aria)return!1;this.setup(),this.$element.on({"initialized.owl.carousel":function(t){t.namespace&&!i._init&&(i.$stage=i._core.$stage,i.navigation(),i.bind(),i.setAria(),i._init=!0)},"changed.owl.carousel":function(t){return i.setAria()}})};o.Defaults={aria:!0},o.prototype.setup=function(){this._init=!1,this.$element=this._core.$element,this.$element.attr("tabindex","0").storeTabindex(),this.$element.find("*").storeTabindex()},o.prototype.navigation=function(){this.$nav=t("."+this.options.navContainerClass+", ."+this.options.dotsClass,this.$element);this.setNavAria(),this.$nav.children().attr("role","button").storeTabindex().filter(":not(button):not(input[type='submit'])").each(function(e,n){var i=t(n);i.on("keydown",function(t){if(32===t.keyCode||13===t.keyCode)return i.trigger("click"),!1})})},o.prototype.bind=function(){var t=this;this.$element.on("to.owl.carousel",function(t){return t.stopPropagation()}),this.$element.on("next.owl.carousel",function(t){return t.stopPropagation()}),this.$element.on("prev.owl.carousel",function(t){return t.stopPropagation()}),this.$element.on("destroy.owl.carousel",function(t){return t.stopPropagation()}),this.$element.on("focusin",function(e){return t.focus(e)}).on("focusout",function(e){return t.blur(e)}).on("keydown",function(e){return t.keydown(e)})},o.prototype.focus=function(){this.$element.attr({"aria-live":"polite"})},o.prototype.blur=function(){this.$element.attr({"aria-live":"off"})},o.prototype.keydown=function(t){var e=null;if(37==t.keyCode||38==t.keyCode?e="prev.owl.carousel":39!=t.keyCode&&40!=t.keyCode||(e="next.owl.carousel"),null!==e)return this.$element.trigger(e),!1},o.prototype.setNavAria=function(){this.$nav.children().each(function(e,n){var i=t(n),o=i.hasClass("disabled")||i.hasClass("active");i.attr("aria-disabled",o?"true":"false"),o?(i.attr("tabindex","-1"),i.attr("data-tabindex",i.attr("tabindex"))):(i.attr("tabindex","0"),i.attr("data-tabindex",i.attr("tabindex")))})},o.prototype.setAria=function(){var e=this;if(!this.$stage||!this.$stage.length)return!1;this.setNavAria(),setTimeout(function(){e.$stage.children().each(function(e,n){var i=t(n),o=i.hasClass("active");i.attr("aria-hidden",o?"false":"true"),i.find("*").each(function(e,n){var i=t(n);!1===o?(i.storeTabindex(),i.attr("tabindex","-1")):i.restoreTabindex()})})})},o.prototype.destroy=function(){var t=this;this.$element.removeAttr("aria-live"),this.$element.removeAttr("tabindex"),this.$element.children().removeAttr("aria-hidden"),this.$element.find("[data-tabindex]").restoreTabindex(),this.$element.off("focusin",function(e){return t.focus(e)}).off("focusout",function(e){return t.blur(e)}).off("keydown",function(e){return t.keydown(e)})},t.fn.extend({restoreTabindex:function(){return this.each(function(){var e=t(this);e.is("[data-tabindex]")?e.attr("tabindex",e.attr("data-tabindex")):e.removeAttr("tabindex")})},storeTabindex:function(){return this.each(function(){var e=t(this);e.is("[tabindex]")&&!e.is("[data-tabindex]")&&e.attr("data-tabindex",e.attr("tabindex"))})}}),t.fn.owlCarousel.Constructor.Plugins.Aria=o}(window.Zepto||window.jQuery,window,document); \ No newline at end of file diff --git a/package.json b/package.json index 8853d30..46b6557 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "owl-aria", - "version": "1.0.6", + "version": "1.0.7", "description": "Owl Carousel v2 accessibility layer", "homepage": "https://github.com/stephan-fischer/owl-aria", "keywords": [ diff --git a/src/js/owl.carousel.aria.js b/src/js/owl.carousel.aria.js index e1c2bcb..6473e7b 100644 --- a/src/js/owl.carousel.aria.js +++ b/src/js/owl.carousel.aria.js @@ -21,7 +21,7 @@ { 'initialized.owl.carousel': e => { - if (e.namespace && !this._init) { + if (e.namespace && !this._init) { this.$stage = this._core.$stage; this.navigation(); this.bind(); @@ -53,9 +53,10 @@ this.options.dotsClass, this.$element); const noButtonSel = ":not(button):not(input[type='submit'])"; + this.setNavAria(); + this.$nav.children() .attr("role", "button") - .attr("tabindex", "0") .storeTabindex().filter(noButtonSel).each((i, e) => { const $el = $(e); @@ -109,23 +110,38 @@ } }; + Aria.prototype.setNavAria = function() + { + this.$nav.children().each((i, el) => + { + const $el = $(el); + const isDisabled = $el.hasClass('disabled') || $el.hasClass('active'); + + $el.attr('aria-disabled', isDisabled ? "true": "false"); + + if (isDisabled) { + $el.attr("tabindex", "-1"); + $el.attr("data-tabindex", $el.attr('tabindex')); + } else { + + $el.attr("tabindex", "0"); + $el.attr("data-tabindex", $el.attr('tabindex')); + } + + }); + }; + Aria.prototype.setAria = function() { if (!this.$stage || !this.$stage.length) { return false; } + this.setNavAria(); + setTimeout(() => { - this.$nav.children().each((i, el) => - { - const $item = $(el); - const isDisabled = $item.hasClass('disabled'); - const isActive = $item.hasClass('active'); - $item.attr('aria-disabled', isDisabled || isActive ? "true": "false"); - - }); this.$stage.children().each((i, el) => {