views:

58

answers:

4

I have this little jQuery plugin:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&gt;
<html>
<head><title></title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"&gt;&lt;/script&gt;
<script type="text/javascript"><!--
(function($){
    $.fn.foo = function(options){
        var options = $.extend({
            text: "Foo!",
            }, options
        );

        this.prepend(
            $("<span></span>").text(options.text)
        ).css({color: "white", backgroundColor: "black"});

        return this;
    };
})(jQuery);


$(function(){
    $("div").foo().foo({text: "Bar!"}).css({border: "1px solid red"});
});
//--></script>
</head>
<body>

<div>One</div>
<div>Two</div>
<div>Three</div>

</body>
</html>

Now I want to improve it so you are able to control where the text gets inserted by means of providing a callback function:

$(function(){
    var optionsFoo = {
        text: "Foo!",
        insertionCallback: $.append
    }
    var optionsBar = {
        text: "Bar!",
        insertionCallback: $.prepend
    }
    $("div").foo(optionsFoo).foo(optionsBar).css({border: "1px solid red"});
});

(Please remember this is just sample code. I want to learn a technique rather than fix an issue.)

Can I pass a jQuery method as an argument and use it inside the plugin in the middle of a chain? If so, what's the syntax? If not, what's the recommended alternative?

Update: myprogress so far

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&gt;
<html>
<head><title></title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"&gt;&lt;/script&gt;
<script type="text/javascript"><!--
(function($){
    $.fn.foo = function(options){
        var options = $.extend({
            text: "Foo!",
            insertionCallback: $.append
            }, options
        );

        options.insertionCallback.call(this, // options.insertionCallback is undefined
            $("<span></span>").text(options.text)
        ).css({color: "white", backgroundColor: "black"});

        return this;
    };
})(jQuery);


$(function(){
    var optionsFoo = {
        text: "Foo!",
        insertionCallback: $.append
    }
    var optionsBar = {
        text: "Bar!",
        insertionCallback: $.prepend
    }
    $("div").foo(optionsFoo).foo(optionsBar).css({border: "1px solid red"});
});
//--></script>
</head>
<body>

<div>One</div>
<div>Two</div>
<div>Three</div>

</body>
</html>
+1  A: 

Sure you can, JavaScript is really dynamic:

    this.prepend(
        $("<span></span>").text(options.text)
    ).css({color: "white", backgroundColor: "black"});

You can replace the native call to this.prepend() with the jQuery function you pass in the options object as a parameter.

Since most jQuery methods behave on a current set of elements (the this object in your plugin) you need to use apply or call to make it work. This way you can call the options.insertionCallback function with the current this object as its context.

Something like:

    options.insertionCallback.call(this, // pass the current context to the jQuery function
        $("<span></span>").text(options.text)
    ).css({color: "white", backgroundColor: "black"});

Edit

However you can't access jQuery methods directly from the $ namespace, you need a constructed jQuery object to access its methods, or simply use $.fn.functionName as Nick Craver pointed out.

alert($.prepend); // won't alert the function

alert($.fn.prepend); // will alert the function
Luca Matteis
Could you please elaborate your example a little more? I cannot grasp the exact syntax. I get *options.insertionCallback is undefined* no matter what I do.
Álvaro G. Vicario
@Álvaro G. Vicario: That's because $.prepend (or $.append) is undefined. I elaborated a bit more in the answer.
Luca Matteis
You can access the methods directly, `$.fn.prepend` ;)
Nick Craver
Oh right, thanks Nick.
Luca Matteis
That did the trick in my sample code: use `$.fn.prepend` to set and `options.insertionCallback.call(this,...)` to call. I understand the callback execution must normally start the method chain if previous methods change the element selection but it's a reasonable drawback. Nick's string trick is also neat.
Álvaro G. Vicario
+1  A: 

extend it like so:

(function($){
    $.fn.foo = function(options){
        var options = $.extend({
            text: "Foo!",
            cb: null
            }, options
        );

        if($.isFunction(options.cb)){
           // optimal also pass parameters for that callback
           options.cb.apply(this, []);
        }

        this.prepend(
            $("<span></span>").text(options.text)
        ).css({color: "white", backgroundColor: "black"});

        return this;
    };
})(jQuery);

I don't really understand what you mean with

Can I pass a jQuery method as an argument and use it inside the plugin in the middle of a chain?

Why would you want to pass a jQuery method ? it's already in there so you can just call it on any jQuery object.

jAndy
What I mean is being able to do this inside the plugin: `this.insertionCallback($("<span></span>").text(options.text)).css({color: "white", backgroundColor: "black"});`
Álvaro G. Vicario
+1  A: 

You can also do this string based to keep it simple, like this:

(function($){
  $.fn.foo = function(options){
    var options = $.extend({
        text: "Foo!",
        insertionCallback: "append"
        }, options
    );
    return this[options.insertionCallback]($("<span></span>").text(options.text))
               .css({color: "white", backgroundColor: "black"});
  };
})(jQuery);

Then your call looks like this:

$(function(){
  var optionsFoo = {
    text: "Foo!",
    insertionCallback: "append"
  }
  var optionsBar = {
    text: "Bar!",
    insertionCallback: "prepend"
  }
  $("div").foo(optionsFoo).foo(optionsBar).css({border: "1px solid red"});
});​

You can test it here, this would allow you to use append, prepend, html and text, giving you a few options to set the content. In this case we're just taking advantage of JavaScript being a dynamic language, where this.prepend(something) is equal to this["prepend"](something).

Nick Craver
Neat! JavaScript syntax will never stop surprising me...
Álvaro G. Vicario
A: 

Not an answer to your question, but the recommended style is:

<script type="text/javascript">

  //this
  jQuery(function($) {
    //... stuff using the jQuery lib, using the $
  });

  //not this
  $(function() {
    //...
  });

  //or this
  (function($) {
    //...
  })(jQuery);

</script>
Justice
The last one is the "Custom Alias in plugin code" described at http://docs.jquery.com/Plugins/Authoring#Custom_Alias but I take note of the alias in page code, which I was not aware of.
Álvaro G. Vicario
Right, this is for page code. Plugin code should always use the last style example instead of the first.
Justice