How to pass options objects as parameters to method set at second parameter of jQuery()?

后端 未结 1 1496
情话喂你
情话喂你 2020-12-21 17:10

At jQuery() :

Important: If the second argument is passed, the HTML string in the first argument must represent a simple element with no attributes.

相关标签:
1条回答
  • 2020-12-21 17:54

    The default duration for the animate method is 400ms. And if you run the first snippet you posted, you'll see the CSS is animated for that short period (0.4s).

    But a specific value for duration can only be passed via the second argument to animate in all available jQuery method signatures.

    jQuery has no support for passing more than one argument to functions specified as properties in the plain object that is passed as second argument to jQuery().

    This can be seen from this code snippet taken from the jQuery v1.12.0 sources, near line 2912:

    // HANDLE: $(html, props)
    if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
        for ( match in context ) {
    
            // Properties of context are called as methods if possible
            if ( jQuery.isFunction( this[ match ] ) ) {
                this[ match ]( context[ match ] );   // <-------------
    
            // ...and otherwise set as attributes
            } else {
                this.attr( match, context[ match ] );
            }
        }
    }
    

    So there is no way to pass a duration to .animate in that way, and so -- in the case of .animate -- the default duration of 400ms will apply.

    Workaround 1: override default value

    There is of course the option to change the default duration to the desired duration and restore it right after the $(html, plainobject) call:

    $.fx.speeds._default = 5000; // change default
    var div = $("<div></div>", {
        animate: {
            fontSize:"100px",
        },
        text: "animated text",
    });
    $.fx.speeds._default = 400; // restore;
    
    $("body").append(div);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    But then you can better chain the animate method:

    Workaround 2: chain the animate call

    var div = $("<div></div>", {
        text: "animated text",
    }).animate({
        fontSize:"100px",
    }, 5000);
    
    $("body").append(div);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    Workaround 3: create a plug-in

    You could also define a plug-in that will accept an array of arguments for each method, instead of just one argument:

    $.fn.multiargs = function(arg) {
        for (key in arg) {
            $.fn[key].apply(this, arg[key]);
        }
    };
    

    Now you can use the 2nd-arg-object to do it all, using arrays as values for the method-properties:

    $.fn.multiargs = function(arg) {
        for (var key in arg) {
            $.fn[key].apply(this, arg[key]);
        }
        return this;
    };
    
    var div = $("<div></div>", {
        multiargs: {
            text: ["animated text"],
            animate: [{
                fontSize:"100px",
            }, {
                duration: 5000,
                done: function() {
                    $(this).text('Done');
                }
            }]
        }
    });
    
    $("body").append(div);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    Workaround 4: patch jQuery library

    If the jQuery code quoted at the start of my answer would be modified so that this line:

            this[ match ].apply(this, context[ match ]);
    

    ...was replaced by this:

            if ( jQuery.isArray( context[ match ] ) ) {
                this[ match ].apply(this, context[ match ]);
            } else {
                this[ match ]( context[ match ] );
            }
    

    Then you could write this:

    var div = $("<div></div>", {
        text: "animated text",
        animate: [{
            fontSize:"100px",
        }, {
            duration: 5000,
            done: function() {
                $(this).text('Done');
            }
        }]
    });
    

    And you would get the same results as in workaround 3.

    Note however that it might have undesired effects when a jQuery method really needs to get an array as the first argument. So this code would need some more tweaking to deal with such situations correctly.

    Also note that if you would use a modified version of jQuery, you'll need to reapply that change whenever you want to upgrade to a newer jQuery version.

    Workaround 5: Redefine $.fn.init at run-time

    You could replace $.fn.init at run-time, and for all other functionality it provides you could rely on the original version of it:

    var prev_$_init = $.fn.init;
    var init = jQuery.fn.init = function( selector, context, root ) {
        var match, elem,
            // redefine regexes that are private to jQuery:
            rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
            rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ );
    
        // Handle HTML strings
        if ( typeof selector === "string" ) {
            if ( selector[ 0 ] === "<" &&
                selector[ selector.length - 1 ] === ">" &&
                selector.length >= 3 ) {
    
                // Assume that strings that start and end with <> are HTML and skip the regex check
                match = [ null, selector, null ];
    
            } else {
                match = rquickExpr.exec( selector );
            }
    
            // Match html or make sure no context is specified for #id
            if ( match && ( match[ 1 ] || !context ) ) {
    
                // HANDLE: $(html) -> $(array)
                // Patch: do not treat jQuery object as context here:
                if ( match[ 1 ] && !(context instanceof jQuery)) {
                    // Option to run scripts is true for back-compat
                    // Intentionally let the error be thrown if parseHTML is not present
                    // Patch: simplify this call, as context is not jQuery:
                    jQuery.merge( this, jQuery.parseHTML(
                        match[ 1 ],
                        document,
                        true
                    ) );
    
                    // HANDLE: $(html, props)
                    if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
                        for ( match in context ) {
    
                            // Properties of context are called as methods if possible
                            if ( jQuery.isFunction( this[ match ] ) ) {
                                // Patch:
                                if ( jQuery.isArray( context[ match ] ) ) {
                                    this[ match ].apply(this, context[ match ]);
                                } else {
                                    this[ match ]( context[ match ] );
                                }
    
                            // ...and otherwise set as attributes
                            } else {
                                this.attr( match, context[ match ] );
                            }
                        }
                    }
    
                    return this;
                }
            }  
        }
        // Patch: forward call to original fn.init
        return prev_$_init.apply(this, arguments);
    };
    init.prototype = jQuery.fn;
        
    var div = $("<div></div>", {
        text: "animated text",
        animate: [{
            fontSize:"100px",
        }, {
            duration: 5000,
            done: function() {
                $(this).text('Done');
            }
        }]
    });
    
    $("body").append(div);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

    This works, but this copies quite some code from the original $.fn.init code and needs to redefine some private variables which the jQuery library defines outside of the $.fn.init method. I marked in comments with "Path" where I modified the original code.

    It is clear that $.fn.init was not designed to be overruled like this.

    Concluding, I feel the downsides of this approach are more important than the advantages it brings over workaround 3.

    0 讨论(0)
提交回复
热议问题