I have the following JavaScript pattern for creating a simple plugin:
(function(window, document, $) {
function method_1(){
}
function method_2{}
{
}
})(window, document, jQuery);
I want to have the ability to access my plugin and plugin methods in the following way:
myplugin.method_1();
myplugin.method_2();
How do I update my existing plugin pattern to enable this?!
NOTE: It has to maintain a self-executing format i.e. no variable declarations.
You could try something like this fiddle, which returns an object that includes the public functions for the plugin.
var myPlugin = (function(window, document, $) {
function privateFunction() {
}
return {
method_1: function () {
},
method_2: function () {
privateFunction(); // This works
}
};
}(window, document, jQuery));
myPlugin.method_1();
myPlugin.method_2();
myPlugin.privateFunction(); // This will throw an error
The following pattern seems to work perfectly for my requirements:
(function(root, name, make){
var $ = root['jQuery'] || root['Zepto'];
if (typeof module != 'undefined' && module['exports']) module['exports'] = make($);
else root[name] = make($);
}(window, 'myPlugin', function($) {
function method_1(){
}
function method_2{}
{
}
var myPlugin = {
method_1: method_1,
method_2: method_2
};
return myPlugin;
}));
Related
I am trying to make a function available globally. I wrapped it in an immediate function but it only works if I call a new $MyClass() inside the function. I want to be able to use it like Jquery, calling functions like this elsewhere in my project:
$MyClass('myButton').click(function(){
console.log('The button was clicked');
});
This is what I want access to:
(function() {
function $MyClass(element){
element = document.getElementById(element)
this.element = element;
return this;
}
$MyClass.prototype.click = function(callback) {
this.element.addEventListener("click", clickFunction);
function clickFunction() {
callback();
}
}
$MyClass.prototype.hover = function(inEvent, outEvent) {
this.element.addEventListener("mouseover", hoverOn);
if (outEvent != undefined) {
this.element.addEventListener("mouseout", hoverOff);
}
function hoverOn() {
inEvent();
}
function hoverOff() {
outEvent();
}
}
}());
How do I expose $MyClass to the global namespace? None of the answers I've found online have made sense to me enough to apply it to my project.
I would like to create my modules in a way, that they can be used with and without requirejs (without require js they should just work normally, so I have to make sure they load correctly, like putting script tags in the right order).
So jQuery does it kindof like this:
// export module
if ( typeof define === "function" && define.amd ) {
define(["dep"], function(dep){
dep.fn.test = test;
return dep;
});
}
else{
dep.fn.test = test;
}
The actual module is defined as like so
var dep = function(...){...}
This definition and the export part is within an IIFE to keep everything in out of the global scope.
Generally it works well with one exception, the dependency is not available.
This problem can be solved by defining the function within the define part, but this would mean defining it twice, in the define part and below in the else part.
How can I get this to work but only define the module once?
I have "plugin-like" extensions to a core dep which should all be in separate files so the main dep must be passed as a depenency
This works fine btw. but it would mean I write the code for test twice.
(function(){
// export module
if ( typeof define === "function" && define.amd ) {
define(["dep"], function(dep){
dep.fn.test = function(){...ssomething using dep...};
return dep;
});
}
else{
dep.fn.test = unction(){...ssomething using dep...};
}
})
Okay, I try another example
animate/animate.js (this is my main file)
define(function(){
...
return animate;
});
animate/modules/easing.js (this is a module file)
(function(){
var ease = function(){
// using animate main function from animate.js
// animate is not available here
...
};
if ( typeof define === "function" && define.amd ) {
define(["animate/animate"], function(animate){
// animate is available here
...
animate.fn.ease = ease;
return animate;
});
}
else
{
// if no requirejs, animate is global
animate.fn.ease = ease;
}
});
I think you're just writing the define incorrectly and so it's not getting registered. This is what I use.
if (typeof define === "function" && define.amd) {
define("telegraph", [], function () { return telegraph; });
}
Put in context
(function(window) {
var telegraph = function() { };
telegraph.prototype.test = function() {
// do something
};
if (typeof define === "function" && define.amd) {
define("telegraph", [], function () { return telegraph; });
}
window.telegraph = telegraph;
})(window);
EDIT
Do the question is really how do you define test and make use of dep internally so that you don't have to supply it as a dependency and can define a named dep module. One solution is to register the second-level functions in the constructor and capture this as self (or another variable) to use within the function. The key thing here is that you use define to define the named module and by using the captured context in the constructor, you don't need to supply the parent object as a dependency. Example (with working fiddle at http://jsfiddle.net/YS8v6/):
(function(){
var dep = function() {
var self = this;
self.fn.test = function() {
self.foo();
};
};
dep.prototype.foo = function() {
alert('foo');
};
dep.prototype.fn = function() {
};
if ( typeof define === "function" && define.amd ) {
define('dep', [], function() { return dep; });
}
})();
The actual problem seems to be that define is not available within the IIFE but window.define is. So passing define as an argument to the IIFE solves the problem.
(function(define){
// export module
if ( typeof define === "function" && define.amd ) {
define(["dep"], function(dep){
dep.fn.test = function(){...ssomething using dep...};
return dep;
});
}
else{
dep.fn.test = unction(){...ssomething using dep...};
}
}(window.define))
Before it would check for define, not find it and immediately try to attache it to dep.fn.test without the requirejs define part.
Question
I'd like to know the best way I can wrap the jQuery function while retaining all functionality. Essentially I want to call $('#someId') but have it operate as $('#' + id + 'someId') by wrapping the function, modifying the arguments, and passing it through to the original jQuery function.
Motivation
I have a section of JS that will reuse the same variable winId which is concatenated and passed to jQuery. Instead of writing
$('#' + winId + 'someId').html();
$('#' + winId + 'someOtherId').css();
...
$('#' + winId + 'someThirdId').text();
throughout the whole file, I want to wrap the jQuery function so I can just call
$('#someId').html();
$('#someOtherId').css();
...
$('#someThirdId').text();
and and have winId added in before passing through to $.
My attempt
Here's what I'm thinking as a wrapper:
(function() {
var fn = $;
return $ = function() {
for ( var i = 0; i < arguments.length; i++ ) {
if ( typeof arguments[i] == 'string') {
arguments[i] = /* code to add in winId, omitted */
}
}
return fn.apply( this, arguments );
}
})();
This works great, except that obviously none of the methods like $.ajax are available:
Uncaught TypeError: Object function () {
for ( var i = 0; i < arguments.length; i++ ) {
if ( typeof arguments[i] == 'string' ) {
arguments[i] = /* code to add in winId, omitted */
}
}
return fn.apply( this, arguments );
} has no method 'ajax'
Note: I know I could copy the object over using jQuery.extend($, jQuery), but I'm interested in a more elegant solution than that if possible.
Here's a different implementation:
DEMO
(jQuery.fn.init = (function (init) {
return function (selector) {
if (typeof selector === 'string' && selector[0] === '#') {
arguments[0] = selector.replace('#', '#prefix_');
}
return init.apply(this, arguments);
};
})(jQuery.fn.init)).prototype = jQuery.fn;
$(function () {
console.log($('#test').length);
console.log($.ajax);
});
EDIT: Followup question: How can I apply this only within a closure? For example, within an object.
Perhaps with functions that allows to add named decorators and remove them, something like:
HTML
<div id="prefix_test"></div>
JS
var decJQ = (function (decJQ, $) {
var decorators = {},
init = $.fn.init;
($.fn.init = function () {
for (var k in decorators) {
if (decorators.hasOwnProperty(k)) {
arguments = decorators[k].apply(this, arguments);
}
}
return init.apply(this, arguments);
}).prototype = $.fn;
return $.extend(decJQ, {
decorate: function (name, fn) {
decorators[name] = fn;
},
undecorate: function (name) {
delete decorators[name];
}
});
})(window.decJQ || {}, jQuery);
decJQ.decorate('idPrefix', function (selector) {
if (typeof selector === 'string' && selector[0] === '#') {
arguments[0] = selector.replace('#', '#prefix_');
}
return arguments;
});
$(function () {
console.log($('#test').length); //1
decJQ.undecorate('idPrefix');
console.log($('#test').length); //0
});
EDIT 2:
You could also go for something extremely simple, such as:
(function ($) {
//use $ which has been wrapped
})(function () {
//do some manipulations
return jQuery.apply(this, arguments);
});
Following the suggestion by Bergi and the post he links to here, this is one way to go:
$.fn.extend({
initCore: $.fn.init,
init: function (selector, context, rootjQuery) {
if (typeof selector === 'string' && selector[0] === '#') {
selector = selector.replace('#', '#' + winId);
}
return $.fn.initCore(selector, context, rootjQuery);
}
});
$.fn.init.prototype = $.fn;
I've tested $('#foo') will find a div that has a winId prefixed to the id value, like this <div id="1foo"></div>.
For example: http://jsfiddle.net/MfdJS/1/
Add class="winID" to your elements.
Use $(".winID").find('#someId").css(...) to access CSS attributes of specific element.
Use $(".winID").css(...) to access CSS attribues to all winID tagged elements.
ok well i just tested
$('.con'+'tainer')
and
$('d'+'iv');
and
var s = 't';
$('.con'+s+'ainer');
and the console is returning the correct values
i belive that you are calling a function jQuery() with a string parameter, so as long as you use the normal syntax for building/appending/constructing a string with the plus signs, i think you're golden. im glad you asked this question because now i know too
That's a pretty strange thing to do. Why don't you just create a CSS selector string for winId and save it as a variable?
var foo = '#' + winId;
Now you can do:
$(foo + ', #bar').html("add some content");
What you're proposing to do will leave any programmer working on this project -- including you six months from now -- completely flummoxed when they use $('#bar') and it's actually selecting #foo and #bar.
Why is the following not working:
(function($){
Drupal.my_module = {};
Drupal.behaviors.my_module = {
attach: function(context, settings) {
$('#some-div').Drupal.my_module.doStuff();
}
};
Drupal.my_module.doStuff = function(){
this.append('Hello World');
}
})(jQuery);
I get this error: TypeError: $(...).Drupal is undefined
If I use another architecture like passing the selector as an argument for the function it works:
(function($){
Drupal.my_module = {};
Drupal.behaviors.my_module = {
attach: function(context, settings) {
Drupal.my_module.doStuff($('#some-div'));
}
};
Drupal.my_module.doStuff = function(elem){
elem.append('Hello World');
}
})(jQuery);
It also works if I declare the function in the jQuery.fn object:
$.fn.doStuff = function(){...do something...}; // It works
Then call it like:
$('#my-div').doStuff(); // It works
But I want to put it under Drupal.my_module, something more like the first case.
Any clues?
Thanks!
$('#some-div').Drupal.my_module.doStuff();
Drupal is not a valid jQuery method or property, hence the undefined error.
file: dojo/dir1/utils/XmlJsonUtils.js
// Author: Rajat Khandelwal
define([
"dojo/_base/declare" // declare
], function(declare){
return declare("dir1.utils.XmlJsonUtils",[],{
parseXml : function (xml) {
var self=this;
var dom = null;
if (window.DOMParser) {
try {
dom = (new DOMParser()).parseFromString(xml, "text/xml");
}
catch (e) { dom = null; }
}
else if (window.ActiveXObject) {
try {
dom = new ActiveXObject('Microsoft.XMLDOM');
dom.async = false;
if (!dom.loadXML(xml)) // parse error ..
window.alert(dom.parseError.reason + dom.parseError.srcText);
}
catch (e) { dom = null; }
}
else
alert("cannot parse xml string!");
return dom;
},
xml2json : function (xmldata)
{
var self=this;
if(xmldata.firstChild==null)
{
return {name:xmldata.nodeName+": (value null)", checked: true}
}
else if(xmldata.firstChild.nodeType==3)
{
return {name:xmldata.nodeName+": "+xmldata.firstChild.nodeValue, checked:true}
}
else
{
var mychildren=[];
var i=0;
var nochildren=xmldata.childElementCount
for(i=0;i<nochildren;i++)
{
var j=self.xml2json(xmldata.childNodes[i])
mychildren[i]=j
}
var ret= {name:xmldata.nodeName, children:mychildren, checked:true}
return ret
}
},
convert2arr : function (result,ctr,res_arr)
{
var self=this;
if(result[ctr].checked[0]==false)
return;
if(result[ctr].children==undefined)
{
var name=result[ctr]['name'][0];
var kv = name.split(': ');
if(kv[1]=="(value null)")
kv[1]="";
res_arr.push.apply(res_arr,["<",kv[0],">",kv[1],"</",kv[0],">"]);
return ctr+1;
}
else
{
var i=ctr;
var new_ctr=ctr;
var no_children=result[ctr].children.length;
res_arr.push.apply(res_arr,["<",result[ctr].name[0],">"])
for(i=0;i<no_children;i++)
{
new_ctr=self.convert2arr(result,result[ctr].children[i]._0,res_arr)
}
res_arr.push.apply(res_arr,["</",result[ctr].name[0],">"]);
return new_ctr;
}
},
convert2xml : function (result)
{
var arr=[]
self.convert2arr(result, 0, arr)
return arr.join('')
}
})
})
but when in the code I require the dir1.utils.XmlJsonUtils, it says Uncaught Error: declare XmlJsonUtils: base class is not a callable constructor. What is the correct way to declare some utility functions.
And those should be like static functions. I don't want to do x=new XmlJsonUtils(); x.parseXml(..). I want to do XmlJsonUtils.parseXml(..)
Your class should not have to have the constructor method defined, dojo.declare is supposed to handle this.. However, doing so doesnt hurt, simply define a blank constructor: function() { }. I suspect youre facing some sort of bug.
The define is as should be, 'define' is used for the require-scope, when running require(["my.module"]), its expected to have a define method, which returns the base class via declare.
file: dojo/dir1/utils/XmlJsonUtils.js:
define([
// requirements
"dojo/_base/declare",
"dir1/utils/Toolkit" // sample in-package dependency
"./Toolkit" // Same as Above
], function (declare) {
// no slash separator, use dot with declare,
// use a reference and return on last line
var Klass = declare(
/// declaredClass: string, moduleUrl with dot-separater + filename /.js//
"dir1.utils.XmlJsonUtils",
/// base class: Array(mixins)
[],
/// class scope
{
_methodMeantToBePrivate: function() { },
randomInstanceMethod: function() { }
}
); // end declare
// set any aliases, which you want to expose (statics)
Klass.StaticCallable = function() {
// careful with your scope access inhere
}
// return the declared class to 'define'
return Klass;
}); // end define
This way (you must have a reference, either pulled in with require or getObject), you could use the StaticCallable function without initializing / constructing an instance of the module. AMD compliant syntax is like so:
require(["dir1/utils/XmlJsonUtils"], function(xmlUtils) {
xmlUtils.StaticCallable();
});
or if previously required
var xmlUtils = dojo.getObject("dir1.utils.XmlJsonUtils")
xmlUtils.StaticCallable();
A specific example could be a versatile class like the following, where both instance and static access is possible. Base class defines 'tools', derived class defines the variables the 'tools' operate on - and if instantiated, the default topics can be subscribed - [ MessageBusBase | MessageBus ]
The issue: in your code.
return declare("dir1.utils.XmlJsonUtils",[],{
parseXml : function (xml) {
Instead of dir1.utils.XmlJsonUtils use dir1/utils/XmlJsonUtils, i.e., use slashes instead of dots in your function declaration.