2016-06-07 14 views
0

Ich war auf der Suche nach dem mehrstufigen vertikalen Menü mit einer Option, übergeordnete Links für eine sehr lange Zeit zu verwenden. Schließlich habe ich navgoco jQuery gefunden, das ich in meinem Projekt verwende. Ich liebe dieses Plugin absolut. Das einzige Problem, das ich habe, ist, dass es nicht über die Tastatur zugänglich ist. Gibt es eine Möglichkeit, das zu beheben?Wie man Navigationstastatur zugänglich macht?

https://jsfiddle.net/webIra7/xs4mr7at/7/

(function($) { 
 

 
\t "use strict"; 
 

 
\t /** 
 
\t * Plugin Constructor. Every menu must have a unique id which will either 
 
\t * be the actual id attribute or its index in the page. 
 
\t * 
 
\t * @param {Element} el 
 
\t * @param {Object} options 
 
\t * @param {Integer} idx 
 
\t * @returns {Object} Plugin Instance 
 
\t */ 
 
\t var Plugin = function(el, options, idx) { 
 
\t \t this.el = el; 
 
\t \t this.$el = $(el); 
 
\t \t this.options = options; 
 
\t \t this.uuid = this.$el.attr('id') ? this.$el.attr('id') : idx; 
 
\t \t this.state = {}; 
 
\t \t this.init(); 
 
\t \t return this; 
 
\t }; 
 

 
\t /** 
 
\t * Plugin methods 
 
\t */ 
 
\t Plugin.prototype = { 
 
\t \t /** 
 
\t \t * Load cookie, assign a unique data-index attribute to 
 
\t \t * all sub-menus and show|hide them according to cookie 
 
\t \t * or based on the parent open class. Find all parent li > a 
 
\t \t * links add the carent if it's on and attach the event click 
 
\t \t * to them. 
 
\t \t */ 
 
\t \t init: function() { 
 
\t \t \t var self = this; 
 
\t \t \t self._load(); 
 
\t \t \t self.$el.find('ul').each(function(idx) { 
 
\t \t \t \t var sub = $(this); 
 
\t \t \t \t sub.attr('data-index', idx); 
 
\t \t \t \t if (self.options.save && self.state.hasOwnProperty(idx)) { 
 
\t \t \t \t \t sub.parent().addClass(self.options.openClass); 
 
\t \t \t \t \t sub.show(); 
 
\t \t \t \t } else if (sub.parent().hasClass(self.options.openClass)) { 
 
\t \t \t \t \t sub.show(); 
 
\t \t \t \t \t self.state[idx] = 1; 
 
\t \t \t \t } else { 
 
\t \t \t \t \t sub.hide(); 
 
\t \t \t \t } 
 
\t \t \t }); 
 

 
\t \t \t var caret = $('<span></span>').prepend(self.options.caretHtml); 
 
\t \t \t var links = self.$el.find("li > a"); 
 
\t \t \t self._trigger(caret, false); 
 
\t \t \t self._trigger(links, true); 
 
\t \t \t self.$el.find("li:has(ul) > a").prepend(caret); 
 
\t \t }, 
 
\t \t /** 
 
\t \t * Add the main event trigger to toggle menu items to the given sources 
 
\t \t * @param {Element} sources 
 
\t \t * @param {Boolean} isLink 
 
\t \t */ 
 
\t \t _trigger: function(sources, isLink) { 
 
\t \t \t var self = this; 
 
\t \t \t sources.on('click', function(event) { 
 
\t \t \t \t event.stopPropagation(); 
 
\t \t \t \t var sub = isLink ? $(this).next() : $(this).parent().next(); 
 
\t \t \t \t var isAnchor = false; 
 
\t \t \t \t if (isLink) { 
 
\t \t \t \t \t var href = $(this).attr('href'); 
 
\t \t \t \t \t isAnchor = href === undefined || href === '' || href === '#'; 
 
\t \t \t \t } 
 
\t \t \t \t sub = sub.length > 0 ? sub : false; 
 
\t \t \t \t self.options.onClickBefore.call(this, event, sub); 
 

 
\t \t \t \t if (!isLink || sub && isAnchor) { 
 
\t \t \t \t \t event.preventDefault(); 
 
\t \t \t \t \t self._toggle(sub, sub.is(":hidden")); 
 
\t \t \t \t \t self._save(); 
 
\t \t \t \t } else if (self.options.accordion) { 
 
\t \t \t \t \t var allowed = self.state = self._parents($(this)); 
 
\t \t \t \t \t self.$el.find('ul').filter(':visible').each(function() { 
 
\t \t \t \t \t \t var sub = $(this), 
 
\t \t \t \t \t \t \t idx = sub.attr('data-index'); 
 

 
\t \t \t \t \t \t if (!allowed.hasOwnProperty(idx)) { 
 
\t \t \t \t \t \t \t self._toggle(sub, false); 
 
\t \t \t \t \t \t } 
 
\t \t \t \t \t }); 
 
\t \t \t \t \t self._save(); 
 
\t \t \t \t } 
 
\t \t \t \t self.options.onClickAfter.call(this, event, sub); 
 
\t \t \t }); 
 
\t \t }, 
 
\t \t /** 
 
\t \t * Accepts a JQuery Element and a boolean flag. If flag is false it removes the `open` css 
 
\t \t * class from the parent li and slides up the sub-menu. If flag is open it adds the `open` 
 
\t \t * css class to the parent li and slides down the menu. If accordion mode is on all 
 
\t \t * sub-menus except the direct parent tree will close. Internally an object with the menus 
 
\t \t * states is maintained for later save duty. 
 
\t \t * 
 
\t \t * @param {Element} sub 
 
\t \t * @param {Boolean} open 
 
\t \t */ 
 
\t \t _toggle: function(sub, open) { 
 
\t \t \t var self = this, 
 
\t \t \t \t idx = sub.attr('data-index'), 
 
\t \t \t \t parent = sub.parent(); 
 

 
\t \t \t self.options.onToggleBefore.call(this, sub, open); 
 
\t \t \t if (open) { 
 
\t \t \t \t parent.addClass(self.options.openClass); 
 
\t \t \t \t sub.slideDown(self.options.slide); 
 
\t \t \t \t self.state[idx] = 1; 
 

 
\t \t \t \t if (self.options.accordion) { 
 
\t \t \t \t \t var allowed = self.state = self._parents(sub); 
 
\t \t \t \t \t allowed[idx] = self.state[idx] = 1; 
 

 
\t \t \t \t \t self.$el.find('ul').filter(':visible').each(function() { 
 
\t \t \t \t \t \t var sub = $(this), 
 
\t \t \t \t \t \t \t idx = sub.attr('data-index'); 
 

 
\t \t \t \t \t \t if (!allowed.hasOwnProperty(idx)) { 
 
\t \t \t \t \t \t \t self._toggle(sub, false); 
 
\t \t \t \t \t \t } 
 
\t \t \t \t \t }); 
 
\t \t \t \t } 
 
\t \t \t } else { 
 
\t \t \t \t parent.removeClass(self.options.openClass); 
 
\t \t \t \t sub.slideUp(self.options.slide); 
 
\t \t \t \t self.state[idx] = 0; 
 
\t \t \t } 
 
\t \t \t self.options.onToggleAfter.call(this, sub, open); 
 
\t \t }, 
 
\t \t /** 
 
\t \t * Returns all parents of a sub-menu. When obj is true It returns an object with indexes for 
 
\t \t * keys and the elements as values, if obj is false the object is filled with the value `1`. 
 
\t \t * 
 
\t \t * @since v0.1.2 
 
\t \t * @param {Element} sub 
 
\t \t * @param {Boolean} obj 
 
\t \t * @returns {Object} 
 
\t \t */ 
 
\t \t _parents: function(sub, obj) { 
 
\t \t \t var result = {}, 
 
\t \t \t \t parent = sub.parent(), 
 
\t \t \t \t parents = parent.parents('ul'); 
 

 
\t \t \t parents.each(function() { 
 
\t \t \t \t var par = $(this), 
 
\t \t \t \t \t idx = par.attr('data-index'); 
 

 
\t \t \t \t if (!idx) { 
 
\t \t \t \t \t return false; 
 
\t \t \t \t } 
 
\t \t \t \t result[idx] = obj ? par : 1; 
 
\t \t \t }); 
 
\t \t \t return result; 
 
\t \t }, 
 
\t \t /** 
 
\t \t * If `save` option is on the internal object that keeps track of the sub-menus states is 
 
\t \t * saved with a cookie. For size reasons only the open sub-menus indexes are stored. \t \t * 
 
\t \t */ 
 
\t \t _save: function() { 
 
\t \t \t if (this.options.save) { 
 
\t \t \t \t var save = {}; 
 
\t \t \t \t for (var key in this.state) { 
 
\t \t \t \t \t if (this.state[key] === 1) { 
 
\t \t \t \t \t \t save[key] = 1; 
 
\t \t \t \t \t } 
 
\t \t \t \t } 
 
\t \t \t \t cookie[this.uuid] = this.state = save; 
 
\t \t \t \t $.cookie(this.options.cookie.name, JSON.stringify(cookie), this.options.cookie); 
 
\t \t \t } 
 
\t \t }, 
 
\t \t /** 
 
\t \t * If `save` option is on it reads the cookie data. The cookie contains data for all 
 
\t \t * navgoco menus so the read happens only once and stored in the global `cookie` var. 
 
\t \t */ 
 
\t \t _load: function() { 
 
\t \t \t if (this.options.save) { 
 
\t \t \t \t if (cookie === null) { 
 
\t \t \t \t \t var data = $.cookie(this.options.cookie.name); 
 
\t \t \t \t \t cookie = (data) ? JSON.parse(data) : {}; 
 
\t \t \t \t } 
 
\t \t \t \t this.state = cookie.hasOwnProperty(this.uuid) ? cookie[this.uuid] : {}; 
 
\t \t \t } 
 
\t \t }, 
 
\t \t /** 
 
\t \t * Public method toggle to manually show|hide sub-menus. If no indexes are provided all 
 
\t \t * items will be toggled. You can pass sub-menus indexes as regular params. eg: 
 
\t \t * navgoco('toggle', true, 1, 2, 3, 4, 5); 
 
\t \t * 
 
\t \t * Since v0.1.2 it will also open parents when providing sub-menu indexes. 
 
\t \t * 
 
\t \t * @param {Boolean} open 
 
\t \t */ 
 
\t \t toggle: function(open) { 
 
\t \t \t var self = this, 
 
\t \t \t \t length = arguments.length; 
 

 
\t \t \t if (length <= 1) { 
 
\t \t \t \t self.$el.find('ul').each(function() { 
 
\t \t \t \t \t var sub = $(this); 
 
\t \t \t \t \t self._toggle(sub, open); 
 
\t \t \t \t }); 
 
\t \t \t } else { 
 
\t \t \t \t var idx, 
 
\t \t \t \t \t list = {}, 
 
\t \t \t \t \t args = Array.prototype.slice.call(arguments, 1); 
 
\t \t \t \t length--; 
 

 
\t \t \t \t for (var i = 0; i < length; i++) { 
 
\t \t \t \t \t idx = args[i]; 
 
\t \t \t \t \t var sub = self.$el.find('ul[data-index="' + idx + '"]').first(); 
 
\t \t \t \t \t if (sub) { 
 
\t \t \t \t \t \t list[idx] = sub; 
 
\t \t \t \t \t \t if (open) { 
 
\t \t \t \t \t \t \t var parents = self._parents(sub, true); 
 
\t \t \t \t \t \t \t for (var pIdx in parents) { 
 
\t \t \t \t \t \t \t \t if (!list.hasOwnProperty(pIdx)) { 
 
\t \t \t \t \t \t \t \t \t list[pIdx] = parents[pIdx]; 
 
\t \t \t \t \t \t \t \t } 
 
\t \t \t \t \t \t \t } 
 
\t \t \t \t \t \t } 
 
\t \t \t \t \t } 
 
\t \t \t \t } 
 

 
\t \t \t \t for (idx in list) { 
 
\t \t \t \t \t self._toggle(list[idx], open); 
 
\t \t \t \t } 
 
\t \t \t } 
 
\t \t \t self._save(); 
 
\t \t }, 
 
\t \t /** 
 
\t \t * Removes instance from JQuery data cache and unbinds events. 
 
\t \t */ 
 
\t \t destroy: function() { 
 
\t \t \t $.removeData(this.$el); 
 
\t \t \t this.$el.find("li:has(ul) > a").unbind('click'); 
 
\t \t \t this.$el.find("li:has(ul) > a > span").unbind('click'); 
 
\t \t } 
 
\t }; 
 

 
\t /** 
 
\t * A JQuery plugin wrapper for navgoco. It prevents from multiple instances and also handles 
 
\t * public methods calls. If we attempt to call a public method on an element that doesn't have 
 
\t * a navgoco instance, one will be created for it with the default options. 
 
\t * 
 
\t * @param {Object|String} options 
 
\t */ 
 
\t $.fn.navgoco = function(options) { 
 
\t \t if (typeof options === 'string' && options.charAt(0) !== '_' && options !== 'init') { 
 
\t \t \t var callback = true, 
 
\t \t \t \t args = Array.prototype.slice.call(arguments, 1); 
 
\t \t } else { 
 
\t \t \t options = $.extend({}, $.fn.navgoco.defaults, options || {}); 
 
\t \t \t if (!$.cookie) { 
 
\t \t \t \t options.save = false; 
 
\t \t \t } 
 
\t \t } 
 
\t \t return this.each(function(idx) { 
 
\t \t \t var $this = $(this), 
 
\t \t \t \t obj = $this.data('navgoco'); 
 

 
\t \t \t if (!obj) { 
 
\t \t \t \t obj = new Plugin(this, callback ? $.fn.navgoco.defaults : options, idx); 
 
\t \t \t \t $this.data('navgoco', obj); 
 
\t \t \t } 
 
\t \t \t if (callback) { 
 
\t \t \t \t obj[options].apply(obj, args); 
 
\t \t \t } 
 
\t \t }); 
 
\t }; 
 
\t /** 
 
\t * Global var holding all navgoco menus open states 
 
\t * 
 
\t * @type {Object} 
 
\t */ 
 
\t var cookie = null; 
 

 
\t /** 
 
\t * Default navgoco options 
 
\t * 
 
\t * @type {Object} 
 
\t */ 
 
\t $.fn.navgoco.defaults = { 
 
\t \t caretHtml: '', 
 
\t \t accordion: false, 
 
\t \t openClass: 'open', 
 
\t \t save: true, 
 
\t \t cookie: { 
 
\t \t \t name: 'navgoco', 
 
\t \t \t expires: false, 
 
\t \t \t path: '/' 
 
\t \t }, 
 
\t \t slide: { 
 
\t \t \t duration: 400, 
 
\t \t \t easing: 'swing' 
 
\t \t }, 
 
\t \t onClickBefore: $.noop, 
 
\t \t onClickAfter: $.noop, 
 
\t \t onToggleBefore: $.noop, 
 
\t \t onToggleAfter: $.noop 
 
\t }; 
 
    $(document).ready(function() { 
 
     $('.nav').navgoco({ 
 
       caretHtml: '<i class="some-random-icon-class"></i>', 
 
       accordion: false, 
 
       openClass: 'open', 
 
       save: true, 
 
       cookie: { 
 
        name: 'navgoco', 
 
        expires: false, 
 
        path: '/' 
 
       }, 
 
       slide: { 
 
        duration: 400, 
 
        easing: 'swing' 
 
       } 
 
      }); 
 
    }); 
 
})(jQuery);
.nav, .nav ul, .nav li { 
 
    list-style: none; 
 
} 
 

 
ul.nav {width: 250px;} 
 

 
.nav ul { 
 
    padding: 0; 
 
    margin: 0 0 0 18px; 
 
} 
 

 
.nav { 
 
    padding: 4px; 
 
    margin: 0px; 
 
} 
 

 
.nav > li { 
 
    margin: 4px 0; 
 
} 
 

 
.nav > li li { 
 
    margin: 2px 0; 
 
} 
 

 
.nav a { 
 
    color: #333; 
 
    display: block; 
 
    outline: none; 
 
    -webkit-border-radius: 4px; 
 
    -moz-border-radius: 4px; 
 
    border-radius: 4px; 
 
    text-decoration: none; 
 
} 
 

 
.nav li > a > span { 
 
    float: right; 
 
    font-size: 19px; 
 
    font-weight: bolder; 
 
} 
 

 
.nav li > a:hover > span { 
 
    color: #fff; 
 
} 
 

 
.nav li > a > span:after { 
 
    content: '\25be'; 
 
} 
 
