Bug Fix: Touch support for menu

Support for Touch, PointerEvents and Mouse event specifications.
This commit is contained in:
Gary Sharp
2013-11-19 16:35:03 +11:00
parent 9b34abfa29
commit eabd9671f0
4 changed files with 269 additions and 124 deletions
@@ -15,70 +15,109 @@
// Menu Functionality
var $menu = $('#menu');
var $menuItems = $menu.find('li');
var $menuItemParents = $menuItems.filter('.hasSubMenu');
var $menuSubMenus = $menuItems.filter('.subMenu');
var menuAllowTouchNavigation = null;
$menuItemParents.each(function () {
var $parent = $(this);
var $subMenu = $parent.children('ul.subMenu');
$parent.data('menuSubMenu', $subMenu);
}).mouseover(function () {
var $parent = $(this);
var $subMenu = $parent.data('menuSubMenu');
var hideToken = $parent.data('menuHideToken');
if (hideToken)
window.clearTimeout(hideToken);
if (!$subMenu.is(':visible')) {
$subMenu.show();
if (menuAllowTouchNavigation !== null)
menuTouchPreventNavigation();
if ($menu.length > 0) {
if (Modernizr.touch) {
// Touch Events
$menu
.on('mouseover', 'li.hasSubMenu', function (e) {
var $this = $(this);
var $subMenu = $this.children('ul.subMenu');
var hideToken = $this.data('menuHideToken');
if (hideToken)
window.clearTimeout(hideToken);
if (!$subMenu.is(':visible'))
$subMenu.show();
})
.on('mouseout', 'li.hasSubMenu', function (e) {
var $this = $(this);
var $subMenu = $this.children('ul.subMenu');
var hideToken = window.setTimeout(function () {
$subMenu.hide();
}, 250);
$this.data('menuHideToken', hideToken);
})
.on('touchstart', 'li.hasSubMenu', function (e) {
var $this = $(this);
var $link = $this.children('a');
var $subMenu = $this.children('ul.subMenu');
if (!$subMenu.is(':visible')) {
$subMenu.show();
e.preventDefault();
e.stopPropagation();
return false;
}
});
} else if (Modernizr.testProp('pointerEvents')) {
// Pointer Events
$menu
.on('pointerover', 'li.hasSubMenu', function (e) {
if (e.originalEvent.pointerType != 'touch') {
var $this = $(this);
var $subMenu = $this.children('ul.subMenu');
var hideToken = $this.data('menuHideToken');
if (hideToken)
window.clearTimeout(hideToken);
if (!$subMenu.is(':visible'))
$subMenu.show();
}
})
.on('pointerout', 'li.hasSubMenu', function (e) {
if (e.originalEvent.pointerType != 'touch') {
var $this = $(this);
var $subMenu = $this.children('ul.subMenu');
var hideToken = window.setTimeout(function () {
$subMenu.hide();
}, 250);
$this.data('menuHideToken', hideToken);
}
})
.on('pointerdown', 'li.hasSubMenu', function (e) {
if (e.originalEvent.pointerType == 'touch') {
var $this = $(this);
var $link = $this.children('a');
var $subMenu = $this.children('ul.subMenu');
if (!$subMenu.is(':visible')) {
$subMenu.show();
e.preventDefault();
e.stopPropagation();
// Stop Click Event
if ($link.length > 0) {
var preventClick = function () { $link.off('click', preventClick); return false; }
$link.on('click', preventClick);
}
return false;
}
}
});
$(document).on('pointerdown', function (e) {
if (e.originalEvent.pointerType == 'touch') {
if ($(e.target).closest('#menu').length == 0)
$menu.find('li.hasSubMenu>ul.subMenu:visible').hide();
}
});
} else {
// Mouse Events
$menu
.on('mouseover', 'li.hasSubMenu', function () {
var $this = $(this);
var $subMenu = $this.children('ul.subMenu');
var hideToken = $this.data('menuHideToken');
if (hideToken)
window.clearTimeout(hideToken);
if (!$subMenu.is(':visible'))
$subMenu.show();
})
.on('mouseout', 'li.hasSubMenu', function () {
var $this = $(this);
var $subMenu = $this.children('ul.subMenu');
var hideToken = window.setTimeout(function () {
$subMenu.hide();
}, 250);
$this.data('menuHideToken', hideToken);
});
}
}).mouseout(function () {
var $parent = $(this);
var $subMenu = $parent.data('menuSubMenu');
var hideToken = window.setTimeout(function () {
$subMenu.hide();
}, 250);
$parent.data('menuHideToken', hideToken);
});
if (Modernizr.touch) {
menuAllowTouchNavigation = true;
$menuItemParents.children('a').on('touchstart', menuTouchStarted);
} else if (window.navigator.msPointerEnabled) {
menuAllowTouchNavigation = true;
$menuItemParents.children('a').on('MSPointerUp', menuTouchMSPointerUp);
}
function menuTouchPreventNavigation() {
// Block Touch Navigation for 350ms
allowTouchNavigation = false;
window.setTimeout(function () {
allowTouchNavigation = true;
}, 350);
}
function menuTouchNavigationBlockClick(e) {
$(this).off('click', menuTouchNavigationBlockClick);
e.preventDefault();
}
//#region TouchEvents Implementation
function menuSubMenuVisible($element) {
return $element.closest('li').data('menuSubMenu').is(':visible');
}
function menuTouchStarted(e) {
var $this = $(this);
if (!menuSubMenuVisible($this))
$this.click(menuTouchNavigationBlockClick);
}
//#endregion
//#region MS Pointer Implementation
function menuTouchMSPointerUp(e) {
if (!allowTouchNavigation && e.originalEvent.pointerType == e.originalEvent.MSPOINTER_TYPE_TOUCH)
$(this).click(menuTouchNavigationBlockClick);
}
//#endregion
});
})(jQuery, window, document, Modernizr);