Mouseleave种族条件

时间:2016-12-03 10:32:29

标签: javascript css internet-explorer microsoft-edge dom-events

我正在建立一个菜单栏。在栏中,每个菜单项都包含一个链接,而那些具有子菜单的菜单项也有一个可点击的区域来打开子菜单。显示和隐藏子菜单的逻辑是用JavaScript完成的,它主要通过click和mouseleave事件来切换元素的类名。

mouseleave事件已附加到所有子菜单元素,其中一个附加到其opener元素。这些事件负责在需要时隐藏子菜单。单击侦听器已附加到包含整个菜单栏的元素。此侦听器在需要时打开子菜单,或者在单击链接时关闭所有子菜单。

接下来发生的问题是:当用户点击链接时,通过将其类切换到设置display: none的类来隐藏所有子类。当然这会触发mouseleave事件,基于类切换的逻辑会再次显示子菜单。

我试图通过设置一个标志来解决这个问题,该标志在mouseleave处理程序中进行了检查。如果已设置标志,则不执行任何操作。该标志将在短暂延迟后重置。

这适用于FF和Chrome,但在IE11和Edge中导致一个奇怪的问题:如果用户在点击链接后保持鼠标不动,并在延迟(500毫秒)后开始移动鼠标,则隐藏的子菜单会弹出再次进入屏幕。

这似乎发生了,因为mouseleave事件在隐藏元素上触发,甚至在延迟之后。如何修复代码,以便IE和Edge也可以隐藏子菜单?

您可以使用下面的堆栈代码重现IE11和Edge的问题,或使用fiddle进行播放。

var WWMainMenu = {
    /// Hides all Submenus in Mainmenu
    closeAllSubs: function () {
        var subs = Array.prototype.slice.call(this.mainMenu.querySelectorAll('.ww-submenu-exp'));
        subs.forEach(function (sub) {
            sub.classList.toggle('ww-submenu');
            sub.classList.toggle('ww-submenu-exp');
            return;
        });
        return;
    },
    /// Hides a current Submenu when blurring the opener element
    closeCurrent: function (e) {
        var nextSub = e.target.nextElementSibling;
        if (this.noLeave) {
            return; /// Quit, toggling sub is not allowed
        }
        if (e.relatedTarget !== nextSub && nextSub.classList.contains('ww-submenu-exp')) {
            nextSub.classList.toggle('ww-submenu');
            nextSub.classList.toggle('ww-submenu-exp');
        }
        return;
    },
    /// Shows/hides Submenu
    toggleSubmenu: function (e) {
        var target = e.target,
            that = this;
        /// Hide all Submenus when clicking on links
        if (target.tagName === 'A') {
        	this.closeAllSubs();
            // Circumvent the race condition
            this.noLeave = true;
            window.setTimeout(function () {
                that.noLeave = false;
            }, 500);
            return;
        }
        /// Validate the target
        if (!target.classList.contains('ww-menuentry')) {
            return;
        } /// Quit, not clicked on a menuentry
        /// Toggle the visibility of Submenu
        target.nextElementSibling.classList.toggle('ww-submenu');
        target.nextElementSibling.classList.toggle('ww-submenu-exp');
        return;
    },
    /// Hides Submenu when blurring the menu itself
    hideSubmenu: function (e) {
        if (this.noLeave) {
            return; /// Quit, toggling sub is not allowed
        } 
        if (this.mainMenu.classList.contains('ww-show-menu')) {
            return; /// Quit, no autoclose when on small screens
        } 
        e.target.classList.toggle('ww-submenu');
        e.target.classList.toggle('ww-submenu-exp');
        return;
    },
    /// Initializes a WWMainmenu object
    init: function (options) {
        var subs, /// Stores all Submenu elements in Mainmenu [Array]
            entries; /// Stores all Menuentry elements in Mainmenu [Array]
        subs = Array.prototype.slice.call(options.menuBar.querySelectorAll('.ww-submenu'));
        entries = Array.prototype.slice.call(options.menuBar.querySelectorAll('.ww-menuentry'));
        /// Create prooperties
        this.mainMenu = options.menuBar; /// Reference to the Mainmenu element [HTMLElement]
        this.noLeave = false; /// Flag controlling mouseleave handling on Submenus and Menuentries
        /// Add click listener for toggling Submenus
        this.mainMenu.addEventListener('click', this.toggleSubmenu.bind(this));
        /// Add blur listeners for Submenus
        subs.forEach(function (sub) {
            sub.addEventListener('mouseleave', this.hideSubmenu.bind(this));
            return;
        }, this);
        /// Add blur listeners for Menuentries
        entries.forEach(function (entry) {
            entry.addEventListener('mouseleave', this.closeCurrent.bind(this));
            return;
        }, this);
        return this;
    }
}.init({
	menuBar: document.querySelector('.ww-mainmenu-bar')
});
a {
    text-decoration: none;
}
/* Menu system */
.ww-mainmenu-bar {
	display: block;
	background-color: rgba(64, 64, 64, 1);
	font-size: 1.0em;
	white-space: nowrap;
}
.ww-mainmenu {
	display: inline-block;
	white-space: nowrap;
	color: rgba(255, 255, 255, 1);
}
.ww-menuentry, .ww-menuentry a, .ww-submenu-exp a {
	color: rgba(255, 255, 255, 1);
}
.ww-menuentry {
	padding: 5px;
}
.ww-menuentry:hover {
	color: rgba(255, 255, 255, 1);
	background: rgba(70, 130, 180, 1);
}
.ww-menuentry::after {
	content: "\2261";
	position: absolute;
	right: 10px;
	cursor: default;
	color: rgba(255, 255, 255, 1);
}
.ww-mainmenu > .ww-menuentry::after {
	position: relative;
	content: "\2261";
	right: 0px;
	margin-left: 10px;
	color: rgba(255, 255, 255, 1);	
}
.ww-submenu, .ww-submenu-exp {
	display: none;
	position: absolute;
	min-width: calc(160px);
	background: rgba(64, 64, 64, 1);
	font-size: 0.95em;
	z-index: 20;
	white-space: nowrap;
	margin-left: 2em;
	padding: 5px;
	border: 1px solid rgba(0, 0, 0, 0.5);
}
.ww-submenu-exp {
	display: block;
}
.ww-submenu-exp > a {
	display: block;
	padding: 5px;
}
.ww-submenu-exp > a:hover {
	background: rgba(70, 130, 180, 1);
}
<!-- MAIN MENU -->
<nav class="ww-mainmenu-bar">
    <div class="ww-mainmenu">
        <div class="ww-menuentry"><a href="#">Main link</a></div>
        <div class="ww-submenu">
            <a href="#">Link 1</a>
            <div class="ww-menuentry"><a href="#">Link 2</a></div>
            <div class="ww-submenu">
                <a href="#">Link 2.1</a>
                <a href="#">Link 2.2</a>
            </div>					
            <div class="ww-menuentry"><a href="#">Link 3</a></div>
            <div class="ww-submenu">
                <a href="#">Link 3.1</a>
                <a href="#">Link 3.2</a>
            </div>
        </div>
    </div>
</nav>

1 个答案:

答案 0 :(得分:1)

SELECT animation_id, animation_name, animation_group FROM animations WHERE animation_id = '45' OR active = true ORDER BY animation_group, animation_name 函数中,为什么不明确地呼叫hideSubmenu而不是hide()