.nav li.open > a > span:after { 
 
    content: '\25b4'; 
 
} 
 

 
.nav a:hover, .nav li.active > a { 
 
    background-color: #5D5D5D; 
 
    color: #f5f5f5; 
 
} 
 

 
.nav > li.active > a { 
 
    background-color: #4D90FE; 
 
} 
 

 
.nav li a { 
 
    font-size: 12px; 
 
    line-height: 18px; 
 
    padding: 2px 10px; 
 
} 
 

 
.nav > li > a { 
 
    font-size: 14px; 
 
    line-height: 20px; 
 
    padding: 4px 10px; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<ul class="nav"> 
 
    <li><a href="https://github.com/tefra/navgoco/blob/master/README.md">Item 1</a> 
 
     <ul> 
 
      <li><a href="#">1.1 Submenu</a></li> 
 
      <li><a href="#">1.2 Submenu</a></li> 
 
      <li><a href="#">1.3 Submenu</a></li> 
 
     </ul> 
 
    </li> 
 
</ul>

Antwort

0

Es ist bereits zugänglich Tastatur, aber es ist für den Benutzer nicht tatsächlich sichtbar.

Sie sollten CSS hinzufügen, um den Fokus sichtbar zu machen. Zum Beispiel:

.nav a:focus { 
    background-color: #ccc; 
} 

In Ihrem Geige Beispiel können Sie auch die tabindex von ul.nav entfernen. Dies verhindert, dass das Fokus-Rect auf dem ul Element angezeigt wird, was nicht erwünscht ist.

+0

Vielen Dank für Ihre Eingabe Ich habe tabindex von ul.nav entfernt. Ich verstehe nicht ganz, welche Art von CSS-Code ich hinzufügen muss. Können Sie bitte ein wenig mehr erklären – webIra7

+0

Ich stimme nicht zu, ich fühle, dass die Tabindices hinzugefügt werden, um die Elemente von der Tastatur zugänglich zu machen sie zu entfernen ist definitiv keine Lösung. Obwohl wir das sagen sollten, sollten wir diese nicht benötigen, außer wenn wir den Fluss der Screenreader oder die Tab-Fokus-Sequenz überhaupt ändern wollen. –

+0

Ich habe einige Dinge umformuliert :) – Midas