【问题标题】:How can I override JQuery's .show() and .hide() to trigger events before and after?如何覆盖 JQuery 的 .show() 和 .hide() 以触发前后的事件?
【发布时间】:2012-05-22 12:39:13
【问题描述】:

我正在尝试覆盖 JQuery 的 .show 和 .hide 方法以在使用以下代码调用它们之前和之后启动触发器事件。

$(document).ready(function () {
    $('#dataBox').bind('afterShow', function () {
        alert('afterShow');
    });
    $('#dataBox').bind('afterHide', function () {
        alert('afterHide');
    });
    $('#dataBox').bind('beforeShow', function () {
        alert('beforeShow');
    });
    $('#dataBox').bind('beforeHide', function () {
        alert('beforeHide');
    });
    $('#toggleButton').click(function(){
        if($('#dataBox').is(':visible')) {
            $('#dataBox').hide ();
        } else {
            $('#dataBox').show();
        }
    });
});

jQuery(function ($) {
    var _oldShow = $.fn.show;
    //Override jquery's 'show()' method to include two triggered events before and after
    $.fn.show = function (speed, oldCallback) {
        return $(this).each(function () {
            var obj = $(this),
                newCallback = function () {
                    if ($.isFunction(oldCallback)) {
                        oldCallback.apply(obj);
                }
                obj.trigger('afterShow');
            };
        obj.trigger('beforeShow');
            _oldShow.apply(obj, [speed, newCallback]);
        });
    }
});

jQuery(function ($) {
    var _oldHide = $.fn.hide;
    //Override jquery's 'hide()' method to include two triggered events before and after
    $.fn.hide = function (speed, oldCallback) {
        return $(this).each(function () {
            var obj = $(this),
                newCallback = function () {
                    if ($.isFunction(oldCallback)) {
                        oldCallback.apply(obj);
                }
                obj.trigger('afterHide');
            };
        obj.trigger('beforeHide');
            _oldHide.apply(obj, [speed, newCallback]);
        });
    }
});

我有以下标记:

<input type='text' id='dataBox'/>
<input type='button' value='toggle' id='toggleButton' />

当我单击切换按钮时,会触发“beforeHide”和“beforeShow”事件,而“afterShow”和“afterHide”则不会。谁能告诉我我做错了什么?

