That how I usually implement my plugins:
(function($){
$.fn.plugingName = function(inSettings){
//code that extands settings with default
return this.each(function(){
new PluginClass($(this), settings);
});
}
PluginClass = function(jElem, settings){
//here I usually put variable and function in such a way to compose better
var events = {
onMouseUp : function(){
//some actions
},
onMouseMove : function(event){
//some actions
},
//etc.
},
draw = {
drawGrid : function(){//some actions},
//etc.
},
//etc.
/*****************************
* This is a place of question
****************************/
}
})(jQuery);
I just want to know if there is a pattern to separate the algorithm from declaration part that I described above. Some thing like to put all your algorithm part inside
function main(){
}();
If there is a better approach to distinguish the main part of your algorithm. I hope I described everything clear.
All others improvment that might be done with represented code are also appreciate.
You mean putting the function main() outside of the big wrapping function($){ ...? But this is not desired, the wrapping function is there for the very reason not to overcrap the global namespace. This is the very clean design and it is desired. So don't be worry to use this standard pattern.
Related
I have an object that I want to pass to .fadeOut().
Before we get to that, here's how I can pass an object to .click():
this.$title.click({story: this}, function (event){
var story = event.data.story;
}
Simple enough.
Now I want to do something similar with .fadeOut:
this.$title.fadeOut("fast", {story: this}, function(){
var story = ???
});
Which doesn't work. But you get the idea? How can I pass this into the anon function?
I'm looking for the cleanest solution. Barring that, I'm looking for the solution that's most in line with what I've done with .click().
Thanks!
ASIDE: is there a cleaner way pass this into .click()?
This is rather a question about JS than about jQuery; you can do it like that:
var story = this
this.$title.click(function () {
/* story still available */
})
this.$title.fadeOut('fast', function () {
/* same here */
})
Or something more fancy (this also preserves the content of story at the moment of assignment even if it gets overwritten in the upper scope later on):
this.$title.click((function (story) {
return function () {
/* story is available */
/* this will be passed to $(...).click(...) */
}
})(this))
It seems fadeOut() doesnt have an overload like click() where you can pass eventData
.click( [eventData ], handler )
Therefore, make a preserve this in a closure and use inside the function
var story = this;
this.$title.fadeOut("fast", function(){
//story can be used here
});
According to this question: Multiple file upload with extra inputText I can override JavaScript function of PrimeFaces element by using widgetVar in this way:
PF('fileUpload').jq.fileupload({
add: function(e, data) {
...
}
});
Now, I try to override function in DataTable and can't understand how do I reference to it? Moreover, PF(') returns undefined from chrome debugger console, so I can't debug it. I suspect that it's matter of scopes but don't know how to solve it.
You could use the prototype,
for example overriding bindEditEvents would look like this
PrimeFaces.widget.DataTable.prototype.bindEditEvents = function() {
....
}
There are couple more solutions as well. Depends on how deep do you want to go.
In my case I was intending to extend DataScroller's functionality.
Despite it's too late answering your question, I hope the solutions below helps others:
Solution #1 (extending entire class with its methods)
PrimeFaces.widget.DataScroller = PrimeFaces.widget.BaseWidget.extend({
init: function(cfg) {
this._super(cfg);
// only for widget with widgetVar="yourWidgetVar"
if(cfg.widgetVar === 'yourWidgetVar') {
this.yourCustomMethod();
}
},
yourCustomMethod: function() {
// do whatever you prefer here
}
});
Solution #2 (extending of already existing methods aimed to specific widgets)
PF('yourWidgetVar').load = function() {
// do whatever you prefer to extend it here
// call the generic implementation
PrimeFaces.widget.DataScroller.prototype.load.call(this, arguments);
};
Solution #3 (extending of already existing methods via prototypes)
const oldLoad = PrimeFaces.widget.DataScroller.prototype.load;
PrimeFaces.widget.DataScroller.prototype.load = function() {
oldLoad.apply(this, arguments);
// do whatever you prefer to extend it here
// in case you need to do it for specific widget. i.e. widgetVar="yourWidgetVar"
if(cfg.widgetVar === 'yourWidgetVar') {
// your custom stuff here
}
};
Solution #4 (overriding init method of component)
if(PrimeFaces.widget.DataScroller) {
PrimeFaces.widget.DataScroller.prototype.init = function(cfg) {
PrimeFaces.widget.DeferredWidget.prototype.init.call(this, cfg);
this.cfg = cfg;
// this._super(cfg);
// original init method code without calling _super method
// apply here your custom code
}
}
I have an occurence where I want to have a main js-file with one resize function and specific files that can add workload to the main file without changing the mainfile and manually calling functions.
Lets say I have an object literal
var App = {
resize: function(){
// Code should be executed here
},
addResize: function(){
// ?
}
}
and I want a function to add code to the resize function which dynamically adds workload to the resize function (which gets called on window resize):
App.addResize(function(){ ... });
The first thing that came to my mind is to store the anonymous functions from addResize to an array and iterating over it in the resize function, but that doesn't feel like doing a best-practice:
var App = {
resizeFunctions = [];
resize: function(){
// iterate over resizeFunctions and call each one
// here I define throttling/debouncing ONCE
},
addResize: function(fn){
this.resizeFunctions.push(fn);
}
}
window.onresize = App.resize();
App.addResize(fn1);
App.addResize(fn2);
Is there a better way?
as you are referring to one function, ie. a resize function, I assume that you are looking for function overloading:
Function overloading in Javascript - Best practices
http://ejohn.org/blog/javascript-method-overloading/
If you want to extend the functionality of a set of methods that are all related to a single parent-object into different child objects, I would look into prototypal inheritance.
It allows you to define re-define the parent methods for each of the child-objects.
Do you want to overwrite the existing function?
Then you can just do this:
App.addResize = function(){}
App.addResize(function(){ ... });
would pass the function to addResize as an attribute but not add it to it. You could do
App.addResize.newFunction = function(){ ... };
Where newFunction is the name of the function
You can treat your object literal as array.
App["resize"] = function(){
//Code goes here
}
__
Or
App.resize = function(){
//Code here
}
Both are equally good. These will update the definition of resize
If you want to add some new method, then too the same syntax will work.
App["myNewMethod"] = new function(){
//Code here
}
Edit after OP's comment
var oldFun = App["resize"]; // or may be store this in App itself
App["resize"] = function(){
//Pre-processing
// Now call original resize method
oldFun(); //If resize method used method argument then use oldFun.apply( this, arguments );
//Post processing
}
I'm using JS and jQuery for the first time after a lot of experience with Java and C++. I'm loving jQuery's idea of $(document).on('click', 'btn-selector', react), but for more complex widgets I'm finding myself in the same rut over and over: in each react handler, I have to look up the widget as a whole and reconstruct all my knowledge about it.
For example, I'm making a simple widget out of <input>s with which the user can make a grading scale: 90 maps to an A, 80 maps to a B, etc. When one of the inputs changes, I want to check to make sure that the inputs are still in order (your scale can't go 90, 70, 80, for example).
So, I have something like
Actual
$(document).on('click', '.scale-input', function() {
var widget = $(this).closest('.scale-widget-container');
ensureLevelsAreInOrder(widget);
});
Almost every single handler has to have this first line to find its context. I'd much rather have code that looks like this:
Preferred
$(document).on('click', '.scale-input', ensureLevelsAreInOrder);
The problem is that in this form, ensureLevelsAreInOrder only has a reference to the input that changed, not the larger context.
In Java or C++, I would have called a constructor on the widget, and each input would have a handler with the context baked in via member variables. I could do something similar with
$(function() {
$('.scale-widget-container').scaleWidget();
});
with scaleWidget() setting up the contextualized handlers, but the page I'm working with loads a lot of its html with ajax and I don't have a reliable time to run that initialization.
Is this a common problem that we just have to deal with if we don't want JS in our HTML, or is there a solution I haven't come across yet?
Not sure what it is you're after exactly, but you don't seem to touch on two quite important concepts when it comes to JS: the event object, and closures. Both of these are open to you to get what you need:
event object:
The callback function is passed an argument, that describes the event itself, and references the elements affected by that event, This isn't exclusive to jQ (just google addEventListener), but it's quite handy:
$(document).on('click', '.scale-input', function(e)//<-- e is our event
{
console.log(e);//check console
});
Which, in vanilla JS would look like this:
document.addEventListener('click', function(e)
{
if (!e.className.test(/\bscale\-input\b/))
{
return e;
}
console.log(e);
}, false);
Another thing you might want to consider is enclosing references to whatever it is you need in an IIFE's scope:
(function()
{
var containers = $('.scale-widget-container'),
localBool = false,
asMany = 'varsAs you need',
previousScales = [],
inputs = $('.scale-input');//references to all DOM nodes you mention
$(document).on('click','.scale-input',function(e)
{
console.log($(this));
console.log(containers);
previousScales.push(this.value);//or something
console.log(previousScales);
//and so on.
});
}());
Hope this helped
Update:
If IE isn't a browser you don't care about that much, you could use one of the DOM-modified events, specifically DOMTreeModified:
(function()
{
var nodes = [];//<-- store current nodes here, if applicable
nodes.containsNode = function(node)
{
var i;
for (i=0;i<this.length;i++)
{
if (this[i] && this[i] === node)
{//node is set, return its index
return i;
}
}
//node not found, return -1
return -1;
};
document.body.addEventListener('DOMSubtreeModified',function(e)
{
var all = document.getElementsByClassName('scale-input'),
i;
for (i=0;i<all.length;i++)
{
if (nodes.containsNode(all[i]) === -1)
{
nodes.push(all[i]);//add new
}
}
},false);
}());
More on the mutation events, and their issues, on the DOM events wiki
I have the following code:
$('#detailData')
.on('click', '.gridLink', function () {
dialog(this);
return false;
})
function dialog(link) {
var $link = $(link);
var viewURL = $link.attr('data-href')
Am I correct in saying I can replace that with this?
$('#detailData')
.on('click', '.gridLink', function () {
var $gridLink = $(this);
dialog($gridLink);
return false;
})
function dialog($gridLink) {
var viewURL = $gridLink.attr('data-href')
I tried to place this onto code review at stackoverflow.com. Someone needs to fix the logon problems as I just could not connect with my stack account :-(
Yes, foo = $(this) is perfectly legal, and legit. In fact, it's not entirely uncommon. It's wise to do this when you feel the need to wrap this over and over in the jQuery object. This way, you wrap it once, and have a reference to work from which offers performance benefits.
Yes the way you are passing in both the cases is perfectly legal..
This also has the advantage of caching it and reusing it instead of trying to access it every single time you use.