Externally call function inside this.each(function(){}) from a jquery plugin - javascript

(function( $ ){
$.fn.foo = function(params) {
params = $.extend( {
on: false
}, params);
this.each(function(){
if(params.on) {
function alertDate(){
alert('FOO BAR!!!');
}
}
});
};
})( jQuery );
How can i access the "alertDate()" function from out of the script?
if i use:
$('#test').foo()
will give-me access for the function, ok, everything fine, i will gain access to function "alertDate()" internally at this.each(function(){}).
I want to access the function "alertDate()" externally by something like:
$('#text').foo({on: 'true'}).each().alertDate();
How can i do this?
Thanks in advance and sorry for the poor english

Here's how I would handle it, so that your alertDate() function can be used both internally and externally:
$.fn.foo = function(params) {
params = $.extend( {
on: false
}, params);
this.alertDate = function(){
alert("FOO BAR!!!");
}
this.each(function(){
if(params.on) {
this.alertDate();
}
});
return this;
};
//Externally the alertDate function could be called as:
$("test").foo().alertDate();

You can do something like this, but this is not how plugins normally work.
$.fn.foo = function() {
function bar() {
alert("bar");
}
this.bar = bar;
return this;
};
$("a").foo().bar();

Related

Wrap jQuery plugin with a function

Let’s say I have following simple plugin:
(function ( $ ) {
$.fn.greenify = function() {
this.css( "color", "green" );
return this;
};
}( jQuery ));
I don’t want to make any changes to the plugin itself, but I would like to wrap with another function and extend its namespace as follow:
(function($) {
$.fn.myFunction = function () {
(function ($) {
$.fn.greenify = function () {
this.css("color", "green");
return this;
};
}(jQuery));
};
})(jQuery);
So I can call the plugin function like this:
$(selector). myFunction().greenify();
Basically I want to disable calling ‘greenify’ function directly.
Is it possible?
It's not clear from the question, but I'm assuming the simple plugin "greenify" is a third-party or other "forced to use" plugin that you, for whatever reason, can't change. Let's also assume that it's actually quite a complicated plugin and simplified for the sake of the question.
This means
you can't change it
you can't duplicate the entire plugin inside your wrapper
The usual method for overwriting something is to take a copy, then make the new version do what you want, possibly calling the old version, eg:
var oldfoo = foo;
foo = function() {
alert("foo called");
oldfoo(); // or oldfoo.apply(this) to be clearer
}
The same principle can be applied here, but instead make 'foo' (in the example above) null - to get rid of it, eg:
var oldfoo = foo;
newfoo = function() {
alert("foo called");
oldfoo(); // or oldfoo.apply(this) to be clearer
}
foo = null;
The complication is with jquery and wanting to keep the method chaining, which can be achieved by storing 'this' and applying it as desired.
Here's the full code with explanation comments:
// The original plugin to be wrapped
(function ( $ ) {
$.fn.greenify = function() {
// changed to background-color for more impact (demo purposes)
this.css( "background-color", "lightgreen" );
return this;
};
}( jQuery ));
(function($) {
// allow this to be referred to later
// inside another loop where 'this' is something else
var me = this;
// take a copy of the original
// this stays inside the scope of (function($)) so can't be seen outside
me.original_greeny = $.fn.greenify;
// provide a wrapper
$.fn.myFunction = function () {
// the jquery elements for applying later
var $this = $(this)
// exported function
return {
greenify: function() {
// Call the original function with the jquery elements
// and return them for chaining
return me.original_greeny.apply($this)
}
};
};
})(jQuery);
// Now remove the original completely
(function ( $ ) {
$.fn.greenify = null;
}(jQuery));
// As desired, also demonstrating chaining still works
$("#a").myFunction().greenify().css("font-style", "italic")
// Confirm that the original has been removed
// gives error $(...).greenify is not a function
try {
$("#a").greenify()
} catch(e) {
$("#a").text("error on $().greenify: " + e)
}
and a jsfiddle
If you want to create your own context, one way is to return an object with a set of functions:
(function($) {
$.fn.myFunction = function () {
// Cache jQuery object
var $this = this;
// Return interface that acts on jQuery object
return {
greenify: function () {
$this.css("color", "green");
return $this;
}
};
};
})(jQuery);
Then you could call it using:
$(selector).myFunction().greenify();
Fiddle
Edit: As a warning though, when you do this you are leaving the jQuery chaining context after calling .myFunction, which can be very confusing in code.
You definitely don't want to take the current shown approach, because every time myFunction is called, it will also assign greenify. Simply take your myFunction plugin, assign a flag to the jQuery object that was constructed, and then check for that flag in greenify.
(function($) {
$.fn.myFunction = function () {
this.greenMarker = true;
return this;
};
$.fn.greenify = function () {
if(!this.greenMarker) return this;
this.css("color", "green");
return this;
};
})(jQuery);
$('.f').greenify();
$('.g').myFunction().greenify();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="f">No Green</div>
<div class="g">Green Me</div>
I think you're talking about method chaining which is the beauty of jQuery methods. Define your new method as follows, but leave greenify() unchanged:
(function($) {
$.fn.myFunction = function () {
return this.each(function() {
//Do stuff
});
};
})(jQuery);
(function ( $ ) {
$.fn.greenify = function() {
this.css( "color", "green" );
return this;
};
}( jQuery ));
(function($) {
$.fn.myFunction = function () {
return this.each(function() {
$(this).css({border:'1px solid black',textAlign:'center'});
});
};
})(jQuery);
$('.box').myFunction().greenify();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box">COLOR CHANGE</div>
Pretty sure so long as your myFunction returns this, that you should be able to chain whatever you want to it.
(function ( $ ) {
$.fn.greenify = function() {
this.css( "color", "green" );
return this;
};
}( jQuery ));
(function($) {
$.fn.myFunction = function () {
// Do some stuff
return this;
};
})(jQuery);
$(selector).myFunction().greenify();
EDIT: If the whole point is to "disable calling greenify directly", why would you extend jQuery in the first place? 'greenify could be nested and triggered by passing an argument of some kind. Something like:
(function($) {
$.fn.myFunction = function (options) {
// Do some stuff
if (options.greenify) {
this.css("color", "green");
}
return this;
};
})(jQuery);
$(selector).myFunction({greenify: true});
... or you could just define greenify as a function (instead of a plugin) and call it. But the point of defining plugins is so they can then be called globally.

