0

マウスオーバー/マウスアウト時にボタンのメニューの表示/非表示を有効にする Ext.Button の拡張に取り組んでいます。ボタンの直接の子メニューでは完全に機能していますが、二次/三次/ect メニューで適切に動作するという問題が発生しています。

現時点では、ユーザーがメニューを含むトップ メニューの項目に移動すると、メニューが開き、ユーザーは問題なくカーソルをそこに移動できます。すべてが開いたままになります。次に、ユーザーがカーソルをセカンダリ メニューからオープン スペースに移動すると、すべてのメニューが閉じますが、これも正しいことです。ただし、ユーザーがセカンダリ メニューに移動してから親メニューに戻ると、すべてのメニューが閉じることがありますが、これは起こるべきではありません。少なくとも、カーソルが現在上にある親メニューは開いたままにする必要があります。 .

私の最初のデバッグから、イベントの発生方法とそのタイミングに問題があるようです。子メニューから親メニューに戻ると、親メニューの mouseenter イベントが発生しないようです。そして第二に、子メニューの mouseleave イベントが発生した後、メニューの mouseover イベントが確実に、または遅延した非表示タスクをキャンセルするのに十分な頻度で発生しないように見えます。

問題のデモ: 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);
    }
});
4

2 に答える 2

4

これが機能し、よりシンプルであることがわかりました。

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);
        }
    }
});
于 2014-03-13T05:19:06.607 に答える
1

私は少し似たようなことをしていて、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);
 }
}

それが役に立てば幸い!

于 2012-11-08T17:14:40.260 に答える