Is it possible to do this from within a class?
$("#" + field).click(this.validate);
So basically I want to pass a function of the object that should be executed whenever something is clicked. Also, if there are more than 1 instances of this object, then the correct instance (i.e the one which runs this code for the given field) should be executed.
I am not sure about an easy way, but you can always go the closure route:
var that = this;
$("#" + field).click(function() {
that.validate();
});
Is it possible to do this from within a class?
$("#" + field).click(this.validate);
“this.validate” is problematic. JavaScript does not have bound methods, so when you pass that reference, it is only pointing to a plain function. When called, ‘this’ will not be correctly set. See ALA for a fairly thorough discussion of the binding loss problem.
Some frameworks provide built-in method-binding functionality; jQuery does not, as it tends to concentrate more on closures than JavaScript objects. In any case, creating a binding wrapper using a closure is pretty simple; Andrey's answer is the usual approach for jQuery users.
The one thing to look out for with closures for event handlers (whether you are using jQuery or not) is that it tends to cause memory leaks in IE. For simple, short-lived web pages you may not care.
Yes it is possible.
10 mins ago was writting a snipet because i had just that problem
$("div.myalarms_delete").click(function(){
var mid = this.id;
$("li#"+mid).fadeOut("fast");
});
the div.myalarms_delete also as the id I needed
From inside a class you're typically better off using the .find function.
var child = this.find('.someChildClass');
var child2 = this.find('#someChildId');
Related
I have some trouble that comes from my Javascript (JS) codes, since I sometimes need to access the same DOM elements more than once in the same function. Some reasoning is also provided here.
From the point of view of the performance, is it better to create a jQuery object once and then cache it or is it better to create the same jQuery object at will?
Example:
function(){
$('selector XXX').doSomething(); //first call
$('selector XXX').doSomething(); //second call
...
$('selector XXX').doSomething(); // n-th call
}
or
function(){
var obj = $('selector XXX');
obj.doSomething(); //first call
obj.doSomething(); //second call
...
obj.doSomething(); // n-th call
}
I suppose that the answer probably depends by the value of "n", so assume that n is a "small" number (e.g. 3), then a medium number (e.g. 10) and finally a large one (e.g. 30, like if the object is used for comparison in a for cycle).
Thanks in advance.
It is always better to cache the element, if n is greater than 1, cache the element, or chain the operations together (you can do $('#something').something().somethingelse(); for most jQuery operations, since they usually return the wrapped set itself). As an aside, it has become a bit of a standard to name cache variables beginning with a money sign $ so that later in the code it is evident that you are performing an operation on a jQuery set. So you will see a lot of people do var $content = $('#content'); then $content.find('...'); later on.
The second is superior. Most importantly, it is cleaner. In the future, if you want to change your selector, you only need to change it one place. Else you need to change it in N places.
Secondly, it should perform better, although a user would only notice for particularly heavy dom, or if you were invoking that function a lot.
If you look at this question from a different perspective, the correct answer is obvious.
In the first case, you're duplicating the selection logic in every place it appears. If you change the name of the element, you have to change each occurence. This should be reason enough to not do it. Now you have two options - either you cache the element's selector or the element itself. Using the element as an object makes more sense than using the name.
Performance-wise, I think the effect is negligible. Probably you'll be able to find test results for this particular use-case: caching jQuery objects vs always re-selecting them. Performance might become an issue if you have a large DOM and do a lot of lookups, but you need to see for yourself if that's the case.
If you want to see exactly how much memory your objects are taking up, you can use the Chrome Heap Profiler and check there. I don't know if similar tools are available for other browsers and probably the implementations will vary wildly in performance, especially in IE's case, but it may satisfy your curiosity.
IMO, you should use the second variant, storing the result of the selection in an object, no so much as to improve performance but to have as little duplicate logic as possible.
As for caching $(this), I agree with Nick Craver's answer. As he said there, you should also use chaining where possible - cleans up your code and solves your problem.
You should take a look at
http://www.artzstudio.com/2009/04/jquery-performance-rules/
or
http://addyosmani.com/jqprovenperformance/
I almost always prefer to cache the jQuery object but the benefit varies greatly based on exactly what you are using for your selector. If you are using ids then the benefit is far less than if you are using types of selectors. Also, not all selectors are created equally so try to keep that in mind when you write your selectors.
For example:
$('table tr td') is a very poor selector. Try to use context or .find() and it will make a BIG difference.
One thing I like to do is place timers in my code to see just how efficient it is.
var timer = new Date();
// code here
console.log('time to complete: ' + (new Date() - timer));
Most cached objects will be performed in less than 2 milliseconds where as brand new selectors take quite a bit longer because you first have to find the element, and then perform the operation.
In JavaScript, functions are generally short-lived—especially when hosted by a browser. However, a function’s scope might outlive the function. This happens, for example, when you create a closure. If you want to prevent a jQuery object from being referenced for a long time, you can assign null to any variables that reference it when you are done with that variable or use indirection to create your closures. For example:
var createHandler = function (someClosedOverValue) {
return function () {
doSomethingWith(someClosedOverValue);
};
}
var blah = function () {
var myObject = jQuery('blah');
// We want to enable the closure to access 'red' but not keep
// myObject alive, so use a special createHandler for it:
var myClosureWithoutAccessToMyObject = createHandler('red');
doSomethingElseWith(myObject, myClosureWithoutAccessToMyObject);
// After this function returns, and assuming doSomethingElseWith() does
// not itself generate additional references to myObject, myObject
// will no longer have any references and be elligible for garbage
// collection.
}
Because jQuery(selector) might end up having to run expensive algorithms or even walk the DOM tree a bit for complex expressions that can’t be handled by the browser directly, it is better to cache the returned object. Also, as others have mentioned, for code clarity, it is better to cache the returned object to avoid typing the selector multiple times. I.e., DRY code is often easier to maintain than WET code.
However, each jQuery object has some amount of overhead. So storing large arrays of jQuery objects in global variables is probably wasteful—unless if you actually need to operate on large numbers of these objects and still treat them as distinct. In such a situation, you might save memory by caching arrays of the DOM elements directly and using the jQuery(DOMElement) constructor which should basically be free when iterating over them.
Though, as people say, you can only know the best approach for your particular case by benchmarking different approaches. It is hard to predict reality even when theory seems sound ;-).
Can I replace s standard DOM functions like removeChild to show for example alert before some node removed from parent? Something like that but my example is with errors
var original = node.removeChild;
node.removeChild = function(node, original){
alert('message');
original(node);
}
If you want to apply this across the document do this
var original = Node.prototype.removeChild;
Node.prototype.removeChild = function(node) {
//custom logic
original.apply(this, arguments);
}
If you want to apply the change only to a selected node then
var original = node.removeChild;
node.removeChild = function(node){
//custom logic
original.apply(this, arguments);
}
First of all, the usage of the new keyword is completely incorrect. That will severely change the behaviour. function is an instance of Function, the "mothership" of all JavaScript functions.
When using the new keyword, the function will be immediately executed and the behaviour very different from what you expect. See Section 13 of the ECMA Language Specification for more details on how to create function objects. Read it here, at page 98.
Second, it is strongly discouraged to alter any native object prototype. It leads to the most tedious and painful bugs in human history. Anybody coming in after you to edit the code will spend a long time before figuring out where that alert originates from.
Keep those two actions separate, they are completely unrelated. Wrapping them up in a prototype function is very bad design, for the above reason any many more, such as:
Using for in loops. You will iterate through more properties that you should if you forget to use hasOwnProperty.
Yourself and other developers will have a hard time figuring out why random things are happing with a basic DOM Node remove operation occurs.(you will forget, happens to everybody).
I am going to be radical an just say no. While it may technically work on the browsers you care about and be the basis for prototype.js, you should never modify DOM objects, or their prototypes, like this.
There is a long post on the topic: http://perfectionkills.com/whats-wrong-with-extending-the-dom/ but the TL;DR is that these are "hosted objects" and modification to their behavior is not guaranteed. It may work today on browser x but there's no guarantee about browser y or even x tomorrow.
Your implementation looks fine, except that you don't need to pass original as an argument there.
var original = node.removeChild;
node.removeChild = function(node) {
alert('message');
original(node);
}
I'm using mootools:
I can't figure out how to use a variable when using an addEvent.
I want to use a for next loop to set values in a loop:
for (x=0;x<num;x++){
var onclickText = 'function (){onclick="addPageMoveEvent('+x+'"); }';
$('pageNum'+x).addEvent('click', onclickText);
}
>
I've search forums but not found any help.
Any help would be great.
Thanks
The addEvent method in MooTools accepts two arguments:
myElement.addEvent(type, fn);
Arguments:
type - (string) The event name to monitor ('click', 'load', etc) without the prefix 'on'.
fn - (function) The function to execute.
It does not take a string and passing a string such as "myFunction()" or "function() { myFunction(); }" will not work.
Since you are inside a loop, and the variable x will share the environment, you need to wrap its value inside another closure. One way is to use an additional closure:
$("pagenum" + x).addEvent("click", (function(value) {
return function() { addPageMoveEvent(value); }
})(x));
See all questions on StackOverflow regarding this particular problem of creating closures within loops.
Also worth checking out is this MDC article - Creating closures in loops: A common mistake
Warning: this first example will not work! Read on for an explanation.
You are confusing onclick HTML syntax with the MooTools addEvent. Try
for (var x=0;x<num;x++){
$('pageNum'+x).addEvent('click', 'addPageMoveEvent('+x+');');
}
This is simpler and cleaner, but might still not do what you want. This code will call the function addPageMoveEvent every time the link is clicked... is that what you want?
Since MooTools doesn't allow the above method, you must use the following:
A programmatically more interesting and less hazardous way to do the same would be:
factory = function (x) { return function() { addPageMoveEvent(x); }; };
for (var x=0;x<num;x++){
$('pageNum'+x).addEvent('click', factory(x));
}
This uses a factory for creating closures that hold your values of x... rather complex code, but it's the purist way. It also avoids using the scary eval that occurs because you feed addEvent a string. (It seems that MooTools doesn't like the other option anyway.)
That a use case for mootools pass method.
for (x=0;x<num;x++){
$('pageNum'+x).addEvent('click', addPageMoveEvent.pass(x));
}
Pass internally creates a closure that holds x in the his scope, so when the click event is fired it has the right value cause its not the same from the for loop.
(I'm using prototype.js here, but I imagine the same holds true across other libraries as well)
I often find myself writing code like this:
var search_box;
Event.observe(window, 'load', function() {
search_box = $('search_box');
});
function doSomething(msg) {
search_box.innerHTML = msg;
}
Rather then writing it simply like this:
function doSomething(msg) {
$('search_box').innerHTML = msg;
}
My intention is to avoid having to traverse the entire DOM searching for the "search_box" element everything I need access to it. So I search for it once on page load and then stick the reference in a global variable. However, I don't recall ever seeing anyone else do this? Am I needlessly making my code more complex?
This is called premature optimization.
You are introducing a global variable to optimize something you have not profiled.
Your assumption that the $ "traverses the DOM" is incorrect. This function is implemented using document.getElementById which is the fastest way to access an element in the DOM.
I suggest coding your javascript using basic programming best practices such as avoiding global variables, and not optimizing without profiling. Once your application is working as expected, then you should profile it (using firebug) and address the area(s) where it is slow.
I usually do the same thing, the reason you don't see it often is probably because you don't see well written code often that's optimized ( nevermind the whole preoptimization is evil thing ) - I say if you can optimize it without headaches then why not?
Realistically speaking though that's a very very trivial DOM lookup, you should only begin to worry if you're iterating through dozens of elements and being vague in the selectors.. so I wouldn't worry too much about it unless you can really notice certain parts of your web page loading rather slowly, in which case you should store the multiple elements you access in the outer scope's variable.
Good:
(function() {
var els = $$('.foo span'); // also better to specify a context but I'm not sure how that's done in Prototype, it's the second param in jQuery.
function foo() {
els.something();
}
els.somethingElse();
})();
Bad:
(function() {
var els = $$('.foo span'); // also better to specify a context but I'm not sure how that's done in Prototype, it's the second param in jQuery.
function foo() {
$$('.foo span').something();
}
$$('.foo span').somethingElse();
})();
I decided to spend a bit of time doing some testing to get some hard data. The answer is that preloading the elements into global variables is twice as efficient as accessing them using the DOM getElementById method. (At least under FF 3.6).
Subsequent accesses to the objects is also more efficient using the global variable method, but only marginally so.
I find myself doing a lot of this kind of JQuery:
$('.filter-topic-id').each(function () {
var me = $(this);
if (me.text() == topics[k]) {
me.parent().show();
}
});
I store $(this) in a variable called me because I'm afraid it will re-evaluate $(this) for no reason. Are the major JavaScript engines smart enough to know that it doesn't have to re-evaluate it? Maybe even JQuery is smart enough somehow?
They are not smart enough to know not to revaluate $(this) again, if that's what your code says. Caching a jQuery object in a variable is a best practice.
If your question refers to your way in the question compared to this way
$('.filter-topic-id').each(function () {
if ($(this).text() == topics[k]) { // jQuery object created passing in this
$(this).parent().show(); // another jQuery object created passing in this
}
});
your way is the best practice.
Are the major JavaScript engines smart enough to know that it doesn't have to re-evaluate it?
No. But if you are using jQuery you are presumably aiming for readability rather than necessarily maximum performance.
Write whichever version you find easiest to read and maintain, and don't worry about micro-optimisations like this until your page is too slow and you've exhausted other more significant sources of delay. There is not a lot of work involved in calling $(node).
You could try to profile your code with Firebug and see if using $(this) many times slows your app or not
There is no good way that a javascript can determine that the following is true:-
fn(x) == fn(x);
Even if this was possible not calling the second fn could only be valid if it could be guaraneed that fn has not have other side-effects. When there is other code between calls to fn then its even more difficult.
Hence Javascript engines have no choice but to actually call fn each time it is invoked.
The overhead of calling $() is quite small but not insignificant. I would certainly hold the result in a local variable as you are doing.
this is just a reference to the current DOM element in the iteration, so there's little or no overhead involved when calling $(this). It just creates a jQuery wrapper around the DOM element.
I think you'll find that calling the jQuery function by passing a dom element is perhaps the least-intensive of ways to construct the object. It doesn't have to do any look-ups or query the DOM, just wrap it and return it.
That said, it definitely doesn't hurt to use the method you're using there, and that's what I do all the time myself. It certainly helps for when you create nested closures:
$('div').click(function() {
var $this = $(this);
$this.find("p").each(function() {
// here, there's no reference to the div other than by using $this
alert(this.nodeName); // "p"
});
});