Variable scope in a jQuery plugin?

I continue a question, what I previously asked. It was a too simple sample :)
There is a plugin, what's syntax often used nowadays. I want to create an element at the initialization, and I want access to it from another methods. In the example below, I put it's declaration pretty deeply, but if I put it outside the init method, the open method drop an error, because it can't find it.
(I'm not sure am I call the methods from another in the right way...)
(function($, window, document, undefined) {
var opt = {
text : 'sample'
};
var methods = {
init: function(options) {
var self = $(this);
if (options) {
$.extend(opt,options);
}
return this.each(function () {
var container = $('<div class="container" />');
container.text(opt.text);
$(this).append(container);
$(this).click(function() {
self.pluginName('open');
});
});
},
open: function(args) {
container.text('opened');
},
submit: function(args) {
// call another method...
}
};
jQuery.fn.pluginName = function (method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
}
else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
}
else {
$.error('Method ' + method + ' does not exist on jQuery.pluginname');
}
};
})(jQuery, window, document);
I would like to call it any of number element, like this:
$(function() {
$('div').pluginName();
});

jQuery plugin boiler plate - private method with bound scope?

I've been looking at the plugin boiler plate for jQuery plugins, I find it ok but there is one major flaw in the design or maybe just something I can't figure out.
When I author plugins at the moment, it is easy for me to define publicly exposed methods and private methods that only the plugin has access to.
When I tried to do something similar in the boiler plate I was thwarted.
;(function ( $, window, document, undefined ) {
// Create the defaults once
var
pluginName = "defaultPluginName",
defaults = {
propertyName: "value"
};
// The actual plugin constructor
function Plugin ( element, options ) {
this.element = element;
this.settings = $.extend( {}, defaults, options );
this.defaults = defaults;
this.name = pluginName;
this.init();
}
Plugin.prototype.init = function() {
console.log('init')
console.log(this)
this.yourOtherFunction();
}
Plugin.prototype.yourOtherFunction = function () {
console.log('yourOtherFunction')
console.log(this)
this.yourOtherFunction2();
}
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction().bind(this)
}
var privateFunction = function() {
console.log('private')
console.log(this)
}
// A really lightweight plugin wrapper around the constructor,
// preventing against multiple instantiations
$.fn[ pluginName ] = function ( options ) {
return this.each(function() {
if ( !$.data( this, "plugin_" + pluginName ) ) {
$.data( this, "plugin_" + pluginName, new Plugin( this, options ) );
}
});
};
})( jQuery, window, document );
$(document).defaultPluginName()
Anyway you can see the function 'privateFunction' it's scope is to the window object, but what I want to be able to do is scope it to the Plugin instance, or basically 'this' from the prototype methods.
What I don't want to do, is pass the scope into each private function as a function argument!
So how can I bind the scope?
Console output
init
Plugin { element=document, settings={...}, defaults={...}, more...}
yourOtherFunction
Plugin { element=document, settings={...}, defaults={...}, more...}
private
Window index.html <-- I want Plugin, not window
You are calling privateFunction and then binding this as scope for its result. So use (as said by #Khanh_TO):
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction.apply(this,arguments);
}
Instead of:
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction().bind(this)
}
More details:
bind returns a copy of the function on which is called (the result of privateFunction in your case) after applying the scope you've passed in (this in your case). What bind does is something like:
Function.prototype.bind = function(scope) {
var _function = this;
var _args = [];
for (var i = 0, len = arguments.length-1; i < len; i++){ _args[i] = arguments[i+1]; }
return function() {
// returns the same function on which is called (not the same Function object, but
// another with same properties) with 'this' equal to the first parameter and
// the remaining specified parameters as parameters of the function returned
return _function.apply(scope, _args);
}
}
eg. myFunction.bind(newScope, param1, param2, ...) -> returns an anonymous function which in turns returns the function myFunction(param1, param2,....) with set this = newScope.
So, as a proof of concept, also this code would have worked:
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction.bind(this)();
}
but you should use the first one since the last one does the same thing with extra passages.
Replace:
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction().bind(this)
}
With
Plugin.prototype.yourOtherFunction2 = function () {
privateFunction.apply(this,arguments);
}

