简体   繁体   中英

nesting functions for jQuery plugin

Is it possible to author a jQuery plugin such that the syntax uses a dot-notated nested function rather than requiring a parameter to be passed in?

For example:

$.fn.switchElement = {};

$.fn.switchElement.in = function() {
  console.log(this); // logs $.fn.switchElement as an object containing a function called 'in'
  this.attr('id'); // TypeError: this.attr is not a function (naturally; the above line shows us that it's not a jQuery object)
}

As the comment mentions, trying to invoke via something like ( $(selector).switchElement.in(); does not work because the reference to this is not what one needs for writing a jQuery plugin; namely, this needs to be an incoming jQuery object.

I'll be the first to admit, this is purely syntax, and the plugin would be perfectly functional accepting an "in" parameter for a $.fn.switchElement function instead.

That said, is there a pattern I can use to accomplish my goal?


[edit]

I gave one of the suggestions below a try... admittedly not fully understanding it. Here's my best attempt at understanding it:

A function that will become the plugin is defined, called "switcher" which accepts an argument. In my case, I intend to pass a jQuery object which is a target, so I call mine "$target".

Within switcher is what will be the nested function, called (for now) switchIn() . Because it is within the scope of switcher , it has access to the same this . I sanity check that I have a $target and then I append this (a jQuery object) to the target before returning this to facilitate chaining.

With the function now declared, we move it into an object arbitrarily called "inner". Within "inner", we set the function to() . to() is a function that is essentially switchIn() but bound with the this that's in scope, along with the $target).

The first setTimeout I don't fully understand still. But within it, the inner (or nest of functions) becomes the plugin called "switchElement".

At the end of switcher , this is returned, which again allows for chaining.

The next $.fn[pluginName] I don't understand either. Didn't I already set a plugin called "switchElement" which contains a function called "to"?

Finally is the extra interesting part. First I initialize the plugin by calling it chained to a jQuery object and passing in my target. This allows the this and the $target to be set in a closure analogy? Does this mean that $target is always going to be the target until I re-initialize?

In the provided sample code, I am also including an additional button click that shows the syntax I wish I could use, without initialization. Just $(selector).switchElement.to($(target)) . This is all done up as a fiddle, too: http://jsfiddle.net/comrhqr4/1/

(function($) {

  var pluginName = "switchElement";

  var switcher = function switcher($target) {

    var switchIn = function switchIn($target) {
        console.log('invoking fn and sanity checking $target, which is ', $target);
      if ($target) {
        this.appendTo($target);
      }
      return this
    }

    var inner = {
      "to": switchIn.bind(this, $target)
    };

    setTimeout(function() {
      $.fn[pluginName] = inner
    })

    return this
  };

  $.fn[pluginName] = switcher;

    $(document).on('click', '#switchTo1', function() {
        console.log('clicked #switchTo1');
      $('#unique').switchElement($('#container1'));
        setTimeout(function() {
          $('#unique').switchElement.to().css('color', 'blue');
        })
    });

    $(document).on('click', '#switchTo4', function() {
        console.log('clicked #switchTo4');
      $('#unique').switchElement.to($('#container4'));
    });

}(jQuery));

So, some remaining questions:

  1. Is it a correct assumption that $target is now set until a re-initialization?
  2. Is there any way to achieve the syntax desired in my "button 4 press"?

Try utilizing Function.prototype.bind() , setTimeout() ; Note, requires initializing plugin first with call to $(element).pluginName([args])

 (function($) { var pluginName = "switchElement"; var Foo = function Foo(args) { var fn = function fn(args) { console.log(this, this.attr("id")); if (args) { this.html(args) } return this } var inner = { "in": fn.bind(this, args) }; setTimeout(function() { $.fn[pluginName] = inner }) return this }; $.fn[pluginName] = Foo; }(jQuery)); $("body").switchElement("123"); setTimeout(function() { $("body").switchElement.in(); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"> </script> <body id="abc"> </body> 


Alternatively, using $.extend()

 var switchElement = { in : function() { console.log(this, this.attr("id")) } } $.fn.switchElement = function() { return $.extend({}, this, switchElement) }; $("body").switchElement().in() 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"> </script> <body id="abc"></body> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM