1

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"?
3
  • See stackoverflow.com/questions/32919441/… Commented Oct 7, 2015 at 18:19
  • This prototype pattern might help markdalgleish.com/2011/05/… Commented Oct 7, 2015 at 18:25
  • Regarding my updated sample: I realize that in the end, all it's doing is something that jQuery already does (moving an item from point A to point B). In the production code, there is a placeholder and some other housekeeping involving dynamically-loaded content that makes more sense than just the above. I'm after the pattern, at which time all of those other details are easily slotted in. Commented Oct 8, 2015 at 3:22

1 Answer 1

1

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>

Sign up to request clarification or add additional context in comments.

5 Comments

The second one fits my brain patterns better (ie. I can grok it) but chaining a method into the next isn't quite the nice syntax of the first. I will try wrapping my head around the first, though, since it fits my needs best!
Regarding the first one (which I'm working on wrapping my head around), why is that second call wrapped in a setTimeout? I know that setTimeout can be used to break synchronous calls, but is there a synchronous call being made that's impeding functionality? Also wrt that first one, what is the bind doing? Is that a way of making sure the nested function (once set as a jQuery plugin) has a correct "this" to work with?
@GregPettit "Regarding the first one (which I'm working on wrapping my head around), why is that second call wrapped in a setTimeout?" Was not certain if expected result .switchElement was object , instead of anonymous function ; setTimeout overwrites first call to function switchElement with object switchElement from this portion of OP "trying to invoke via something like ($(selector).switchElement.in();" . Yes, .bind() developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… sets this of in property of inner object
Whether I fully grok the first one or not, the second one definitely works and I will eventually get around to the first. Thanks for the time you put into this, it's appreciated! Going to learn more about .bind() now in general. Seems like a glaring oversight to not have learned it yet, now that I'm viewing the page you sent me to!
I couldn't help giving it an honest attempt. See edits in my original question for my attempt, a fiddle, and a (broken) test scenario of how I wish the syntax worked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.