Object as jQuery.fn prototype?

If I define prototype like this
jQuery.fn.myFunc = function() {
console.log(this);
}
and call it like this:
$('div').myFunc();
this inside of myFunc will refer to jQuery object selection.
Now if I don't want to pollute .fn with multiple attached functions, can I do something like
jQuery.fn.myPlugin = {
myFunc1: function() {
},
myFunc2: function() {
}
}
If I call it $('div').myPlugin.myFunc1(); - how do I get reference to selected objects inside of myFunc1? Or is there a different, better approach?
Nope. can't do it that way. Instead, define it as a function, then add properties to that function.
jQuery.fn.myPlugin = function() {
console.log(this);
};
jQuery.fn.myPlugin.myFunc1 = function() {
};
jQuery.fn.myPlugin.myFunc2 = function() {
};
note that myFunc1 and myFunc2 still wont' have access to the selected element, therefore it's relatively useless to define them this way other than the fact that they can be easily overridden by other developers (which is a good thing)
The normal way of having additional methods within your plugin is to have your plugin method accept a parameter that can eitehr be an object to init the plugin, or a string to execute a target method on the element. for example, $("somediv").myPlugin("myFunc1")
The jQuery plugin tutorial suggests this:
(function( $ ) {
$.fn.popup = function( action ) {
if ( action === "open") {
// Open popup code.
}
if ( action === "close" ) {
// Close popup code.
}
};
}( jQuery ));
I suppose this would be another alternative:
(function($) {
$.fn.myPlugin = function (action) {
var functions = {
open: function () {
console.log('open: ', this);
},
close: function () {
console.log('close:', this);
}
}
if (action && functions[action]) {
functions[action].call(this)
} else {
console.log('no function', this);
}
return this;
};
}(jQuery));
$('#theDiv')
.myPlugin()
.myPlugin('open')
.myPlugin('close');
http://jsfiddle.net/faJAk/
work if you create a object before.
Like this:
<script>
jQuery.fn.myPlugin = {};
jQuery.fn.myPlugin = {
myFunc1: function() {
console.log(this);
},
myFunc2: function() {
alert(this);
}
};
$(document).ready(function(){
$('div').myPlugin.myFunc1();
$('div').myPlugin.myFunc2();
});
</script>
Another possible approach is to use defineProperty:
(function($) {
var myPlugin = {
foo: function() {
console.log(this)
}
}
Object.defineProperty($.fn, "myPlugin", {
get : function() { return $.extend({}, this, myPlugin) }
});
})(jQuery);
Now $(...).myPlugin.foo should resolve this correctly:
$(function() {
$("body").myPlugin.foo(); // logs "body"
})

Best way to define custom jQuery object properties?

I want myproperty to be able to be accessed from other methods within Foo.
Here is the way I'm doing it now:
(function($){
$.fn.Foo = {
main: function(){
return this.each(function(){
this.myproperty = 'abc';
...
...bind('click', 'somemethod')...
...
});
}
somemethod: function(e){
// access here myproperty
alert($.fn.Foo.myproperty);
}
}(jQuery));
(function($){
$.fn.extend({ Foo : $.fn.Foo.main});
}(jQuery));
and seems to work. But is this the best way to do it? Are there any other ways?
I think your trying to make a class? You currently are extending the jQuery library with function you can call on elements like $("#test").Foo();
A good start setup is: http://jsfiddle.net/VhGpe/
(function($) {
var methods = {
init: function(settings) {
var options = $.extend({}, $.fn.FooMe.defaults, settings);
$(this).data("settings", options);
$(this).bind("click", methods.myOtherFunction);
},
myOtherFunction: function() {
alert($(this).data("settings").you);
}
};
$.fn.FooMe = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.Foo');
}
};
$.fn.FooMe.defaults = {
you: "default"
};
}(jQuery));
$("#test").FooMe({
you: "TADAA"
});
can be exposed via a function of Foo
(function($){
$.fn.Foo = {
main: function(){
return this.each(function(){
this.myproperty = 'abc';
...
...bind('click', 'somemethod')...
...
});
getmyproperty: function(){
var myproperty = 'abc';
return myproperty;
}
}
somemethod: function(e){
// access here myproperty
alert($.fn.Foo.getmyproperty());
}
}(jQuery));

Categories

Resources