9

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 avatar a p:overlayPanel appears with more information (lazy loaded) on the user, and disappears when the user moves the cursor away - like this:

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

This works very well - as long as the user is "slowhanded". Whenever an user moves the cursor fast above many avatars many of the overlayPanels stay visible. For example when the user has the cursor over the position where user avatars are displayed and uses the scroll wheel of his mouse to scroll the usertable down or up.

I believe that the overlaypanel starts to load the information dynamically (dynamic="true") from the server when showEvent="mouseover" is dispatched and displays the overlaypanel after the response from the server arrives. This way it is not possible to detect whether the cursor is already away when the overlaypanel becomes visible - so the hideEvent="mouseout" is never dispatched.

Is there a way to make the primefaces overlaypanel appear directly on mousover, showing a loading gif and update the content into the overlaypanel when the response comes from the server.

Is this a good appraoch or does anyone know any other way to solve this nasty problem?

Thanks Pete

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

同時に、この素​​晴らしいソリューションに感謝します。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 に答える
2

うわー、ついに長いデバッグセッションとさまざまなアプローチのテストの後、問題はajaxリクエストではなく、イベントハンドラー自体にあることに気付きました:

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

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

ご覧のとおり、以前に表示されていたウィジェットは非表示になっています。マウスの移動が速すぎると、次の 2 つのことが発生する可能性があります。

  • ウィジェットがまったく表示されない
  • アニメ化はまだまだ続きます

この場合、イベントは破棄され、パネルは表示されたままになります。アニメーションはキューに入れられるので、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.delayOverlayPanelただし、追加の属性を指定して、コンポーネント モデルも置き換える必要があることに注意してください。

于 2012-08-14T21:53:28.280 に答える