Latency issue with Primefaces overlayPanel - loads to lazy

后端 未结 4 1897
天涯浪人
天涯浪人 2021-01-04 22:51

I am using Primefaces 3.2 with jsf 2 and glassfish 3.1.2.

I have a p:dataTable of users containing avatars of the user. Whenever the user moves the mouse over the av

4条回答
  •  Happy的楠姐
    2021-01-04 23:43

    Wow, finally after a long debuging session and testing various approaches i recognized that the problem isnt the ajax request but the event handlers itself:

    .on(hideEvent, this.targetId, this, function(e) {
                var _self = e.data;
    
                if(_self.isVisible()) {
                    _self.hide();
                }
            });
    

    As you can see, the widget is just hidden if its visible before. If your moving your mouse out too fast, now two things can happen:

    • The widget isnt visible at all
    • The animation is still going on

    In this case the event is discarded and the panel stays visible. As animations are queued, one simply has to remove the if statement to fix the issue. I did this by replacing the whole bindEvents method:

    PrimeFaces.widget.OverlayPanel.prototype.bindEvents =  function() {
        //mark target and descandants of target as a trigger for a primefaces overlay
        this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
    
        //show and hide events for target
        if(this.cfg.showEvent == this.cfg.hideEvent) {
            var event = this.cfg.showEvent;
    
            $(document).off(event, this.targetId).on(event, this.targetId, this, function(e) {
                e.data.toggle();
            });
        }
        else {
            var showEvent = this.cfg.showEvent + '.ui-overlay',
            hideEvent = this.cfg.hideEvent + '.ui-overlay';
    
            $(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
                var _self = e.data;
    
                if(!_self.isVisible()) {
                    _self.show();
                }
            })
            .on(hideEvent, this.targetId, this, function(e) {
                var _self = e.data;
    
                _self.hide();
    
            });
        }
    
        //enter key support for mousedown event
        this.bindKeyEvents();
    
        var _self = this;
    
        //hide overlay when mousedown is at outside of overlay
        $(document.body).bind('mousedown.ui-overlay', function (e) {
            if(_self.jq.hasClass('ui-overlay-hidden')) {
                return;
            }
    
            //do nothing on target mousedown
            var target = $(e.target);
            if(_self.target.is(target)||_self.target.has(target).length > 0) {
                return;
            }
    
            //hide overlay if mousedown is on outside
            var offset = _self.jq.offset();
            if(e.pageX < offset.left ||
                e.pageX > offset.left + _self.jq.outerWidth() ||
                e.pageY < offset.top ||
                e.pageY > offset.top + _self.jq.outerHeight()) {
    
                _self.hide();
            }
        });
    
        //Hide overlay on resize
        var resizeNS = 'resize.' + this.id;
        $(window).unbind(resizeNS).bind(resizeNS, function() {
            if(_self.jq.hasClass('ui-overlay-visible')) {
                _self.hide();
            }
        });
    };
    

    Execute this code on load and the issue should be gone.



    As your replacing the js code nevertheless, you can use this oppurtunity to implement quite a nice feature. By using timeouts in the event handlers one can easily implement a little delay not just improving usability (no more thousands of popups appear) but also reducing network traffic:

            $(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
                var _self = e.data;
    
                _self.timer = setTimeout( function(){
                    if(!_self.isVisible()) {
                        _self.show();
                    }
                }, 300);
            })
            .on(hideEvent, this.targetId, this, function(e) {
                var _self = e.data;
    
                clearTimeout(_self.timer);
                _self.hide();
    
            });
    

    Ofcourse you can use a global variable to control the delay time. If you want a more flexible approach youll have to overwrite the encodeScript method in the OverlayPanelRender to transmit an additional property. You could access it then with _self.cfg.delay. Notice though that youll have to replace the component model OverlayPanel too providing it with an extra attribute.

提交回复
热议问题