I find myself doing different things to the same jQuery object a lot.
So instead of doing something like this:
var thing = $("pile_o_selectors");
thing.blah;
thing.blah_2;
//...
thing.blah_n;
or even worse
$("selectors").blah;
$("selectors").blah_2;
//...
$("selectors").blah_n;
So, I made a little jQuery plugin to let me do this:
$("selectors").do(function(){
this.blah;
this.blah_2;
this.blah_n;
});
Is there a built in jQuery function that already does this? Or even a plugin that already exists and is tested and mature so I don't have to put effort into making sure mine works in all cases? I tried searching the web a bit, but I don't even know what to call it.
Not 100% sure what those 'things' you want to do are, but can they simply be chained?
http://www.w3schools.com/jquery/jquery_chaining.asp
i.e. $("#p1").css("color","red").slideUp(2000).slideDown(2000);
You can just chain them. For example:
$('selector')
.hide()
.text("Hello, world!")
.fadeIn();
This works for most methods that have nothing obvious to return; in that case, they return the jQuery object again, making it possible. However, if they need to return something, then they can't return the jQuery object, and you cannot chain any further.
Related
I was recently assigned a very small but complex task in jQuery, the requirement was quite simple, given the following HTML :
<div>
<span id="myid2151511" class="myclass23462362">....foobar....</span>
<span id="myid2151512" class="myclass23462362">....YoLO....</span>
<span id="myid2151513" class="myclass23462362">....lalal....</span>
<span id="myid2151514" class="myclass23462362">....foobar....</span>
</div>
What i have to do i recursively go through all the span under div, With a certain id and check if the values contained in the spans is foobar, So i can up with the following jQuery code:
$(function(){
$('div [id^="myid"]:contains("foobar"):last').css({'background' : 'rgb(227, 216, 22)' })
});
FIDDLE HERE
Its quite a complex bit of code by itself, but the jQuery documentation made it a cakewalk for me as for as understanding the code is concerned.
By now i am comfortable writing code like so in jQuery:
$('some-Element').somemethod().anothermethod().yetanothermethod();
Every function returns a value in the above jQuery statement, so chain ability becomes a reality.
but when i see code like so.
$('div [id^="myid"]:contains("foobar"):last').css({'background' : 'rgb(227, 216, 22)' });
I am thrown a bit off the hook(although i managed to write the above line myself), notice how alot of the filtering is done by a selector :last and :contains, to me they appear to be working much like some kind of a jQuery method. So my question is, how do these selectors in jQuery work in comparison to jQuery methods ?
If anybody could explain or give me a vague idea, it would be Fantastic.
EDIT ::
well to clarify my question in one line, to me $(".someClass").eq('10'); makes sense, but somehow $(".someClass:eq(10)") does't , i mean it works, but how on earth is it implemented internally ?(I wrote this edit after reading the answers below, and well this question has been thoroughly answered by now, but this edit is just to clarify my question.).
That's an interesting question. The short answer is they both accomplish the same thing. Of course though, there's always more to the story. In general:
$('div [id^="myid"]:contains("foobar"):last').css({'background' : 'rgb(227, 216, 22)' });
Is equivalent to:
$("div").find("[id^='myid']").filter(":contains('foobar')").last().css({'background' : 'rgb(227, 216, 22)' });
Most of the time when you call $(), jQuery is calling document.querySelectorAll(). This is a browser implemented function that grabs elements based on a selector. That complex string you create is passed to this method and the elements are returned.
Naturally, things implemented by the browser are faster than JavaScript so the less JavaScript and more C++, the better. As a result, your example passing everything as a selector is likely to be faster as it just sends it all to the browser as one call and tells it "do it." Calling $(), contains(), last() on the other hand is going to call querySelectorAll multiple times and therefore it will likely be slower since we're doing more JavaScript as opposed to letting the browser do the heavy lifting in one shot. There are exceptions though. JQuery generally calls querySelectorAll. However, there are times when it doesn't. This is because jQuery extends what querySelectorAll is capable of.
For example, if you do something like $(".someClass:eq(10)") per the jQuery documentation:
jQuery has extended the CSS3 selectors with the following selectors. Because these selectors are jQuery extension and not part of the CSS specification, queries using them cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. To achieve the best performance when using these selectors, first select some elements using a pure CSS selector, then use .filter().
So in that case, while $(".someClass:eq(10)") might seem to be faster, in reality $(".someClass").eq(10) or $(".someClass").filter(":eq(10)") is going to be faster since the first call will be executed as JavaScript code. The latter two will first call querySelectorAll to select by class, then only use JavaScript to find the 10th element. When jQuery has to do the selection in pure JavaScript, it does it using the Sizzle engine which is fast, very fast, but not faster than native code in the browser. So again, the short answer is, they're the same thing, the long answer is, it depends. If you're interested in all the extensions that fall into that category, the link to the jQuery documentation I included lists them.
First of all, yes nikhil was right. ID is unique identifier and can be only used once. If you are willing to apply same styles to several elements, or you to use it to select several elements together use class attribute. But however, i couldn't understand your question. But maybe this could help
there is function in javascript which is widely supported by almost all major browsers
document.querySelectorAll("div [id^=myId]");
in fact you could write your own library (well not as advanced one like jquery but)
var $ = function(selector){
return document.querySelectorAll(selector);
}
// and then you could use it like this
var elementsWithMyId = $("div [id^=myId]");
// where elementsWithMyId will contain array of all divs which's id start with myId
so as i understood your question, No. there is no magic happening behind jQuery selections it's just browser built in function which is kinda shortened by jquery. of course they added tons of new features, which would work like this:
var $ = function(selector){
var elementsArray = document.querySelectorAll(selector);
elementsArray.makeBlue = function(){
for(var i = 0; i < elementsArray.length; i++){
elementsArray[i].style.backgroundColor = "blue";
}
// so elementsArray will now have function to make all of its
// div blues. but if you want to have chain like that, we have to return this array not just make all of it blue
return elementsArray;
}
elementsArray.makeRed = function(){
for(var i = 0; i < elementsArray.length; i++){
elementsArray[i].style.backgroundColor = "red";
}
return elementsArray;
}
return elementsArray;
}
// so now you can use it like this
// this returns array which has options make blue, and make red so lets use make blue first
// makeBlue then returns itself, meaning it returns array which has again options of making itself red and blue so we can use makeRed now
$("div [id^=myId]").makeBlue().makeRed();
and thats it!
I was just wondering which is the correct or most efficient way of navigating through the Dom using variables.
For example, can I concatenate selectors
var $container = '.my-container';
$($container).addClass('hidden');
$($container + ' .button').on('click', function(){
//something here
});
or should I use the jQuery traversal functions
var $container = $('.my-container');
$container.addClass('hidden');
$container.children('.button').on('click', function(){
//something here
});
Is there a different approach, is one best, or can you use them at different times?
The $ is usually used only when working with an actual jquery object. You generally shouldn't prefix anything with that unless it's really something from jquery.
Beyond that little bit though, performance-wise, your second bit of code is going to be faster. I made an example jsperf here: http://jsperf.com/test-jquery-select
The reason the second bit of code is faster is because (if I remember correctly) jquery caches the selection, and then any actions performed on that selection are scoped. When you use .find (which is really what you meant in your code, not .children), instead of trying to find elements through the entire document, it only tries to find them within the scope of whatever my-container is.
The time when you wouldn't want to use the second pattern is when you expect the dom to change frequently. Using a previous selection of items, while efficient, is potentially a problem if more buttons are added or removed. Granted, this isn't a problem if you're simply chaining up a few actions on an item, then discarding the selection anyway.
Besides all of that, who really wants to continuously type $(...). It's awkward.
I am using this function for easiness, as I am going to use fadeTo a lot:
function fade_to(div, speed, opacity, after_fade) {
$(div).fadeTo(speed, opacity, after_fade);
}
Then I am calling the same function for after_fade parameter:
fade_to('#div', 3000, 1, function() { fade_to('#another_div', 3000, 1)});
Is that a bad thing to do? Will I have speed/smoothness issues?
Is it better to just use jQuery's default fadeTo function?
Thanks!
There is no gain to be made with your method. Plus you are using the jQuery fadeTo function. There is nothing wrong with what you did, just no gain. You could save work with such a technique if for example you had less arguments in your custom function:
function fade_to(div, after_fade) {
$(div).fadeTo(3000, 1, after_fade);
}
fade_to('#div', function(){ fade_to('#another_div', $.noop); });
This would actually save you work by preventing you from having to enter speed and opacity arguments. You could also curry it like this
function Fade_to(speed, opacity){
return function(div, callback){
$(div).fadeTo(speed, opacity, callback);
}
}
Then you could make argument saving functions on the fly like
var fade_to_foo = Fade_to(3000, 1);
fade_to_foo('#div', function(){ fade_to_foo('#another_div'); });
Otherwise there is no reason not to just write it the jQuery way
$('#div').fadeTo(3000, 1, function(){ $('#another_div').fadeTo(3000, 1); });
It's a bad practice because you cannot use any more modifiers without applying them to every instance your function is called. Since you can chain modifiers in jQuery, and most would agree that doing so is a useful feature, you are disabling that useful feature for yourself or anyone else working on this code body.
If you want to add any additional animations or stylings, you'll have to select the object again through regular jQuery this time. Extra work, extra calls, no real benefit.
this practice is against the goal of using jQuery as a chainable, short-syntax library. however if this specific functionality is useful for a project, can be effective.
Mike,
I don't know that this is necessarily a 'bad' thing to do, as it might offer easier usability or something (not sure, given that I don't know the context of your example) that using the standard function given in an API wouldn't otherwise.
Actually, having a function call itself is using an idea in CS called 'recursion' which can be useful for traversing trees (you can google both recursion, and trees to get a better idea of what I'm referring to here), or performing some kind of mathematical operation (i.e. Euclidean algorithm).
I would say, that if you're doing this, it's a great idea to ask "why". You won't be gaining anything in terms of speed since you're passing the parameters an extra time, and your function isn't accomplishing anything extra compared to the API's implementation (at least that I'm seeing). If you were to make a habit out of this...let's say with a more
computationally taxing function...you might notice a slowdown.
I'm hoping to not just answer your question here, but to give you some further insight as to why it's generally a bad idea to do this. I agree with dunsmoreb, and Thomasdotnet as well. Good points!
-sf
Your approach is convenient. I doubt it will slow down the fade effect. There is nothing wrong with this approach in my mind. If you decided that your fade effect should pulse or blink before fading you would only need to modify your function to pulse/blink and then fade all calls to fade would then run the new routine. In this case it makes sense as it reduces code and improves maintainability. Ericosg does make a valid point though why not reduce the paramaters if they are going to be the same.
It is better to just use jQuery's default fadeTo function. just this.
I'm populating a list by cloning elements into it. Then I change attrs to make each item unique. They need to call a function on click, so I'm wondering if it's more efficient to use new_element.click(func); or new_element.attr('onlick','func();');
new_element.attr('onclick','func();');
Is:
inefficient (needlessly creating a new inline function from a string, that does nothing except call func and lose the this reference);
aggravating to put any complex code in, since it all has to be JS string escaped;
broken in IE, due to bugs in setAttribute.
Avoid. click()/bind('click') is there for a reason.
onclick has a number of limitations, including cluttering the DOM and only allowing one function at a time. So you should use click. See Quirks Mode for more information.
Directly referencing the function will be more efficient than having to interpret a string.
The lowest touch way of doing this, however, is this way:
$(links_selector).live('click', func);
links_selector will presumably be something like ul.listClass a.actionClass. This will not require anything to be done when new list elements get added.
Since you are using jQuery then make it this way
new_element.click(function(){
// your code
});
or you can bind the click event handler like
new_element.bind("click", function(){
// your code
});
Any difference in performance between the two is most likely going to be negligible. You should use the one that reads better, and that's element.click. (Plus, onclick has many disadvantages, as #Matthew Flaschen mentioned.)
We're considering switching our site from Prototype to jQuery. Being all-too-familiar with Prototype, I'm well aware of the things about Prototype that I find limiting or annoying.
My question for jQuery users is: After working with jQuery for a while, what do you find frustrating? Are there things about jQuery that make you think about switching (back) to Prototype?
I think the only that gets me is that when I do a selection query for a single element I have to remember that it returns an array of elements even though I know there is only one. Normally, this doesn't make any difference unless you want to interact with the element directly instead of through jQuery methods.
Probably the only real issue I've ever ran into is $(this) scope problems. For example, if you're doing a nested for loop over elements and sub elements using the built in JQuery .each() function, what does $(this) refer to? In that case it refers to the inner-most scope, as it should be, but its not always expected.
The simple solution is to just cache $(this) to a variable before drilling further into a chain:
$("li").each(function() {
// cache this
var list_item = $(this);
// get all child a tags
list_item.find("a").each(function() {
// scope of this now relates to a tags
$(this).hide("slow");
});
});
My two pain points have been the bracket hell, can get very confusing
$('.myDiv').append($('<ul />').append($('<li />').text('content')));
My other common issue has to do with the use of JSON in jQuery, I always miss the last comma,
$('.myDiv').tabs({ option1:true, options2:false(, woops)});
Finally, I've been using jQuery for about 6 months now and I don't think I'll ever go back to prototypes. I absolutely love jQuery, and a lot of the tricks they use have helped me learn a lot. one cool trick that I like is using string literals for method calls, I never really did that too much with prototypes.
$('.myDiv')[(add ? 'add' : 'remove') + 'Class']('redText');
(The only thing I can think of is that this is the element instead of a jQuery object in $("...").each(function)-calls, as $(element) is more often used then just the element. And that extremly minor thing is just about it.
Example of the above (simplified and I know that there are other much better ways to do this, I just couldn't think of a better example now):
// Make all divs that has foo=bar pink.
$("div").each(function(){
if($(this).attr("foo") == "bar"){
$(this).css("background", "pink");
}
});
each is a function that takes a function as parameter, that function is called once for each matching element. In the function passed, this refers to the actual browser DOM-element, but I find that you often will want to use some jQuery function on each element, thus having to use $(this). If this had been set to what $(this) is, you'd get shorter code, and you could still access the DOM element object using this.get(0). Now I see the reason for things being as they are, namely that writing $(this) instead of this, is hardly that cumbersome, and in case you can do what you want to do with the DOM element the way it is is faster than the way it could have been, and the other way wouldn't be faster in the case you want $(this).)
I don't think there are any real gotchas, or even any lingering annoyances. The other answers here seem to confirm this - issues are caused simply by the slightly different API and different JavaScript coding style that jQuery encourages.
I started using Prototype a couple of years ago and found it a revelation. So powerful, so elegant. After a few months I tried out jQuery and discovered what power and elegance really are. I don't remember any annoyances. Now I am back working on a project using Prototype and it feels like a step back (to be fair, we're using Prototype 1.5.1).
If you reversed the question - "What Prototype annoyances should I be aware of as a jQuery user?" - you would get a lot more answers.
Nope. Nada. Nyet.
.each:
jQuery (you need Index, even if you're not using it):
$.each(collection, function(index, item) {
item.hide();
});
Prototype (you're usually using the item, so you can omit the index):
collection.each(function(item) {
item.hide();
});
This is really only an annoyance if you're doing a lot of DOM manipulation. PrototypeJs automatically adds its API to DOM Elements, so this works in prototypejs (jQuery of course doesn't do this):
var el = document.createElement("div");
el.addClassName("hello"); // addClassName is a prototypejs method implemented on the native HTMLElement
Even without running the native element through the $() function.
PS: Should note that this doesn't work in IE.