【问题标题】:ExtJS 4.1 "HoverButton" extension issueExtJS 4.1“HoverButton”扩展问题
【发布时间】:2012-05-08 19:22:30
【问题描述】:

我正在开发 Ext.Button 的扩展,它可以在鼠标悬停/鼠标悬停时显示/隐藏按钮的菜单。它对于按钮的直接子菜单非常有效,但是我遇到了一个问题,让它在任何二级/三级/等菜单中都能正常运行。

现在,当用户移动到包含菜单的顶部菜单中的某个项目时,它将打开该菜单并且用户可以毫无问题地将光标移入其中,一切都将保持打开状态。如果用户随后将光标移出二级菜单进入开放空间,则所有菜单都将关闭,这也是正确的。但是,有时如果用户移动到二级菜单,然后回到其父菜单,所有菜单都会关闭,这不是应该发生的,至少光标现在所在的父菜单应该保持打开状态.

从我最初的调试来看,事件的触发方式及其时间似乎存在问题。从子菜单移回父菜单时,似乎不会触发父菜单的 mouseenter 事件。其次,在我看来,菜单 mouseover 事件的触发不够可靠或不够频繁,无法在子菜单上的 mouseleave 事件触发后取消延迟的隐藏任务。

问题演示:http://qs1724.pair.com/users/autod1nx/EMPLOYEE/BDAMI/hoverbutton/index.html

这是代码,它有什么根本性的错误吗?

Ext.define('Ext.HoverButton', {    
    extend: 'Ext.Button',
    alias: 'widget.hoverButton',
    isOver: false,
    hideDelay: 250,
    showDelay: 200,

    applyListeners: function(menu, cfg) {
        Ext.apply(menu, cfg);
        Ext.each(menu.items, function(item, idx, allItems) {
            if(item.menu) this.applyListeners(item.menu, cfg);
        }, this);
    },

    initComponent: function() {
        var config = {}, 
            menuConfig = {}, 
            me = this;

        me.delayedShowMenu = new Ext.util.DelayedTask(function() {
            if(!me.isOver) return;
            me.showMenu();
        }, this);

        me.delayedHideMenu = new Ext.util.DelayedTask(function() {
            if(me.isOver) return;
            me.hideMenu();
        });

        if(Ext.isDefined(this.initialConfig.menu)) {
            config = {
                listeners: {
                    mouseover: {
                        scope: me,
                        fn: function(b) {
                            me.isOver = true;
                            me.delayedShowMenu.delay(me.showDelay);
                        }
                    },
                    mouseout: {
                        scope: me,
                        fn: function(b) {
                            me.isOver = false;
                            me.delayedHideMenu.delay(me.hideDelay);
                        }
                    }
                }
            };

            menuConfig = {
                listeners: {
                    mouseover: {
                        scope: me,
                        fn: function(menu, item, e) {
                            me.delayedHideMenu.cancel();
                        }
                    },
                    mouseenter: {
                        scope: me,
                        fn: function(menu, e) {
                            me.delayedHideMenu.cancel();
                        }
                    },
                    mouseleave: {
                        scope: me,
                        fn: function(menu, e) {
                            me.delayedHideMenu.delay(me.hideDelay);
                        }
                    }
                }
            };


            //apply mouseover/leave listeners to all submenus recursively
            me.applyListeners(me.menu, menuConfig);      
        }

        Ext.apply(me, Ext.apply(me.initialConfig, config));
        Ext.HoverButton.superclass.initComponent.apply(me, arguments);
    }
});

【问题讨论】:

  • 您是否尝试将me.isOver = true/false 添加到 menuConfig 监听事件?
  • 我有,是的,似乎没有任何区别。经过更多测试后,问题似乎在于鼠标悬停事件是如何触发的。在离开子菜单时触发 mouseleave 事件后,这并不能真正持续或经常触发 .cancel() 。

标签: button extjs menu hover extjs4


【解决方案1】:

我发现这个可行,而且更简单。

Ext.define('Ext.HoverButton', {
extend    : 'Ext.Button',
alias     : 'widget.hoverButton',
listeners : {
        mouseover : function() {
            this.showMenu();
        },
        menushow : function() {
            this.mouseLeaveMonitor = this.menu.el.monitorMouseLeave(100, this.hideMenu, this);
        },
        destroy : function(combo) {
            combo.menu.el.un(combo.mouseLeaveMonitor);
        }
    }
});

【讨论】:

    【解决方案2】:

    我一直在做一些类似的事情,在查看http://www.quirksmode.org/dom/events/mouseover.html后我已经解决了这个问题

    似乎 DOM 的事件顺序应该是 mouseover -> mouseenter -> mouseout -> mouseleave,这意味着有时会在设置 delay() 之前调用 cancel()。为了解决这个问题,我将最后输入的内容保留在一个变量中:

    mouseenter: {
     scope: me,
     fn: function(menu, e) {
      presentlyInside = menu; /* << */
      me.delayedHideMenu.cancel();
     }
    },
    mouseleave: {
     scope: me,
     fn: function(menu, e) {
      if(presentlyInside==menu) /* << */
        me.delayedHideMenu.delay(me.hideDelay);
     }
    }
    

    希望对你有帮助!

    【讨论】:

    • 有趣,我已经有一段时间没有接触过这段代码了,但下次有机会一定要试一试。感谢您的回复!
    • 我在发布 anwser 前几分钟才让它工作,但到目前为止,在我的实现中(以及在 Chrome 上)我还没有发现任何故障。
    • 我知道这个回复很晚,但我终于用你的修复更新了扩展,它就像一个冠军!再次感谢!
    • 不适合我,菜单突然消失...请在此处发布完整代码
    猜你喜欢
    • 1970-01-01
    • 2013-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-18
    • 2012-06-22
    • 1970-01-01
    相关资源
    最近更新 更多