I still can't get my head around with this AMD jquery plugin.
// UMD dance - https://github.com/umdjs/umd
!function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(root.jQuery);
}
}(this, function($) {
'use strict';
// Default options
var defaults = {
};
// Constructor, initialise everything you need here
var Plugin = function(element, options) {
this.element = element;
this.options = options;
};
// Plugin methods and shared properties
Plugin.prototype = {
// Reset constructor - http://goo.gl/EcWdiy
constructor: Plugin,
someMethod: function(options) {
return options;
}
};
// Create the jQuery plugin
$.fn.plugin = function(options) {
// Do a deep copy of the options - http://goo.gl/gOSSrg
options = $.extend(true, {}, defaults, options);
return this.each(function() {
var $this = $(this);
// Create a new instance for each element in the matched jQuery set
// Also save the instance so it can be accessed later to use methods/properties etc
// e.g.
// var instance = $('.element').data('plugin');
// instance.someMethod();
$this.data('plugin', new Plugin($this, options));
});
};
// Expose defaults and Constructor (allowing overriding of prototype methods for example)
$.fn.plugin.defaults = defaults;
$.fn.plugin.Plugin = Plugin;
});
Some tests,
console.log($('.some-element').plugin({
test: 'option1',
test2: 'option2'
}));
I always get this empty object,
Object[]
So how can I use this empty object?
And I want to access the method inside the plugin,
var plugin = $('.element').plugin();
var instance = $('.element').data('plugin',plugin);
console.log(instance); // an empty object again!
console.log(instance.someMethod("hello world"));
TypeError: instance.someMethod is not a function
console.log(instance.someMethod("hello world"));
So how should I do to run the method inside the plugin then?
It is so different from the conventional jquery plugin. AMD's is so difficult to understand. Any idea how I can get this AMD works like the conventional!??
EDIT:
Finally I got something,
var plugin = $('.element').plugin();
var instance = $('.element').data('plugin');
console.log(instance);
console.log(instance.someMethod("hello world"));
result,
Object { element={...}, options={...}, constructor=function(), more...}
hello world
Why was it so difficult for the those who comment and answer to point this out! Sigh!
In your test with .some-element you would get an empty jQuery object if .some-element matches nothing since $.fn.plugin returns what this.each(...) returns and the jQuery.each function returns exactly the same jQuery object as what it was called on.
As to how to get the instance, let's go through each step:
var plugin = $('.element').plugin();
The line above won't create a plugin if $('.element') matches nothing. One thing for sure, the value you assign to plugin is equal to $('.element').
var instance = $('.element').data('plugin',plugin);
In context, the line above is equivalent to var instance = $('.element').data('plugin', $('.element')); And the return value of jQuery.data(key, value) is the jQuery object itself, so that here instance will be equal to $('.element').
console.log(instance); // an empty object again!
That's what you'd get if $('.element') matches nothing.
console.log(instance.someMethod("hello world"));
This is definitely not going to work.
How to access the plugin instance is detailed in a comment in your code:
// var instance = $('.element').data('plugin');
// instance.someMethod();
Of course for this to work, $('.element') has to match a non-empty set of elements.
AMD is not a factor at all in your problems. All of the issues here have to do with how to use jQuery and how to write jQuery plugins.
var plugin = $('.element').plugin();
var instance = $('.element').data('plugin');
console.log(instance);
console.log(instance.someMethod("hello world"));
result,
Object { element={...}, options={...}, constructor=function(), more...}
hello world
Related
I'm confused as to how best create a JQuery plugin I'm working on.
The base layout I have is:
// Create Closure.
(function($) {
// Plugin definition.
$.fn.myPlugin = function(options) {
var settings = $.extend(true, {}, $.fn.myPlugin.defaults, options);
// Private:
var privateVar = 'private';
var privateFunction = function() {
alert('hidden');
}
// Public:
this.publicVar = 'public';
this.publicFunction = function() {
alert('visible');
}
return this;
};
// Plugin defaults – added as a property on our plugin function.
$.fn.myPlugin.defaults = {
foreground: "red",
background: "yellow"
};
// End Closure.
}(jQuery));
But I'm wondering why the JQuery docs (https://learn.jquery.com/plugins/advanced-plugin-concepts/), recommend having the defaults as a property, defined outside of the plugins body, as opposed to inside. So, the way I'm thinking it could be implemented instead is:
// Create Closure.
(function($) {
// Plugin definition.
$.fn.myPlugin = function(options) {
// Plugin defaults – added as a property inside the plugin function.
this.defaults = {
foreground: "red",
background: "yellow"
};
var settings = $.extend(true, {}, this.defaults, options);
// Private:
var privateVar = 'private';
var privateFunction = function() {
alert('hidden');
}
// Public:
this.publicVar = 'public';
this.publicFunction = function() {
alert('visible');
}
return this;
};
// End Closure.
}(jQuery));
Am I to assume that if the plugin is created in the second manner, then the defaults will be unable to be overridden? As the plugin object will first have to be instantiated?
If that's the case, then why does it really matter? As it's going to be instantiated at some point anyway...
Can anyone chime in with their view on this?
The first method creates one static version of the defaults, which are globally accessible (for overriding etc).
The second creates one copy of the defaults per instance of the plugin and are only accessible on an instance of the plugin - therefore not overridable in any meaningful way.
Opinion - javascript is apretty open language, you can implement things in any number of ways. When a library advises you to do something in a particular way its usually preferable to take the advice.
I am trying to return a query like object, in my first try I try the Object.create method
var ElementArray = {
someMethod : myMethod,
....
}
var addMethods = function(elements) {
var obj = Object.create(ElementArray);
obj[0] = elements;
return obj;
};
var getId = function( query ) {
return addMethods( doc.getElementById(query) );
};
(jsperf)
I immediately found that this was slower then jQuery(sizzle), especially on firefox. The issues with firefox where probably to do with cross-compartment wrappers (see bug here), but I was still quite dissatisfied.
I also tried using prototype instead
var ElementArray = function(){};
ElementArray.prototype.someMethod = someMethod;
....
var addMethods = function(elements) {
var obj = new ElementArray();
....
};
Slightly better on Chome, but still very slow on firefox.
So my question is, how does jQuery(sizzle), and other libraries do it || Whats the fastest way to return a object with a 1-2 instance properties? (Everything else can just be references)
So my question is, how does jQuery(sizzle), and other libraries do it
jQuery uses the prototype. It kind of hides that fact by aliasing the prototype to .fn, but it is still the prototype. Here's the jQuery function.
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
},
And, here's the aliasing:
jQuery.fn = jQuery.prototype
And, the implementation of the actual jQuery constructor:
init = jQuery.fn.init = function( selector, context, root ) {
var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false)
if ( !selector ) {
return this;
}
// Method init() accepts an alternate rootjQuery
// so migrate can support jQuery.sub (gh-2101)
root = root || rootjQuery;
.....
And, the round-about assignment of the `.init.prototype':
// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;
I am currently writing an extensible plugin for jQuery. You can see the plugin here.
My question is specific to lines 37 and 96:
// Creates a Namespace
hautD = {};
// A Basic Dropdown Constructor function
hautD.HautForms = function(el, options) {
// Overwrite default options
// with user provided ones
// and merge them into "options".
var options = $.extend({}, defaults, options);
if (el) {
this.init(el, options);
}
}
switch(options.cornerType){
default:
// default = 'rounded'
if(this.borderRadius){
alert('woo hoo');
}
break
}
(source) where I have this.borderRadius. When I set a breakpoint for this.borderRadius, it is undefined. So in the Chrome Inspector I set a watch expression for 'this' and it returns hautD.HautForms. If I set a watch inspection for hautD.HautForms.borderRadius the returned value is 'true'.
In a nutshell: why is this.borderRadius == undefined while hautD.HautForms.borderRadius == true given this == hautD.HautForms.
Sorry if this question is convoluted, this is my first 'real' plugin.
this is a special keyword that is used in methods to refer to the
object on which a method is being invoked
this is used inside the class object to access member variables.
Otherwise, you access your object from outside by public methods/variables.
In your code, you use this correctly here:
this.init(el, options);
because it is inside hautD.HautForms operation
Your code should be like this:
hautD.HautForms = function(el, options) {
// Overwrite default options
// with user provided ones
// and merge them into "options".
var options = $.extend({}, defaults, options);
if (el) {
this.init(el, options);
}
switch(options.cornerType) {
// here you can use this.borderRadius instead of hautD.HautForms.borderRadius from outside
}
}
I'm struggling with how best to combine javascript Classes and jQuery plugins. This question isn't very specific, what I'm hoping for is pointers to more resources.
Basically, I want to store state data and private methods in a class, and then extend each jQuery object which I call my plugin on to have those private methods and properties. Such that inside the plugin I can call methods directly off the jQuery object.
I read jQuery plugin design pattern (common practice?) for dealing with private functions, specifically David's answer, however this initializes a new Class each time, and thus can't be used to save the state of the object.
I also found http://fuelyourcoding.com/jquery-plugin-design-patterns-part-i/, which recommends creating a class and then storing it in .data().
I think ideally what I want to end up with is code that looks like
(function( $ ){
var methods = {
init : function( options ) { // Initialize each object with a state and private methods },
show : function( ) {
// testFoo() is a private method that checks the element's state
if(this.testFoo()){
// Relying on jQuery's html() method
this.html() = this.fooTemplate();
}
}
};
// Boiler plate plugin from http://docs.jquery.com/Plugins/Authoring
$.fn.myPlugin = function( method ) {
// Method calling logic
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.myPlugin' );
}
};
})( jQuery );
Finally, it doesn't seem like I can bake the private methods into the plugin directly because methods like "testFoo()" will return a boolean, and therefore aren't chainable.
Thoughts? Am I approaching this the right way? Is there another design pattern I should be using? Perhaps not using jQuery plugin architecture at all?
Here's a proposed solution. It combines few different approaches (John Resig's inheritance model and Alex Saxton's plugin inheritance model).
Define your inheritable plugin:
(function ($) {
My.Plugin = Class.extend({
/*
* Initialization (constructor)
*/
init: function (element, meta) {
var $meta = $.extend({ name: "pluginName" }, meta);
// Call the base constructor
this._super(element, $meta);
// TODO: Add custom initialization code like the following:
// this._testButton = $('.testButton', element).get(0);
},
/*
* Public methods
*/
show: function() {
alert('This is a public method');
},
/*
* Private methods
*/
// DEMO: Overriding the base _paint method:
_paint: function () {
// "this._super()" is available in all overridden methods
// and refers to the base method.
this._super();
alert('TODO: implement myPlugin._paint!');
}
});
// Declare this class as a jQuery plugin
$.plugin('my_plugin', My.Plugin);
})(jQuery);
Define Base class
(function () {
var initializing = false, fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function () { };
// Create a new Class that inherits from this class
Class.extend = function (prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] =
typeof prop[name] == "function"
&& typeof _super[name] == "function"
&& fnTest.test(prop[name])
? (function (name, fn) {
return function () {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name])
: prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if (!initializing && this.init)
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();
Plugin Creation
(function ($) {
// The "inheritance plugin" model
// [http://alexsexton.com/?p=51][1]
$.plugin = function (name, object) {
$.fn[name] = function (options) {
var instance = $.data(this, name, new object(this, options));
return instance;
};
};
})(jQuery);
Calling your plugin from javascript:
$('#someElem').my_plugin({options: {}, data: {} /* you can modify your plugin code to accept anything */}).show();
Note:
Private methods here are marked as _methodName. It's pure convention. If you really want to hide them, you can use module pattern (google for it or here's one for starters: http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth)
Is this what you're looking for?
Consider the following base code:
(function($) {
$.fn.myPlugin = function(settings) {
return this.each(function() {
//whatever
});
};
});
The plugin returns a jQuery object. The question is how am I supposed to write a plugin that returns a custom object so that I can do something like this:
var api = $('div.myelement').myPlugin();
api.onMyEventName(function(e, whateverParam) {
//whatever
});
It'd be highly appreciated if you could write some lines of code that describes me how to do that, how to call the onMyEventName function on a custom api object...
Thanks.
(function($) {
function MyApi($this, settings) {
this.$this = $this;
this.settings = settings;
};
MyApi.prototype.output = function (val) {
// use this.$this to access the original jQuery object
return this.settings[val];
};
$.fn.myPlugin = function(settings) {
return new MyApi(this, settings);
};
});
Note that we've passed this from $.fn.myPlugin() into the MyApi constructor; this allows you to access the jQuery object that myPlugin() was originally called on within MyApi methods.
You can also do the same using the object literal syntax:
(function($) {
$.fn.myPlugin = function(settings) {
return {
settings: settings,
output: function (val) {
// use this.$this to access the original jQuery object
return this.settings[val];
},
$this: this
};
};
});
Then;
var something = $('#something').myPlugin({
val: 'Lemon'
});
alert(something.output('val')); // Lemon
... again, we've captured the value of this (the jQuery object) into a property $this on the newly constructed objected, to gain access to the original jQuery object.
There is a great article by Hector Virgen detailing a possible solution (also used in bootstrap) to adress this question.
The key moment is basically storing your API object in the data section of the node:
$.fn.myplugin = function() {
return $.each(function() {
...
myplugin = new MyPlugin(this)
...
$(this).data('myplugin', myplugin);
}
}
After this, users can easily access the object by:
$(this).data("myplugin")
A possibly useful extension to this solution could be defining a mypluginapi method as a shorthand, for accessing your API object:
$.fn.mypluginapi = function() {
return $(this).myplugin().data('myplugin')
}