How can I override JQuery's .show() and .hide(

2019-05-15 09:48发布

I am tryign to override the JQuery's .show and .hide methods to launch trigger events before and after they are called with the following code.

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

I have the following Markup:

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

When I click the toggle button the 'beforeHide' and 'beforeShow' events are triggering while the 'afterShow' and 'afterHide' aren't. Can anyone clue me in as to what I am doing wrong?

3条回答
\"骚年 ilove
2楼-- · 2019-05-15 10:29

Why the afterShow and afterHide events are not being triggered

jQuery will only call the callback function (newCallback in your code) if the show / hide is animated. If no parameters are passed to .show() / .hide() (or if speed is undefined), then jQuery will show / hide the element(s) directly, and the callback function will not be executed.

Why overriding .show() / .hide() isn't the best idea

  • jQuery calls .show() / .hide() internally (e.g. as part of .fadeTo()) and recursively (when doing an animated show / hide), so your custom events will be triggered when you may not be expecting them to.

  • IMHO it's best to keep "stock" jQuery and customizations separate (as plugins), with separate method names / namespaces:

    • When your developers look up stock methods in http://api.jquery.com, they'll know that the documentation describes the methods' behaviour completely.
    • If your developers work on code from elsewhere, they won't call stock methods and expect behaviour that's specific to your company's codebase.

.eventedShow() / .eventedHide()

This adds .eventedShow() and .eventedHide() (which takes the same arguments as .show() / .hide()) and should do what you want:

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

You can try the code here: http://jsfiddle.net/jefferyto/bv8D2/

You'd still need to change show / hide to eventedShow / eventedHide in your existing code, but this can be done with a global search-and-replace.

查看更多
劳资没心,怎么记你
3楼-- · 2019-05-15 10:36

I set some breakpoints in your code and saw that you newCallback function is never being called. Thats I think because you are not using the speed param when you are calling that function. As you can see from jQuery's original function's doc $.show, you can call $.show with no, one, two or `three parameters

.show( duration [, callback] )

durationA string or number determining how long the animation will run.

callbackA function to call once the animation is complete.

version added: 1.4.3

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

durationA string or number determining how long the animation will run.

easingA string indicating which easing function to use for the transition.

callbackA function to call once the animation is complete.

So you you may or may not pass a bunch of different parameters. So I think the problem is in this line

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

Where in this specific case speed is undefined.

And you can call $.fn.show function like

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

The callback will always have to be the first argument if no other argument is specified and in that case jQuery will use it's default 400ms speed/duration for the animation. And you will see a fadeIn/fadeOut type of effect. To make it behave like show/hide with this callback

You have to call _oldShow/_oldHide like

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

//or

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

In a quick Search I found a a code in the internet which seems to work fine and have special handling for all arguments you can pass to $.show and $.hide . Check the following fiddle to have a look at that

Working Fiddle

查看更多
时光不老,我们不散
4楼-- · 2019-05-15 10:45

plz check the demo fiddle . hope it works

$.show / $.hide - override function:

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

usage

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;
});
});
查看更多
登录 后发表回答