Jump to content

Recommended Posts

Posted

Hi, really hope someone here can help or offer some guidance.

 

I'm using dropotron to power the drop down menus (code will be on post below) on our new site which works fine. The problem I have is that's not very accessible for keyboard only users.

 

Curriculum

       Art    
       GCSE Media Studies    
       Humanities & Social Sciences      



Information

       Information Home
       Admissions
       Behaviour For Learning


 

For example, if a user hovers the mouse over Curriculum the submenu appears. On a mobile device touching Curriculum displays the sub menu. Tab-Enter will display the menu, but tabbing again will move to the next option at the top level, it will move to Information rather than Art on the submenu.

 

Looking at Information which has a specified URL we get the following. Mouse hover displays the sub menu, touch and tab-enter take the user to the URL specified.

 

What I'm desperate to achieve is the following behaviour:

 

  • When a menu is hovered by a mouse, show the sub-menu.
  • When a menu is clicked with a mouse, open the link (or do nothing, or close the menu - not too bothered!).
  • The first time a menu is clicked by touch, show the sub menu.
  • The second time a menu is clicked by touch, open the link (or do nothing, or close the menu - not too bothered!).
  • Hitting Enter opens the link

 

I'm in no way an expert (barely a beginner!) with JS so anything would be a help!

Posted
/* jquery.dropotron.js v1.4.3 | (c) n33 | n33.co | MIT licensed */

