Variable scope in a jQuery plugin? - javascript

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();
});

Related

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"
})

Javascript, groovy like invoke method?

I was wondering if there is a way having for instance:
var klass = {
getName : function () { return "myname"; }
}
and doing klass.getName();
having a method fire before getName is actually called? In Groovy all method calls can for instance be listened to if an invoke method is added:
var klass = {
invoke function() { console.log("fires before getName()") ; },
getName : function () { return "myname"; }
}
I know this a long shot, but worth a try.
Not interested in altering the way the method is actually invoked: klass.getName()
The obvious answer is to simply call invoke in your getName method. If, for whatever reason, you don't wanna do that, you can proxy the methods of klass afterwards:
// loop through all properties of klass
for (var i in klass) {
// skip if it's not a custom property, not a function or the invoke function
// (to prevent infinite nested calls)
if(!klass.hasOwnProperty(i) || typeof klass[i] !== 'function'
|| i === 'invoke') {
continue;
}
// add the invoke() method as a proxy to the current method
var old = klass[i];
klass[i] = function () {
klass.invoke.apply(this, arguments);
return old.apply(this, arguments);
};
}
You can also put everything together neatly like this:
var klass = (function () {
this.invoke = function () {
console.log('called invoke()');
};
this.getName = function () {
return "called getName()";
};
(function (_this) {
for (var i in _this) {
if (!_this.hasOwnProperty(i) || typeof _this[i] !== 'function'
|| i === 'invoke') {
continue;
}
var old = _this[i];
_this[i] = function () {
_this.invoke.apply(_this, arguments);
return old.apply(_this, arguments);
};
}
})(this);
return this;
})();
console.log(klass.getName());

Trying to use jQuery.data() API to cache a function in a jQuery plugin

Please suggest solutions to below mentioned scenario
Background (jQuery Plugin):
$.fn.myplugin = function (action) {
if (actions[action]) {
return actions[action].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof action === 'object' || !action) {
return actions.init.apply(this, arguments);
} else {
$.error('Action ' + action + ' does not exist on myplugin');
return this;
}
};
and variable actions looking like:
var actions = {
init: function (options) {
if (this.length) {
var settings = $.extend({}, $.fn.myplugin.defaults);
return this.each(function () {
if (options) {
settings = $.extend(settings, options);
}
$(this).data('myplugin', new MyPlugin($(this), settings));
});
} else {
throw new Error('unable to init undefined');
}
},
update: function () {
...
},
destroy: function () {
...
}
};
and MyPlugin looking like
function MyPlugin($el, settings) {
var $content = $('.content', $el);
var a = function setup() { ... };
var b = function hold() { ... }
$content.on({
'click': function(e) { ... },
'hover': function(e) { ... }
});
}
I get that I can dump $.cache to console and see what gets associated in .data().
Problem/Suggestions wanted:
If I call the update function like $('myEle').myplugin('update') then I need the update function to change state of the instance of MyPlugin created and cached using .data() API. What are the possible ways to do that?
My current result of $('myEle').data('myplugin') shows MyPlugin{} with nothing between the curly braces.
The problem doesn't have anything to do with jQuery or the data() API, it's due to a misunderstanding about functions, objects and constructors in JavaScript.
This is easy to test inside the JavaScript console in a browser:
> function MyPlugin() { var a = 1; var b = 2; }
undefined
> new MyPlugin()
MyPlugin {}
> function MyPlugin() { this.a = 1; this.b = 2; }
undefined
> new MyPlugin()
MyPlugin {a: 1, b: 2}

jQuery Plugin Authoring Scoping

If I want to have a publicly accessible function on a jQuery plugin I'm making, is this the proper way to do it?
(function($) {
$.fn.myPlug = function(options) {
// Do this...
this.hello = function() {
return 1;
};
}
})(jQuery);
var foo = $("div").myPlug();
// then do this...
foo.hello();
You should structure your plugin so that method names can be passed as parameters to your plugin. This is recommended by the jQuery plugin authoring guide:
(function($) {
var methods = {
init: function(options) {
},
hello: function () {
return 1;
}
};
$.fn.myPlug = 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.myPlug');
}
};
})(jQuery);
The usage would go something like this:
$("div").myPlug({ ... });
$("div").myPlug("hello"); // returns 1

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