What is better to use, the each function it self or using some smart selectors to do the same thing.
Example:
EACH FUNCTION
$('nav#mainNav > ul > li > ul').each(function(){
$(this).closest('li').addClass('hasSub');
});
SELECTOR
var addClass = $('nav#mainNav > ul > li > ul').closest('li').addClass('hasSub');
Both of these do the same thing, however is there any point to doing one over the other?
Explicitly iteration (.each) should be used when applying logic that varies between elements. Otherwise, implicit iteration will do just fine.
Your code sample is almost exactly the same as the .each API page:
Note: most jQuery methods that return a jQuery object also loop
through the set of elements in the jQuery collection — a process known
as implicit iteration. When this occurs, it is often unnecessary to
explicitly iterate with the .each() method.
For example, instead of using a selector to select uls nested inside lis and traverse a level up, you can also use .each applying a logic test for lis that contain uls:
$('#mainNav > ul > li').each(function() {
if ($(this).children('ul').length) {
$(this).addClass('hasSub');
}
});
Though this is more verbose, it does essentially the same thing.
For code-golf purposes, as mentioned in the comments and Bergi's answer, li:has(ul) would be the shortest way of achieving this without requiring a .closest() call. Though :has is not essentially the same as the children selector - :has looks for descendants (various levels deep) while > looks for direct children (1 level deep) - I believe it should work as well for this specific use case.
You could use the jQuery .has() method instead which reads a lot better:
var addClass = $('nav#mainNav > ul > li').has('ul').addClass('hasSub');
Both of these do the same thing, however is there any point to doing one over the other?
Yes. The second one is shorter and more performant (since .each is called anyway internally from those methods). There is absolutely no reason to incorporate an each here - it's just as pointless as using .each(function(){ $(this).addClass(…); }).
Btw, maybe you're looking for the :has selector, which would shorten your expression even more:
$('#mainNav > ul > li:has(ul)').addClass('hasSub');
If you want to be sure that the ul is a direct child of the list item, you still may want to use the suggestion from the comments (the closest(li) is always the direct parent of the ul):
$('nav#mainNav > ul > li > ul').parent().addClass('hasSub');
Most jQuery methods will operate on a collection of elements, and automatically iterate as if you'd used .each(). So one does not normally use .each() in simple cases like that.
However, if you need to perform multiple operations based on each element, it may be necessary to use .each(). E.g.
$('nav#mainNav > ul > li > ul').each(function () {
$(this).closest('li').addClass('hasSub');
$(this).closest('.otherClass').removeClass('.otherClass');
});
You could write this without .each() by using .end():
$('nav#mainNav > ul > li > ul')
.closest('li').addClass('hasSub').end()
.closest('.otherClass').removeClass('.otherClass');
but I believe .each() makes the intent clearer (although indentation, like this, does a pretty good job as well).
Related
I have this jQuery code:
$("#Filter ul input:checked").each(function () {
if (!$(this).prop("disabled") && !$(this).hasClass("ignoreInput")) {
Can this be written in only one selector? Right now I'm taking in too many elements to test with the if statement.
Is it also better to use .find(selector) instead of writing all in one selector?
$(document.body).find("#Filter ul ...)
You could use a combination of :not() along with the attribute selector, like this:
$("#Filter ul input:checked:not([disabled],.ignoreInput)").each(function () {
// your logic here
});
Is it also better to use .find(selector) instead of writing all in one selector?
This makes little to no performance difference.
I have a simple structure like:
HTML
<ul id="costsDropdown">
<li data-position="bla bla"></li>
</ul>
and I want to change each "data-position" attribute of my list Elements.
My first Jquery Shot was this here:
$("#costsDropdown ul").each(function() {
$("li").attr("data-position", "TEST-VALUE123");
});
but it doesnt work, I think my selector are wrong...
could anyone give me a hint please?
Thanks for any help!
Greetz
Your selectors are a bit off
$("#costsDropdown ul").each
That is trying to select the child ul of the container #costsDropdown (which is the ID of the ul) - what you want is:
$("#costsDropdown li").each(function() {
$(this).attr("data-position", "TEST-VALUE123");
});
ID's are unique - no need to double up the selector with an ID and the type of element it is.
Note that I used $(this), not $("li"), inside the each callback. $("li") selects all li elements, anywhere on the page; we just want a jQuery wrapper for the one specific one we're handling inside the each.
In fact, the each is completely unnecessary because of the set-based nature of jQuery; if you use the .attr setter, it sets the attribute on all elements in the set:
$("#costsDropdown li").attr("data-position", "TEST-VALUE123");
That will set the value on all of the li elements inside #costsDropdown.
If you need to set separate individual values on the individual li elements, you still don't need each (though it's fine if you want to use it); you can use the version of attr that accepts a callback that it uses to find out what value to set:
$("#costsDropdown li").attr("data-position", function(index) {
return "Test value " + index;
});
That will set "Test value 0" on the first li, "Test value 1" on the second, etc. And like the each example above, if you need to, you can use this within the callback to refer to the li for that call (possibly using $(this) to wrap it if you need a jQuery wrapper).
$("#costsDropdown ul") matches no elements, it has to be $("#costsDropdown") (#costsDropdown is the ul).
And even that is unnecessary. Go
$("li[data-position]").attr("data-position", "TEST-VALUE123");
instead.
I am trying to build an array of fruits in JavaScript and to do so, I need to get text values of all li children of my ul element. I've made some good progress, in that I was able to find the number of children my list had.
$("#fruits-list > li").length
=> 6
I can also find elements by index. Here, I found the first element using this
$("#fruits-list > li")[0]
=> <li id="apple">Apple</li>
I'm able to get the element, but I'm unable to get the test value of it. Here, I'm trying to just get the text value apple, and I have tried just about everything. I've tried
$("#fruits-list > li")[0].text
$("#fruits-list > li")[0].html
$("#fruits-list > li")[0].val
I got undefined for all three of them. I also tried adding () at the end of all of them, but got TypeError: Is not a function.
I could not find an answer here on SO. Any help is appreciated. Thanks in advance.
When you use the bracket notation with an index on a jQuery element, you get a DOM element. Those objects can't be called with jQuery functions directly. Use eq instead.
Use
$("#fruits-list > li").eq(0).text()
Note that if your end goal is to build an array of the texts of the LI elements, you can simply do
var arr = $("#fruits-list > li").map(function(){ return $(this).text() }).get();
text, html and val are not properties of the DOM, they are jQuery methods.
You could simply do a natural JS method.
document.querySelector("#fruits-list > li").textContent; //or innerText for legacy IE
querySelector returns the first item of a list of elements. (if you use jQuery, however, I suggest dystroy's answer)
When you access $("#fruits-list > li")[0], you are getting an HTMLElement object, which is why you cannot use jQuery's function on it. dystroy's solution let's you use jQuery to get the text because eq returns an instance of jQuery. You can also do
$("#fruits-list > li")[0].textContent
I have created a Responsive Sub-Menu for a Responsive Website that actives at equal to or less than 768px. I have it set up to where jQuery removes the link for the Parent List Item and displays the sub-menu onClick.
The problem is, there is a Parent that has no children and I am trying to only remove the link(href) of Parent Elements that do have children. But, I even applied it in an If statement so it wouldn't remove the parent link if it has no children, that didn't work. So I tried siblings, which I thought would make more sense. But it is still not behaving as so.
Here is my jQuery for this:
if (jQuery(".navigation > ul > li > a").siblings("ul")){
jQuery(".navigation > ul > li > a").removeAttr("href");
};
You may see an example here: http://stlredtails.com/construction/
Edit: The Contact link is the link that has no "ul" children, practical terms, there is no sub-menu.
Thank you!
your if statement always returns true and your following command removes the href attribute from all matched elements. you will need to loop through the matching anchors and process an if statement for each one.
jQuery(".navigation > ul > li > a").each( function() {
if(jQuery(this).siblings("ul").length) {
jQuery(this).removeAttr("href");
}
});
I'm not exactly sure if I understand what you are asking, but it sounds like you may be in need of jQuery's :empty and/or :parent selectors.
Try something like:
$(".navigation > ul:parent > li > a")
jQuery :empty documentation
jQuery :parent documentation
jQuery Siblings Not Working?
It is working. Yet it always evaluates to a jQuery wrapper object even if it matched nothing, and that value is truthy so your if-block always executes. Check for the .length-property to be not 0.
I have it set up to where jQuery removes the link for the Parent List Item and displays the sub-menu onClick.
There is absolutely no reason to remove the href attribute. To prevent following the link even if you clicked ("activated") it, call preventDefault() on the event object in the click event handler!
I certainly know how to select confine a jQuery selector to children of a particular element each time I make the selection (i.e. $('#left ul') selects all ul that are children of #left). While that's simple enough, if I'm going to perform a bunch of operations to various children of #left, it's a bit cumbersome to have to keep writing $('#left ul'), $('#left p'), $('#left ...'). Instead, I'd prefer to just be able to temporarily confine the potential selectors to only children of #left. For example:
...some code that established #left as the "scope" of any future selectors;
$('ul').css(...); //actually selects $('#left ul')
$('p').css(...); //actually selects $('#left p')
...some code that re-established the full DOM as the "scope";
$('ul').css(...); //selects all ul, not just children of #left
$('p').css(...); //selects all p, not just children of #left
Thanks!
Just rely on context and find.
var $l = $('#left')
, ul = $l.find('ul')
, p = $l.find('p');
The "$" method can receive 2 arguments: the selector and the context. So, you can use the following syntax:
$('.children_selector',$('.the_parent_selector'))
But, to avoid the $(selector,context) usage. You can write a function, "overriding" the $ function, and write your code inside. Take a look:
(function($)
{
//here the $ selects the elements inside the first element with the class xpto
})(function(a){return $(a,$('.xpto')[0]);});
Anyway, I would dislike to find this kind of construction. For maintenance purposes is better to get more line of codes than fewer lines of obscure code.