Primefaces overlayPanel的延迟问题 - 加载到延迟

时间:2012-08-10 11:46:30

标签: jsf-2 primefaces

我使用的是Primefaces 3.2和jsf 2以及glassfish 3.1.2。

我有一个p:dataTable用户包含用户的头像。每当用户将鼠标移动到头像上时,p:overlayPanel会在用户上显示更多信息(延迟加载),并在用户将光标移开时消失 - 如下所示:

<p:overlayPanel for="avatar" dynamic="true" showEvent="mouseover" hideEvent="mouseout" ...>

这非常有效 - 只要用户“慢速”。每当用户将光标快速移动到许多化身上方时,许多overlayPanel都会保持可见。 例如,当用户将光标悬停在显示用户头像的位置时,并使用鼠标滚轮向下或向上滚动用户。

我相信在调度dynamic="true"时,overlaypanel会从服务器动态加载信息(showEvent="mouseover"),并在服务器响应到达后显示overlaypanel。 这样,当overlaypanel变得可见时,无法检测光标是否已经离开 - 因此永远不会调度hideEvent="mouseout"

有没有办法让primefaces overlaypanel直接显示在mousover上,显示加载gif并在响应来自服务器时将内容更新到overlaypanel。

这是一个很好的appraoch还是有人知道解决这个令人讨厌的问题的其他方法吗?

谢谢皮特

4 个答案:

答案 0 :(得分:5)

由于我的第一个答案已经很长并且包含有效信息,我决定打开一个新的答案,展示我的最终方法。

我现在使用Primefaces继承模式,使代码更清晰。另外我注意到替换/覆盖整个bindEvents函数是不必要的,因为我们可以删除旧的事件处理程序。最后,这段代码修复了最新的问题:ajax到来之前的隐藏事件。

PrimeFaces.widget.OverlayPanel = PrimeFaces.widget.OverlayPanel
        .extend({

            bindEvents : function() {
                this._super();

                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;

                            clearTimeout(_self.timer);
                            _self.timer = setTimeout(function() {
                                _self.hidden = false;
                                _self.show();
                            }, 300);
                        }).on(hideEvent, this.targetId, this, function(e) {
                    var _self = e.data;

                    clearTimeout(_self.timer);
                    _self.hidden = true;
                    _self.hide();

                });
            },

            _show : function() {
                if (!this.cfg.dynamic || !this.hidden) {
                    this._super();
                }
            }

        });

我很抱歉格式不佳:日食错误;)

答案 1 :(得分:2)

哇,最后经过长时间的调试和测试各种方法后,我认识到问题不是ajax请求,而是事件处理程序本身:

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

            if(_self.isVisible()) {
                _self.hide();
            }
        });

正如您所看到的,如果小部件之前可见,则该小部件将被隐藏。如果您将鼠标移出太快,现在可能会发生两件事:

  • 小部件根本不可见
  • 动画仍在继续

在这种情况下,事件被丢弃,面板保持可见。当动画排队时,只需删除if语句即可解决问题。我通过替换整个bindEvents方法来完成此操作:

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();
        }
    });
};

在加载时执行此代码,问题应该消失。



尽管你替换了js代码,你可以利用这个机会来实现一个很好的功能。通过在事件处理程序中使用超时,可以轻松实现一点延迟,不仅可以提高可用性(不会出现数千个弹出窗口),还可以减少网络流量:

        $(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();

        });

当然,您可以使用全局变量来控制延迟时间。如果您想要更灵活的方法,则必须覆盖encodeScript中的OverlayPanelRender方法以传输其他属性。您可以使用_self.cfg.delay访问它。请注意,您还必须替换组件模型OverlayPanel,同时为其提供额外的属性。

答案 2 :(得分:2)

与此同时,我感谢您提供这个出色的解决方案,我借此机会为Primefaces 5.2更新它。在我们的应用程序中,代码在升级后破坏了。

按照 Primefaces 5.2 的更新代码:

    PrimeFaces.widget.OverlayPanel.prototype.bindTargetEvents =  function() {
    var $this = this;
    //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;

        this.target.on(event, function(e) {
            $this.toggle();
        });
    }
    else {
        var showEvent = this.cfg.showEvent + '.ui-overlaypanel',
        hideEvent = this.cfg.hideEvent + '.ui-overlaypanel';

        this.target
            .off(showEvent + ' ' + hideEvent)
            .on(showEvent, function(e) {
                clearTimeout($this.timer);

                $this.timer = setTimeout(function() {
                    $('.ui-overlaypanel').hide(); 
                    $this.hidden = false;
                    $this.show();
                }, 500);
            })
            .on(hideEvent, function(e) {

                clearTimeout($this.timer); 

                $this.timer = setTimeout(function() {
                    // don't hide if hovering overlay
                    if(! $this.jq.is(":hover")) {
                        $this.hide();
                    }
                }, 100);
            });
    }

    $this.target.off('keydown.ui-overlaypanel keyup.ui-overlaypanel').on('keydown.ui-overlaypanel', function(e) {
        var keyCode = $.ui.keyCode, key = e.which;

        if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
            e.preventDefault();
        }
    })
    .on('keyup.ui-overlaypanel', function(e) {
        var keyCode = $.ui.keyCode, key = e.which;

        if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
            $this.toggle();
            e.preventDefault();
        }
    });
};

我还添加了一个额外的功能,允许用户将鼠标移到叠加层上而不隐藏它。当你将鼠标移出它时它应该隐藏,然后我完成了它:

<p:overlayPanel .... onShow="onShowOverlayPanel(this)" ...>

function onShowOverlayPanel(ovr) {
    ovr.jq.on("mouseleave", function(e) {
        ovr.jq.hide();
    });
}

希望你喜欢!

答案 3 :(得分:0)

已经很长时间了,但是如果有人碰到这个问题,可以在showDelay中添加一个overlayPanel属性来解决此问题starting from Primefaces 6.2。但是,由于某种原因,它不在官方文档中。