(function($) {

// Disables selection.
	$.fn.disableSelection_dropotron = function() { return $(this).css('user-select', 'none').css('-khtml-user-select', 'none').css('-moz-user-select', 'none').css('-o-user-select', 'none').css('-webkit-user-select', 'none'); }

// Dropotron prototype method.
	$.fn.dropotron = function(options) {

		if (this.length == 0)
			return $(this);

		if (this.length > 1)
			for (var i=0; i < this.length; i++)
				$(this[i]).dropotron(options);

		return $.dropotron($.extend({ selectorParent: $(this) }, options));

	}

// Dropotron method.
	$.dropotron = function(options) {

		// Settings.
			var settings = $.extend({

				// Parent jQuery object.
					selectorParent: null,
				
				// Base Z-Index.
					baseZIndex: 1000,
				
				// Menu class (assigned to every </pre><ul>).
					menuClass: 'dropotron',
				
				// Expansion mode ("hover" or "click").
					expandMode: 'hover',
				
				// Hover delay (in ms).
					hoverDelay: 150,
				
				// Hide delay (in ms; 0 disables).
					hideDelay: 250,
				
				// Opener class.
					openerClass: 'opener',
				
				// Active opener class.
					openerActiveClass: 'active',
				
				// Submenu class prefix.
					submenuClassPrefix: 'level-',
				
				// Menu mode ("instant", "fade", "slide", "zoom").
					mode: 'fade',
				
				// Menu speed ("fast", "slow", or ms).
					speed: 'fast',
				
				// Easing mode ("swing", "linear").
					easing: 'swing',
				
				// Alignment ("left", "center", "right").
					alignment: 'left',
				
				// Submenu offset X.
					offsetX: 0,
				
				// Submenu offset Y.
					offsetY: 0,
				
				// Global offset Y.
					globalOffsetY: 0,
				
				// IE Offset X.
					IEOffsetX: 0,
				
				// IE Offset Y.
					IEOffsetY: 0,
				
				// If true and mode = "fade", prevents top-level opener fade.
					noOpenerFade: true,
				
				// Detach second level menus (prevents parent style bleed).
					detach: true,
				
				// If true and detach = true, leave original menu intact.
					cloneOnDetach: true		

			}, options);

		// Vars.
			var	$top = settings.selectorParent,
				$menus = $top.find('ul'),
				$body = $('body'),
				$bodyhtml = $('body,html'),
				$window = $(window);
			
			var	isLocked = false,
				hoverTimeoutId = null,
				hideTimeoutId = null;

		// Main.
		
			// Top.
				$top
					.on('doCollapseAll', function() {
						$menus.trigger('doCollapse');
					});

			// Top level menus.
				$menus.each(function() {

					var $menu = $(this), $opener = $menu.parent();

					// If a hideDelay is set, set up the event.
						if (settings.hideDelay > 0)
							$menu.add($opener)
								.on('mouseleave', function(e) {
									window.clearTimeout(hideTimeoutId);
									hideTimeoutId = window.setTimeout(function() {
										$menu.trigger('doCollapse');
									}, settings.hideDelay);
								});

					// Menu.
						$menu
							.disableSelection_dropotron()
							.hide()
							.addClass(settings.menuClass)
							.css('position', 'absolute')
							.on('mouseenter', function(e) {
								window.clearTimeout(hideTimeoutId);
							})
							.on('doExpand', function() {
								
								// Already visible? Bail out.
									if ($menu.is(':visible'))
										return false;

								// Reset our "hide" timeout.
									window.clearTimeout(hideTimeoutId);
								
								// Collapse everything but this menu.
									$menus.each(function() {

										var $this = $(this);

										if (!$.contains($this.get(0), $opener.get(0)))
											$this.trigger('doCollapse');
									
									});

								// Vars.
									var	oo = $opener.offset(),
										op = $opener.position(),
										opp = $opener.parent().position(),
										ow = $opener.outerWidth(),
										mw = $menu.outerWidth(),
										isTL = ($menu.css('z-index') == settings.baseZIndex);
									
									var x, c, left, top;

								// If this is a top level menu ...
									if (isTL) {
										
										// If detach is enabled, use .position()
											if (!settings.detach)
												x = op;
										// Otherwise, use .offset()
											else
												x = oo;
									
										top = x.top + $opener.outerHeight() + settings.globalOffsetY;
										c = settings.alignment;
										
										// Remove alignment classes.
											$menu
												.removeClass('left')
												.removeClass('right')
												.removeClass('center');

										// Figure out alignment and position.
											switch (settings.alignment) {
												
												case 'right':
													left = x.left - mw + ow;
													
													if (left < 0) {

														left = x.left;
														c = 'left';

													}
														
													break;
													
												case 'center':
													left = x.left - Math.floor((mw - ow) / 2);

													if (left < 0) {

														left = x.left;
														c = 'left';

													}
													else if (left + mw > $window.width()) {
														
														left = x.left - mw + ow;
														c = 'right';
													
													}
														
													break;

												case 'left':
												default:
													left = x.left;
													
													if (left + mw > $window.width()) {
														
														left = x.left - mw + ow;
														c = 'right';
													
													}

													break;
											
											}
										
										// Add alignment class.
											$menu.addClass(c);
									
									}

								// Otherwise, we're dealing with a submenu.
									else {
									
										// If the opener position isn't static ...
											if ($opener.css('position') == 'relative'
											||	$opener.css('position') == 'absolute') {
												
												top = settings.offsetY;
												left = (-1 * op.left);
											
											}
											else {
												
												top = op.top + settings.offsetY;
												left = 0;
											
											}

										// Figure out position (based on alignment).
											switch (settings.alignment) {
												
												case 'right':
													left += (-1 * $opener.parent().outerWidth()) + settings.offsetX;
													
													break;
												
												case 'center':
												case 'left':
												default:
													left += $opener.parent().outerWidth() + settings.offsetX;

													break;
											
											}
									
									}

								// Legacy IE? Apply IE-specific offsets.
									if (navigator.userAgent.match(/MSIE ([0-9]+)\./) && RegExp.$1 < 8) {
										
										left += settings.IEOffsetX;
										top += settings.IEOffsetY;
									
									}

								// Position and show the menu.
									$menu
										.css('left', left + 'px')
										.css('top', top + 'px')
										.css('opacity', '0.01')
										.show();
								
								// Kludge!
									var tmp = false;
									
									// Non-static position fix.
										if ($opener.css('position') == 'relative'
										||	$opener.css('position') == 'absolute')
											left = (-1 * op.left);
										else
											left = 0;
									
									if ($menu.offset().left < 0) {
										
										left += $opener.parent().outerWidth() - settings.offsetX;
										tmp = true;
									
									}
									else if ($menu.offset().left + mw > $window.width()) {
										
										left += (-1 * $opener.parent().outerWidth()) - settings.offsetX;
										tmp = true;
									
									}

									if (tmp)
										$menu
											.css('left', left + 'px');

									$menu
										.hide()
										.css('opacity', '1');
								
								// Figure out animation mode.
									switch (settings.mode) {
										
										case 'zoom':

											isLocked = true;

											$opener.addClass(settings.openerActiveClass);
											$menu.animate({
												width: 'toggle',
												height: 'toggle'
											}, settings.speed, settings.easing, function() {
												isLocked = false;
											});

											break;
									
										case 'slide':

											isLocked = true;

											$opener.addClass(settings.openerActiveClass);
											$menu.animate({ height: 'toggle' }, settings.speed, settings.easing, function() {
												isLocked = false;
											});

											break;
									
										case 'fade':

											isLocked = true;
											
											if (isTL && !settings.noOpenerFade) {
												
												var tmp;

												if (settings.speed == 'slow')
													tmp = 80;
												else if (settings.speed == 'fast')
													tmp = 40;
												else
													tmp = Math.floor(settings.speed / 2);
												
												$opener.fadeTo(tmp, 0.01, function() {
													$opener.addClass(settings.openerActiveClass);
													$opener.fadeTo(settings.speed, 1);
													$menu.fadeIn(settings.speed, function() {
														isLocked = false;
													});
												});

											}
											else {

												$opener.addClass(settings.openerActiveClass);
												$opener.fadeTo(settings.speed, 1);
												$menu.fadeIn(settings.speed, function() {
													isLocked = false;
												});

											}

											break;
										
										case 'instant':
										default:

											$opener.addClass(settings.openerActiveClass);
											$menu.show();

											break;
									
									}

								return false;
							})
							.on('doCollapse', function() {
								
								// Not visible? Bail out.
									if (!$menu.is(':visible'))
										return false;

								// Collapse the menu.
									$menu.hide();
									$opener.removeClass(settings.openerActiveClass);
									$menu.find('.' + settings.openerActiveClass).removeClass(settings.openerActiveClass);
									$menu.find('ul').hide();
								
								return false;

							})
							.on('doToggle', function(e) {
							
								if ($menu.is(':visible'))
									$menu.trigger('doCollapse');
								else
									$menu.trigger('doExpand');
							
								return false;

							});
					
					// Menu's opener.
						$opener
							.disableSelection_dropotron()
							.addClass('opener')
							.css('cursor', 'pointer')
							.on('click touchend', function(e) {
							
								// Locked? Bail.
									if (isLocked)
										return;
								
								// Toggle menu.
									e.preventDefault();
									e.stopPropagation();
									$menu.trigger('doToggle');
									
							});

					// If expandMode is "hover", set up the event.
						if (settings.expandMode == 'hover')
							$opener.hover(function(e) {

								if (isLocked)	
									return;

								hoverTimeoutId = window.setTimeout(function() {
									$menu.trigger('doExpand');
								}, settings.hoverDelay);

							},
							function (e) {
							
								window.clearTimeout(hoverTimeoutId);
							
							});

				});

			// All links.
				$menus.find('a')
					.css('display', 'block')
					.on('click touchend', function(e) {

						// Locked? Bail.
							if (isLocked)
								return;
						
						// No href? Prevent default.
							if ($(this).attr('href').length < 1)
								e.preventDefault();

					});
				
			// All list items.
				$top.find('li')
					.css('white-space', 'nowrap')
					.each(function() {

						var $this = $(this), $a = $this.children('a'), $ul = $this.children('ul'),
							href = $a.attr('href');
						
						// If href is blank ("") or a hash (#), prevent the link from doing anything.
							$a.on('click touchend', function(e) {
								
								if (href.length == 0
								||	href == '#')
									e.preventDefault();
								else
									e.stopPropagation();

							});
						
						// If there is a link but no unordered list ...
							if ($a.length > 0 && $ul.length == 0)
								$this.on('click touchend', function(e) {

									if (isLocked)
										return;
										
									$top.trigger('doCollapseAll');

									e.stopPropagation();

								});

					});

			// Top level list items.
				$top.children('li').each(function() {

					var $opener = $(this), $menu = $opener.children('ul'),
						c;

					if ($menu.length > 0) {
						
						// If we're using detach ...
							if (settings.detach) {
								
								// If we're cloning on detach ...
									if (settings.cloneOnDetach) {
										
										// Make a copy of the menu.
											c = $menu.clone();
										
										// Leave it behind
											c
												.attr('class', '')
												.hide()
												.appendTo($menu.parent());
									
									}
							
								// Detach the menu and move it to the end of the  element.
									$menu
										.detach()
										.appendTo($body);
							
							}

						// Step through menus, assigning z-indexes and submenu class prefixes as necessary.
							for(var z = settings.baseZIndex, i = 1, y = $menu; y.length > 0; i++) {
								
								y.css('z-index', z++);
								
								if (settings.submenuClassPrefix)
									y.addClass(settings.submenuClassPrefix + (z - 1 - settings.baseZIndex));
								
								y = y.find('> li > ul');
							
							}
					
					}

				});
			
			// Window.
				$window
					.on('', function() {
						$top.trigger('doCollapseAll');
					})
					.on('keypress', function(e) {

						// Collapse all menus if the user hits escape.
							if (!isLocked && e.keyCode == 27) {

								e.preventDefault();
								$top.trigger('doCollapseAll');

							}

					});
			
			// Body.
				$bodyhtml
					.on('click touchend', function() {

						if (!isLocked)
							$top.trigger('doCollapseAll');

					});

	};

})(jQuery)

