9

我将 Primefaces 3.2 与 jsf 2 和 glassfish 3.1.2 一起使用。

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

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

这工作得很好——只要用户是“慢手”。每当用户在许多头像上方快速移动光标时,许多叠加面板都会保持可见。例如,当用户将光标悬停在显示用户头像的位置并使用鼠标的滚轮向下或向上滚动用户表时。

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

有没有办法让 primefaces 覆盖面板直接出现在鼠标悬停上,显示加载 gif 并在响应来自服务器时将内容更新到覆盖面板中。

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

谢谢皮特

4

4 回答 4

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

        });

我很抱歉格式不好:Eclipses fault ;)

于 2012-08-15T17:22:02.820 回答
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,为其提供额外的属性。

于 2012-08-14T21:53:28.280 回答
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();
    });
}

希望你喜欢!

于 2015-09-02T21:08:46.287 回答
0

已经很久了,但万一有人遇到这个问题,从 Primefaces 6.2 开始showDelay添加了一个属性overlayPanel来解决这个问题。但是,由于某种原因,它不在官方文档中。

于 2020-09-01T01:22:06.263 回答