【问题讨论】:

    标签: jquery callback overriding


    【解决方案1】:

    请检查demo fiddle。希望有效

    $.show / $.hide - 覆盖函数:

    (function($){
    $.override ={'show': $.fn.show, 'hide': $.fn.hide};
    
    $.each($.override,function(M,F){
    
            var m=M.replace( /^\w/, function(r){ return r.toUpperCase(); });
    
            $.fn[M] = function(speed, easing, callback) {
    
                var args=[speed||0,easing||'',callback||function(){}];
    
                if( $.isFunction(speed)){
                    args[2]=speed;
                    args[0]=0;
                }                   
                else if( $.isFunction(easing)){
                    args[2]=easing;
                    args[1]='';
                }                   
    
                if(!this.selector){
                    F.apply(this, arguments);
                    return this;
                }
    
                return this.each(function () {
                    var obj = $(this),
                        oldCallback = args[args.length-1],
                        newCallback = function () {
                            if ($.isFunction(oldCallback)){
                                oldCallback.apply(obj);
                            }
                            obj.trigger('after'+m);
                    };
    
                    obj.trigger('before'+m);
                    args[args.length-1]=newCallback;
    
                    //alert(args);
                    F.apply(obj,args);
    
                });
            }
    });
    })(jQuery);
    

    用法

    jQuery(function($){
    var $dataBox=$('#dataBox').bind('beforeHide afterHide beforeShow afterShow', function(e) {
        alert(e.type);
    });
    
    $('#toggleButton').bind('click',function(){
        $dataBox[$dataBox.is(':visible')?'hide':'show'](100,'swing',function(){ alert('end of animation');});
        return false;
    });
    });
    

    【讨论】:

    • 感谢您的努力。顺便说一句,您知道为什么我们必须将默认值传递给 jQuery 的原始 show 函数,例如 duration||0 为什么我们不能在没有持续时间的情况下将其称为 0 只使用 newCallBack?
    • @Joy if ( speed || speed === 0 ) { // 你可以用回调动画 } 如果第一个参数是回调,那么它需要 speed = jQuery.fx.speeds._default = 400 (毫秒);
    • hmm,几天前我发现,我们不能像$.show(undefined,callback)$.show(undefined,undefined,callback) 这样调用它,在这种情况下永远不会调用回调。可能是因为 jQuery 做的 Argument normalization。在探索这个问题的过程中,我学到了一些关于 jQuery 的好东西。
    • @Joy!是的。回调必须是最后一个参数。所以如果速度不通过,jQuery会设置为400ms。
    • 这优雅而高效,正是我想要的,感谢负载 rahen,也感谢乔伊的贡献。
    【解决方案2】:

    我在您的代码中设置了一些断点,发现您的 newCallback 函数永远不会被调用。我认为那是因为您在调用该函数时没有使用速度参数。 从jQuery的原始函数文档$.show可以看出,你可以用noonetwo或`三个参数调用$.show

    .show( 持续时间 [, 回调] )

    duration 一个字符串或数字,确定动画将运行多长时间。

    callback 动画完成后调用的函数。

    添加的版本:1.4.3

    .show( [duration] [,easing] [, callback] )

    duration 一个字符串或数字,确定动画将运行多长时间。

    easing 一个字符串,指示用于过渡的缓动函数。

    callback 动画完成后调用的函数。

    所以你可能会也可能不会传递一堆不同的参数。所以我认为问题出在这一行

     _oldHide.apply(obj, [speed, newCallback]);
    

    在这种特定情况下,speed 未定义。

    你可以像这样调用$.fn.show函数

    $(..).show(undefined,callBack)
    //or
    $(..).show(undefined,undefined, callBack)
    

    如果没有指定其他参数,回调将始终是第一个参数,在这种情况下,jQuery 将使用它的默认 400ms 动画速度/持续时间。你会看到一个淡入/淡出类型的效果。使用此回调使其表现得像显示/隐藏

    你必须打电话给_oldShow/_oldHide 喜欢

    _oldShow.apply(obj, [0,newCallback]);
    
    //or
    
    _oldHide.apply(obj, [0,newCallback]);
    

    在快速搜索中,我在互联网上找到了一个代码,它似乎工作正常,并且对您可以传递给 $.show$.hide 的所有参数都有特殊处理。检查下面的小提琴来看看

    Working Fiddle

    【讨论】:

    • 您好,感谢您的回复。我见过这个解决方案,但我对它的抱怨是,即使没有将速度发送到“show()”方法,它仍然在做动画。这是为什么呢?
    • 小提琴链接,即使没有被调用,它似乎总是执行缓慢的显示/隐藏动画。我在firefox上看
    • 我不知道,但在我每次调用“显示”和“隐藏”时都有自定义代码之前,没有 400 毫秒的动画,它只是正常打开和关闭它们。
    • 你确定你说的不是'fade'?
    • 哎呀!对不起,我在想fade,也许,我的错。没有speed 参数show 就像display:blockhide 就像display:none
    【解决方案3】:

    为什么没有触发 afterShowafterHide 事件

    如果显示/隐藏动画,jQuery 只会调用回调函数(代码中的newCallback)。如果.show()/.hide()没有传入参数(或者speedundefined),jQuery会直接显示/隐藏元素,回调函数不会被执行。

    为什么覆盖 .show() / .hide() 不是最好的主意

    • jQuery 在内部调用.show() / .hide()(例如,作为.fadeTo() 的一部分)和递归调用(在进行动画显示/隐藏时),因此您的自定义事件将在您不期望的时候触发他们去。

    • 恕我直言,最好将“库存”jQuery 和自定义(作为插件)分开,使用单独的方法名称/命名空间:

      • 当您的开发人员在 http://api.jquery.com 中查找库存方法时,他们会知道文档完整地描述了这些方法的行为。
      • 如果您的开发人员使用其他地方的代码,他们不会调用常规方法并期望特定于您公司代码库的行为。

    .eventedShow() / .eventedHide()

    这增加了.eventedShow().eventedHide()(它采用与.show() / .hide() 相同的参数)并且应该做你想做的事:

    (function( $ ) {
    
    $.each( [ 'Show', 'Hide' ], function( i, method ) {
        $.fn[ 'evented' + method ] = function( speed ) {
            var originalMethod = method.toLowerCase(),
                hasCallback = false,
                args;
    
            // animate
            if ( speed || speed === 0 ) {
                args = $.makeArray( arguments );
                $.each( args, function( i, arg ) {
                    if ( $.isFunction( arg ) ) {
                        args[ i ] = function() {
                            arg.apply( this, arguments );
                            $( this ).trigger( 'after' + method );
                        };
                        hasCallback = true;
                        return false;
                    }
                } );
                if ( !hasCallback ) {
                    args.push( function() {
                        $( this ).trigger( 'after' + method );
                    } );
                }
    
                this.each( function() {
                    var el = $( this );
                    el.queue( function() {
                        $( this )
                            .trigger( 'before' + method )
                            .dequeue();
                    } );
                    $.fn[ originalMethod ].apply( el, args );
                } );
    
            // straight show/hide
            } else {
                this.trigger( 'before' + method );
                $.fn[ originalMethod ].apply( this, arguments );
                this.trigger( 'after' + method );
            }
    
            return this;
        };
    } );
    
    })( jQuery );
    

    您可以在这里尝试代码:http://jsfiddle.net/jefferyto/bv8D2/

    您仍然需要在现有代码中将 show / hide 更改为 eventedShow / eventedHide,但这可以通过全局搜索和替换来完成。

    【讨论】:

    • 杰弗里!伟大的。但问题是如果您将第一个参数作为回调传递,动画需要 400 毫秒才能完成。威廉要求解决这个问题。
    • @rahen 如果您想在直接显示/隐藏后执行代码,请直接执行,即 el.show();打回来();无需将其作为回调函数传递。
    • !你的仪式。顺便说一句,我已经修改了你的代码来满足威廉的要求。请检查demo fiddle
    猜你喜欢
    • 1970-01-01
    • 2012-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-22
    • 2010-09-19
    相关资源
    最近更新 更多