Posted

Just looking at the html I can see a slight issue in that you have gone nav -> list item -> unordered list -> list item etc - but this might be due to copying out the html. Your example seems a little incomplete though.

 

Taking your code, and the example at https://github.com/n33/jquery.dropotron, can come up with this though. The interesting event here is 'doExpand' because your main issue seems to be the fact that opening a menu with tab+enter does not then put the focus on the child list.

 

So changing the list HTML to the following ( sorting out the structure + adding in tabindex to submenu )

 


 
   
     Curriculum
     
       Art    
       GCSE Media Studies    
       Humanities & Social Sciences      
     
   
   
     Information
     
         Information Home
         Admissions
         Behaviour For Learning
     
   
 

 

And changing the line

[color=#333333]$menu.addClass(c);[/color]

to

 

$opener.find( 'a' ).blur(); [color=#333333]// Take focus off the opening link
[/color]
$menu
 .addClass(c)
 .find( 'li:first-of-type a' )
 .focus(); // After adding classes, give focus to the first anchor tag in the submenu

 

This makes it so that after opening a submenu with tabs, further tabs happen first of all in that submenu. I've tried the above in Chrome and IE11. Full gist available here : https://gist.github.com/SimonHooker/c797a95d965cec891e4df72028b1da2b

  • Thanks 1
Posted

Hi Simon,

 

Thank you so much for this, I really appreciate the time you took - you've got me out of a hole! Oddly it doesn't work in Firefox on Windows (only), but I'll dig into that later.

 

If someone has JS disabled, the menu won't appear is there anything simple I could to counteract this?

 

Thanks again.

Posted

Hi there,

 

It's odd that it doesn't work on Firefox as that should use the same rendering engine as Chrome. It may be the li:first-of-type selector that it's not agreeing with. You could also try

 

$menu
 .addClass(c)
 .find( 'li' )
 .first()
 .find( 'a' )
 .focus(); // After adding classes, give focus to the first anchor tag in the submenu

 

Regarding handling users without Javascript enabled, that is more difficult. The only real fallback you can do in for that would involve

  • Thanks 1
  • 2 weeks later...
Posted

Hi Simon,

 

Sorry to drag this back up, it appears this doesn't work on the latest version of Chrome (Windows or OS X) or Firefox (both OSs). Nearly had a heart attack when we started to deploy the updated versions this morning. Is there anything you can think of that I could do?

 

Thanks,

 

Ben

Posted

Hi Ben

 

Considering that I primarily use Chrome + so of course tested the above in Chrome originally, this is a bit concerning. If you could supply updated source code, what you actually have in your source that is, then I could give it another quick look in case there's something that's been missed somewhere along the line.

Posted

Yeah, it was working absolutely perfectly until this morning until we noticed it stopped working on machines running version 50.0.2661.86. :dizzy:

 

Nothing has changed in the code (I haven't touched it anyway) but have pasted below anyway. I can't thank you enough for looking at this. The JS will be in a separate post due to the length.

 


 
   
     Curriculum
     
       Art    
       GCSE Media Studies    
       Humanities & Social Sciences      
     
   
   
     Information
     
         Information Home
         Admissions
         Behaviour For Learning
     
   
 

Posted

Looks like I'm still on 49, will update and get back to you once I get a gap to look at this in, hopefully not too long.

 

Sure hope Chrome 50 doesn't break the JS I've just been working on!

Posted
(function($) {

// Disables selection.
	$.fn.disableSelection_dropotron = function() { return $(this).css('user-select', 'none').css('-khtml-user-select', 'none').css('-moz-user-select', 'none').css('-o-user-select', 'none').css('-webkit-user-select', 'none'); }

// Dropotron prototype method.
	$.fn.dropotron = function(options) {

		if (this.length == 0)
			return $(this);

		if (this.length > 1)
			for (var i=0; i < this.length; i++)
				$(this[i]).dropotron(options);

		return $.dropotron($.extend({ selectorParent: $(this) }, options));

	}

// Dropotron method.
	$.dropotron = function(options) {

		// Settings.
			var settings = $.extend({

				// Parent jQuery object.
					selectorParent: null,
				
				// Base Z-Index.
					baseZIndex: 1000,
				
				// Menu class (assigned to every </pre><ul>).
					menuClass: 'dropotron',
				
				// Expansion mode ("hover" or "click").
					expandMode: 'hover',
				
				// Hover delay (in ms).
					hoverDelay: 150,
				
				// Hide delay (in ms; 0 disables).
					hideDelay: 250,
				
				// Opener class.
					openerClass: 'opener',
				
				// Active opener class.
					openerActiveClass: 'active',
				
				// Submenu class prefix.
					submenuClassPrefix: 'level-',
				
				// Menu mode ("instant", "fade", "slide", "zoom").
					mode: 'fade',
				
				// Menu speed ("fast", "slow", or ms).
					speed: 'fast',
				
				// Easing mode ("swing", "linear").
					easing: 'swing',
				
				// Alignment ("left", "center", "right").
					alignment: 'left',
				
				// Submenu offset X.
					offsetX: 0,
				
				// Submenu offset Y.
					offsetY: 0,
				
				// Global offset Y.
					globalOffsetY: 0,
				
				// IE Offset X.
					IEOffsetX: 0,
				
				// IE Offset Y.
					IEOffsetY: 0,
				
				// If true and mode = "fade", prevents top-level opener fade.
					noOpenerFade: true,
				
				// Detach second level menus (prevents parent style bleed).
					detach: true,
				
				// If true and detach = true, leave original menu intact.
					cloneOnDetach: true		

			}, options);

		// Vars.
			var	$top = settings.selectorParent,
				$menus = $top.find('ul'),
				$body = $('body'),
				$bodyhtml = $('body,html'),
				$window = $(window);
			
			var	isLocked = false,
				hoverTimeoutId = null,
				hideTimeoutId = null;

		// Main.
		
			// Top.
				$top
					.on('doCollapseAll', function() {
						$menus.trigger('doCollapse');
					});

			// Top level menus.
				$menus.each(function() {

					var $menu = $(this), $opener = $menu.parent();

					// If a hideDelay is set, set up the event.
						if (settings.hideDelay > 0)
							$menu.add($opener)
								.on('mouseleave', function(e) {
									window.clearTimeout(hideTimeoutId);
									hideTimeoutId = window.setTimeout(function() {
										$menu.trigger('doCollapse');
									}, settings.hideDelay);
								});

					// Menu.
						$menu
							.disableSelection_dropotron()
							.hide()
							.addClass(settings.menuClass)
							.css('position', 'absolute')
							.on('mouseenter', function(e) {
								window.clearTimeout(hideTimeoutId);
							})
							.on('doExpand', function() {
								
								// Already visible? Bail out.
									if ($menu.is(':visible'))
										return false;

								// Reset our "hide" timeout.
									window.clearTimeout(hideTimeoutId);
								
								// Collapse everything but this menu.
									$menus.each(function() {

										var $this = $(this);

										if (!$.contains($this.get(0), $opener.get(0)))
											$this.trigger('doCollapse');
									
									});

								// Vars.
									var	oo = $opener.offset(),
										op = $opener.position(),
										opp = $opener.parent().position(),
										ow = $opener.outerWidth(),
										mw = $menu.outerWidth(),
										isTL = ($menu.css('z-index') == settings.baseZIndex);
									
									var x, c, left, top;

								// If this is a top level menu ...
									if (isTL) {
										
										// If detach is enabled, use .position()
											if (!settings.detach)
												x = op;
										// Otherwise, use .offset()
											else
												x = oo;
									
										top = x.top + $opener.outerHeight() + settings.globalOffsetY;
										c = settings.alignment;
										
										// Remove alignment classes.
											$menu
												.removeClass('left')
												.removeClass('right')
												.removeClass('center');

										// Figure out alignment and position.
											switch (settings.alignment) {
												
												case 'right':
													left = x.left - mw + ow;
													
													if (left < 0) {

														left = x.left;
														c = 'left';

													}
														
													break;
													
												case 'center':
													left = x.left - Math.floor((mw - ow) / 2);

													if (left < 0) {

														left = x.left;
														c = 'left';

													}
													else if (left + mw > $window.width()) {
														
														left = x.left - mw + ow;
														c = 'right';
													
													}
														
													break;

												case 'left':
												default:
													left = x.left;
													
													if (left + mw > $window.width()) {
														
														left = x.left - mw + ow;
														c = 'right';
													
													}

													break;
											
											}
										
										// Add alignment class.
											$opener.find( 'a' ).blur(); // Take focus off the opening link

$menu
 .addClass(c)
 .find( 'li' )
 .first()
 .find( 'a' )
 .focus(); // After adding classes, give focus to the first anchor tag in the submenu
									
									}

								// Otherwise, we're dealing with a submenu.
									else {
									
										// If the opener position isn't static ...
											if ($opener.css('position') == 'relative'
											||	$opener.css('position') == 'absolute') {
												
												top = settings.offsetY;
												left = (-1 * op.left);
											
											}
											else {
												
												top = op.top + settings.offsetY;
												left = 0;
											
											}

										// Figure out position (based on alignment).
											switch (settings.alignment) {
												
												case 'right':
													left += (-1 * $opener.parent().outerWidth()) + settings.offsetX;
													
													break;
												
												case 'center':
												case 'left':
												default:
													left += $opener.parent().outerWidth() + settings.offsetX;

													break;
											
											}
									
									}

								// Legacy IE? Apply IE-specific offsets.
									if (navigator.userAgent.match(/MSIE ([0-9]+)\./) && RegExp.$1 < 8) {
										
										left += settings.IEOffsetX;
										top += settings.IEOffsetY;
									
									}

								// Position and show the menu.
									$menu
										.css('left', left + 'px')
										.css('top', top + 'px')
										.css('opacity', '0.01')
										.show();
								
								// Kludge!
									var tmp = false;
									
									// Non-static position fix.
										if ($opener.css('position') == 'relative'
										||	$opener.css('position') == 'absolute')
											left = (-1 * op.left);
										else
											left = 0;
									
									if ($menu.offset().left < 0) {
										
										left += $opener.parent().outerWidth() - settings.offsetX;
										tmp = true;
									
									}
									else if ($menu.offset().left + mw > $window.width()) {
										
										left += (-1 * $opener.parent().outerWidth()) - settings.offsetX;
										tmp = true;
									
									}

									if (tmp)
										$menu
											.css('left', left + 'px');

									$menu
										.hide()
										.css('opacity', '1');
								
								// Figure out animation mode.
									switch (settings.mode) {
										
										case 'zoom':

											isLocked = true;

											$opener.addClass(settings.openerActiveClass);
											$menu.animate({
												width: 'toggle',
												height: 'toggle'
											}, settings.speed, settings.easing, function() {
												isLocked = false;
											});

											break;
									
										case 'slide':

											isLocked = true;

											$opener.addClass(settings.openerActiveClass);
											$menu.animate({ height: 'toggle' }, settings.speed, settings.easing, function() {
												isLocked = false;
											});

											break;
									
										case 'fade':

											isLocked = true;
											
											if (isTL && !settings.noOpenerFade) {
												
												var tmp;

												if (settings.speed == 'slow')
													tmp = 80;
												else if (settings.speed == 'fast')
													tmp = 40;
												else
													tmp = Math.floor(settings.speed / 2);
												
												$opener.fadeTo(tmp, 0.01, function() {
													$opener.addClass(settings.openerActiveClass);
													$opener.fadeTo(settings.speed, 1);
													$menu.fadeIn(settings.speed, function() {
														isLocked = false;
													});
												});

											}
											else {

												$opener.addClass(settings.openerActiveClass);
												$opener.fadeTo(settings.speed, 1);
												$menu.fadeIn(settings.speed, function() {
													isLocked = false;
												});

											}

											break;
										
										case 'instant':
										default:

											$opener.addClass(settings.openerActiveClass);
											$menu.show();

											break;
									
									}

								return false;
							})
							.on('doCollapse', function() {
								
								// Not visible? Bail out.
									if (!$menu.is(':visible'))
										return false;

								// Collapse the menu.
									$menu.hide();
									$opener.removeClass(settings.openerActiveClass);
									$menu.find('.' + settings.openerActiveClass).removeClass(settings.openerActiveClass);
									$menu.find('ul').hide();
								
								return false;

							})
							.on('doToggle', function(e) {
							
								if ($menu.is(':visible'))
									$menu.trigger('doCollapse');
								else
									$menu.trigger('doExpand');
							
								return false;

							});
					
					// Menu's opener.
						$opener
							.disableSelection_dropotron()
							.addClass('opener')
							.css('cursor', 'pointer')
							.on('click touchend', function(e) {
							
								// Locked? Bail.
									if (isLocked)
										return;
								
								// Toggle menu.
									e.preventDefault();
									e.stopPropagation();
									$menu.trigger('doToggle');
									
							});

					// If expandMode is "hover", set up the event.
						if (settings.expandMode == 'hover')
							$opener.hover(function(e) {

								if (isLocked)	
									return;

								hoverTimeoutId = window.setTimeout(function() {
									$menu.trigger('doExpand');
								}, settings.hoverDelay);

							},
							function (e) {
							
								window.clearTimeout(hoverTimeoutId);
							
							});

				});

			// All links.
				$menus.find('a')
					.css('display', 'block')
					.on('click touchend', function(e) {

						// Locked? Bail.
							if (isLocked)
								return;
						
						// No href? Prevent default.
							if ($(this).attr('href').length < 1)
								e.preventDefault();

					});
				
			// All list items.
				$top.find('li')
					.css('white-space', 'nowrap')
					.each(function() {

						var $this = $(this), $a = $this.children('a'), $ul = $this.children('ul'),
							href = $a.attr('href');
						
						// If href is blank ("") or a hash (#), prevent the link from doing anything.
							$a.on('click touchend', function(e) {
								
								if (href.length == 0
								||	href == '#')
									e.preventDefault();
								else
									e.stopPropagation();

							});
						
						// If there is a link but no unordered list ...
							if ($a.length > 0 && $ul.length == 0)
								$this.on('click touchend', function(e) {

									if (isLocked)
										return;
										
									$top.trigger('doCollapseAll');

									e.stopPropagation();

								});

					});

			// Top level list items.
				$top.children('li').each(function() {

					var $opener = $(this), $menu = $opener.children('ul'),
						c;

					if ($menu.length > 0) {
						
						// If we're using detach ...
							if (settings.detach) {
								
								// If we're cloning on detach ...
									if (settings.cloneOnDetach) {
										
										// Make a copy of the menu.
											c = $menu.clone();
										
										// Leave it behind
											c
												.attr('class', '')
												.hide()
												.appendTo($menu.parent());
									
									}
							
								// Detach the menu and move it to the end of the  element.
									$menu
										.detach()
										.appendTo($body);
							
							}

						// Step through menus, assigning z-indexes and submenu class prefixes as necessary.
							for(var z = settings.baseZIndex, i = 1, y = $menu; y.length > 0; i++) {
								
								y.css('z-index', z++);
								
								if (settings.submenuClassPrefix)
									y.addClass(settings.submenuClassPrefix + (z - 1 - settings.baseZIndex));
								
								y = y.find('> li > ul');
							
							}
					
					}

				});
			
			// Window.
				$window
					.on('', function() {
						$top.trigger('doCollapseAll');
					})
					.on('keypress', function(e) {

						// Collapse all menus if the user hits escape.
							if (!isLocked && e.keyCode == 27) {

								e.preventDefault();
								$top.trigger('doCollapseAll');

							}

					});
			
			// Body.
				$bodyhtml
					.on('click touchend', function() {

						if (!isLocked)
							$top.trigger('doCollapseAll');

					});

	};
})(jQuery)

  • 2 weeks later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now



×
×
  • Create New...