1. #1
    Sencha User BulletzBill's Avatar
    Join Date
    Mar 2010
    Location
    New York
    Posts
    138
    Vote Rating
    0
    BulletzBill is on a distinguished road

      0  

    Default Help with a "HoverButton" extension

    Help with a "HoverButton" extension


    I am working on an extension of Ext.Button that enables the showing/hiding of a button's menu on mouseover/mouseout. It is working perfectly for the button's immediate child menu, however I am running into an issue with having it behave properly for any secondary/tertiary/ect menus.

    Right now, when the user moves over am item in the top menu that contains a menu, it will open the menu and the user can move the cursor into it with no problems, everything will stay open. If the user then moves the cursor out of the secondary menu into open space, all menus will close which is correct as well. BUT, sometimes if a user moves into a secondary menu, and then back into its parent menu, all the menus will close, which isn't what should happen, at the very least that parent menu that the cursor is now over should remain open.

    From my initial debugging it looks to be an issue with how the events are firing, and their timing. It appears that the mouseenter event for a parent menu does not fire when moving from a child menu back into the parent menu. And secondly it looks to me like the the menu mouseover event does not fire reliably enough or often enough for it to cancel the delayed hide task after a mouseleave event on a child menu has fired.

    Here's the code below, does anything fundamentally wrong with it stand out to anyone?

    Code:
    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);
        }
    });

  2. #2
    Sencha User BulletzBill's Avatar
    Join Date
    Mar 2010
    Location
    New York
    Posts
    138
    Vote Rating
    0
    BulletzBill is on a distinguished road

      0  

    Default


    Here's a demo, may help illustrate what the actual issue is better than my description:

    http://qs1724.pair.com/users/autod1n...ton/index.html

  3. #3
    Sencha User BulletzBill's Avatar
    Join Date
    Mar 2010
    Location
    New York
    Posts
    138
    Vote Rating
    0
    BulletzBill is on a distinguished road

      0  

    Default


    After more testing it looks like the issue lies in how the mouseover event is firing. In that is not really firing consistently or often enough to invoke the .cancel() on delayedHideMenu after the mouseleave event fires when leaving a sub menu.

    In Ext is the mouseover event for elements in general buffered or limited internally in some way so that it does not fire continually as the mouse moves or something? I did try setting the "buffer" and "delay" options both to zero for the menu mouseover event just to make sure, but didn't seem to make any difference. Anyone have more insight on this matter?

  4. #4
    Sencha User BulletzBill's Avatar
    Join Date
    Mar 2010
    Location
    New York
    Posts
    138
    Vote Rating
    0
    BulletzBill is on a distinguished road

      0  

    Default


    still stumped on this, any help would be greatly appreciated!

  5. #5
    Sencha Premium Member
    Join Date
    Jan 2008
    Posts
    112
    Vote Rating
    14
    KajaSheen will become famous soon enough

      0  

    Default


    This is my working solution:
    PHP Code:
    Ext.define('Ext.ux.button.HoverButton', {
        
    extend'Ext.button.Button',
        
    alias'widget.hoverbutton',
        
    isOverfalse,
        
    hideDelay250,
        
    showDelay0,

        
    applyListeners: function(menucfg) {
            
    // allow for array notation
            
    if(Ext.isArray(menu)) {
                var 
    menuObj = {xtype'menu'itemsmenu};
                
    menu menuObj;
            }
            
    Ext.apply(menucfg);
            
    Ext.each(menu.items, function(itemidxallItems) {
                if(
    item.menu) {item.menu this.applyListeners(item.menucfg);}
            }, 
    this);

            return 
    menu;
        },

        
    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: {
                            
    scopeme,
                            
    fn: function(b) {
                                
    me.isOver true;
                                
    me.delayedShowMenu.delay(me.showDelay);
                            }
                        },
                        
    mouseout: {
                            
    scopeme,
                            
    fn: function(b) {
                                
    me.isOver false;
                                
    me.delayedHideMenu.delay(me.hideDelay);
                            }
                        }
                    }
                };

                
    menuConfig = {
                    
    listeners: {
                        
    mouseover: {
                            
    scopeme,
                            
    fn: function(menuiteme) {
                                
    me.isOver true;
                                
    me.delayedHideMenu.cancel();
                            }
                        },
                        
    mouseenter: {
                            
    scopeme,
                            
    fn: function(menue) {
                                
    me.isOver true;
                                
    me.delayedHideMenu.cancel();
                            }
                        },
                        
    mouseleave: {
                            
    scopeme,
                            
    fn: function(menue) {
                                
    me.isOver false;
                                
    me.delayedHideMenu.delay(me.hideDelay);
                            }
                        }
                    }
                };

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

            
    Ext.apply(meExt.apply(me.initialConfigconfig));
            
    this.callParent(arguments);
        }
    }); 
    I had to apply the changed menu to the initialConfig as well, because otherwise the listeners were not getting added. Also I added a check to the applyListeners to be able to support Array Notation of Menu, which is getting rewritten to Ext Cfg Object. Might help someone out there.

Thread Participants: 1