jQuery extension, can I make this code more elegant? - javascript

I was writing some custom jQuery plugins and I have the following code
that extends the jQuery:
(function ($) {
$.fn.extend({
pluginOne: function () { return this.each(pluginOneFN) },
pluginTwo: function () { return this.each(pluginTwoFN) },
pluginThree: function (p1, p2) {
return this.each(function() {
pluginThreeFN.call(this, p1, p2);
});
}
});
})(jQuery);
Now the pluginOne and pluginTwo are OK but pluginThree looks messy.
The pluginThree requires some parameters, and I was wondering
is there shorter or more elegant way to write this piece of extension code for plugin 3?

Only thing I can think of is a little reusability improvement. You could introduce a function to initialize plugins in general, supporting arguments.
(function ($) {
$.fn.initializePlugin = function(pluginFn, args) {
return this.each(function() {
pluginFn.apply(this, args);
});
};
$.fn.extend({
pluginOne: function () { return this.each(pluginOneFN) },
pluginTwo: function () { return this.each(pluginTwoFN) },
pluginThree: function () { return this.initializePlugin(pluginThreeFN, arguments) }
});
})(jQuery);
Note that I haven't tested this code, it's only for demonstrating the idea.
Also, if I were creating plugins, I would follow the existing convention of using only one argument, which is some kind of options object at initializtion, or a string for calling methods. Your method cannot support this, neither can this in my answer. You can check bootstrap's source code for a good skeleton implementation of such a plugin system.

Related

Function/dom calls in javascript library

I'm trying to create a javascript library like jquery. I get how to create a normal library like so:
var lib=lib||(function () {
function privateFunction (alert ("hi");){};
return {
exampleAlert: function(input){
alert(input);
}
}
})();
Calling it like so:
lib.exampleAlert ("test");
This is like jquery
$.ajax(stuffhere);
My question revolves around jquery. It can call the dom like $('.class').hide() and have functions like $.ajax(stuffhere); in the same library. How can I do dom calls and a regular function call like the ajax one in the above example library?
Thanks in advance!! Have searched more days than I would like to admit.
DOM has nothing to do here, it is just up to jQuery implementation.
If you ask about having both lib() and lib.func() calls, then you can do the following to support both function types at the same time:
var lib = function(sel) {
return {
object: document.querySelector(sel),
text: function(val) {
if (val === undefined) {
return this.object.innerText;
} else {
this.object.innerText = val;
}
}
};
};
lib.ajax = function() {
console.log("AJAX imitation");
};
Now, you can do both:
lib("body").text("hi"); // jQuery-style setter
var text = lib("body").text(); // jQuery-style getter, returns "hi"
and
lib.ajax();
jQuery works exactly in the same way, but hundred times more complex.

Structure application by using jQuery

I am going to build an application as it is not too much rich otherwise I can use angularjs for that purpose. I wanted to organize my JS code into proper modular programming approach.
E.g
var SignUpModule = {
elem: $('#id'), // unable to access jquery object here
init: function (jQuery) {
alert(jQuery('.row').html());
}
};
var application = {
modules: [],
addModule: function (module) {
this.modules.push(module);
},
run: function (jQuery) {
_.each(this.modules, function (module) {
//Iterate each module and run init function
module.init(jQuery);
});
}
}
jQuery(document).ready(function () {
application.addModule(SignUpModule);//add module to execute in application
application.run(jQuery);//Bootstrap application
});
Please now look at it I have updated my question with actual code
The biggest mistake you've done is using anonymous first parameter of an anonymous function taken by $.each method. The first argument is just index, and the second argument is an element you were looking for. Below you can find working code.
And no, you don't need to pass jQuery object everywhere. It's global. It already is everywhere. You can see it in the code below.
var SignUpModule = {
elem: $('.row'), //jQuery works fine here
init: function () {
alert(this.elem.html());
}
};
var application = {
modules: [],
addModule: function (module) {
this.modules.push(module);
},
run: function () {
$.each(this.modules, function (index, module) { //this function takes 2 parameters
//Iterate each module and run init function
module.init();
});
}
}
//document.ready
$(function () {
application.addModule(SignUpModule);//add module to execute in application
application.run();//Bootstrap application
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row">alert <b>me!</b></div>

Calling a function in a jquery plugin

I am trying to get the object of a jquery plug in I'm making use of. I want to eventually be able to add features to the plugin to suit my needs. I am currently having a problem with calling one the functions I defined in the plug in. Here is the skeleton of the code I have:
;(function($) {
$.fn.myPlugin = function(o) {
.//Implementations here
.
.
var Ex = function(e){
//implementations here
};
};
})(jQuery);
The reason I want this function inside is because I want access to some of the variables defined. I would like be able to call function Ex from my html file but whatever I tried so far hasn't worked. An example of what I have tried is:
$.fn.myPlugin.Ex("x");
No return statement anywhere. I'm not great at javascript or jquery but I'm trying to learn. Any help in explaining what I'm doing wrong is appreciated.
Your plugin design pattern is wrong.
To achieve what you want, you can use this common one :
;(function($){
var methods = {
init: function() {
//stuff
},
function1: function() {
//stuff
},
function2: function(opt) {
//stuff
}
};
$.fn.myPlugin= 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);
}
};
})(jQuery);
With this structure :
$.fn.myPlugin(); will call init()
$.fn.myPlugin("function1"); will call function1()
$.fn.myPlugin("function2",option_exemple); will call function2(opt)
Disclaimer : I use this very often, but it's not mine. Can't remember where I found it*.
edit : http://docs.jquery.com/Plugins/Authoring , thanks to Beetroot-Beetroot for the reminder !

jQuery expose plugin functions

One of our old developers has built a jQuery plugin like so:
jQuery.fn.limelight = function(options) {
/*Skipped code here*/
jQuery(".spotlight-btn.back a").click( function (e) {
if(lastSelectedCastIndex - 1 >= 0) {
removeFromSpotlight();
lastSelectedCastIndex--;
e.preventDefault();
$.address.value(lastSelectedCastIndex);
ca$t.scroll(jQuery.jcarousel.intval(lastSelectedCastIndex), true);
switchTo(lastSelectedCastIndex);
}
return false;
});
function switchTo(i)
{
ca$t.scroll(jQuery.jcarousel.intval(i), true);
$.address.title($("#title_text").text());
putInSpotlight();
}
};
I've not done any jQuery plugin programming, but would like to expose the switchTo function so it can be called anywhere. How would I be able to do this?
This is probably overkill for your purposes, but it doesn't seem like your developer really understood or grasped the purpose of jQuery plugins.
You want a plugin to be somewhat generic where it can accept a selector and apply events, styles, dynamic html, whatever to the item(s) found in the selector. It looks like he wrote a "plugin" for a single purpose... maybe just to maintain some sort of organization.
Most plugins follow a form similar to this:
; (function ($) {
$.fn.limelight = function (method) {
var methods = {
//Initialize the plugin
init: function (options) {
return this.each(function () {
//Refactor to conform to plugin style
// $(this).click( function (e) {
// if(lastSelectedCastIndex - 1 >= 0) {
// removeFromSpotlight();
// lastSelectedCastIndex--;
// e.preventDefault();
// $.address.value(lastSelectedCastIndex);
// ca$t.scroll(jQuery.jcarousel.intval(lastSelectedCastIndex), true);
// switchTo(lastSelectedCastIndex);
// }
// return false;
// });
});
},
switchTo: function (i) {
//Refactor to conform to plugin style
// ca$t.scroll(jQuery.jcarousel.intval(i), true);
// $.address.title($("#title_text").text());
// putInSpotlight();
}
};
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.limelight');
}
};
})(jQuery);
//Following this pattern you'd be able to call your plugin like this.
$(".spotlight-btn.back a").limelight();
$(".spotlight-btn.back a").limelight("switchTo", 0);
Here's the official documentation on the subject: http://docs.jquery.com/Plugins/Authoring
Paste the switchTo() function within your <script></script> tags to make it a generally-available function.
You can use Jquery UI Widget Factory to create stateful plugins, which allows you to expose public methods (still avoiding global window scope) to be used even after the plugin has been instantiated
https://learn.jquery.com/plugins/stateful-plugins-with-widget-factory/

Javascript function hooks

EDIT: OK, I believe the following solutions are valid:
Use the jQuery AOP plugin. It basically wraps the old function together with the hook into a function sandwich and reassigns it to the old function name. This causes nesting of functions with each new added hook.
If jQuery is not usable for you, just pillage the source code, there did not seem to be any jQuery dependencies in the plugin, and the source is simple and very small.
Have an object describing all hooks and their targets and one to store the initial unmodified function. When adding a new hook, the wrapping would be redone around the original function, instead of re-wrap the the previous wrapping function.
You escape nested functions, and get two objects to handle instead. Potentially, this could also mean easier hook handling, if you add/remove hooks often and out of order.
I'll go with the first, since it's already done, and I don't have performance to worry about. And since the original functions are not affected, even if I switch hooking methods, I'll only need to redo the hook adding, which might be just some simple search&replace operations.
Hi,
Is it possible to create a mechanism, in which function A might have a set of hooks(functions that will execute before/after function A)?
Ideally, function A would not be aware of hooking functionality, so that I do not have to modify the source code of function A to call the hooks. Something like:
A = function(){
alert("I'm a naive function");
};
B = function(){
alert("I'm having a piggyback ride on function A!"+
"And the fool doesn't even know it!");
};
addHook(B, A)//add hook B to function A
A()
//getting alerts "I'm a naive function"/"I'm having a
//piggyback ride on function A! And the fool doesn't even know it!"
I've been trying to hack something up for a couple of hours, but so far no luck.
Might not be pretty but it seems to work...
<script>
function A(x) { alert(x); return x; }
function B() { alert(123); }
function addHook(functionB, functionA, parent)
{
if (typeof parent == 'undefined')
parent = window;
for (var i in parent)
{
if (parent[i] === functionA)
{
parent[i] = function()
{
functionB();
return functionA.apply(this, arguments)
}
break;
}
}
}
addHook(B, A);
A(2);
</script>
Take a look at jQuery's AOP plugin. In general, google "javascript aspect oriented programming".
Very simple answer:
function someFunction() { alert("Bar!") }
var placeholder=someFunction;
someFunction=function() {
alert("Foo?");
placeholder();
}
This answer is not definitive, but rather demonstrative of a different technique than those offered thus far. This leverages the fact that a function in Javascript is a first-class object, and as such, a) you can pass it as a value to another function and b) you can add properties to it. Combine these traits with function's built-in "call" (or "apply") methods, and you have yourself a start toward a solution.
var function_itself = function() {
alert('in function itself');
}
function_itself.PRE_PROCESS = function() {
alert('in pre_process');
}
function_itself.POST_PROCESS = function() {
alert('in post_process');
}
var function_processor = function(func) {
if (func.PRE_PROCESS) {
func.PRE_PROCESS.call();
}
func.call();
if (func.POST_PROCESS) {
func.POST_PROCESS.call();
}
}
The following function will give you before and after hooks that can be stacked. So if you have a number of potential functions that need to run before the given function or after the given function then this would be a working solution. This solution does not require jQuery and uses native array methods (no shims required). It should also be context sensitive so if you are calling the original function with a context if should run each before and after function likewise.
// usage:
/*
function test(x) {
alert(x);
}
var htest = hookable(test);
htest.addHook("before", function (x) {
alert("Before " + x);
})
htest.addHook("after", function (x) {
alert("After " + x);
})
htest("test") // => Before test ... test ... After test
*/
function hookable(fn) {
var ifn = fn,
hooks = {
before : [],
after : []
};
function hookableFunction() {
var args = [].slice.call(arguments, 0),
i = 0,
fn;
for (i = 0; !!hooks.before[i]; i += 1) {
fn = hooks.before[i];
fn.apply(this, args);
}
ifn.apply(this, arguments);
for (i = 0; !!hooks.after[i]; i++) {
fn = hooks.after[i];
fn.apply(this, args);
}
}
hookableFunction.addHook = function (type, fn) {
if (hooks[type] instanceof Array) {
hooks[type].push(fn);
} else {
throw (function () {
var e = new Error("Invalid hook type");
e.expected = Object.keys(hooks);
e.got = type;
return e;
}());
}
};
return hookableFunction;
}
Here's what I did, might be useful in other applications like this:
//Setup a hooking object
a={
hook:function(name,f){
aion.hooks[name]=f;
}
}a.hooks={
//default hooks (also sets the object)
};
//Add a hook
a.hook('test',function(){
alert('test');
});
//Apply each Hook (can be done with for)
$.each(a.hooks,function(index,f){
f();
});
I don't know if this will be useful. You do need to modify the original function but only once and you don't need to keep editing it for firing hooks
https://github.com/rcorp/hooker

Categories

Resources