get a total of jquery's .each() - javascript

I'm using jquery's .each() to iterate over a group of li's. I need a total of all the li's matched. Is the only way to create a count variable outside the .each() and increment this inside the .each()? It doesn't seem very elegant.
var count;
$('#accordion li').each(function() {
++count;
});

Two options:
$('#accordion li').size(); // the jQuery way
$('#accordion li').length; // the Javascript way, which jQuery uses
Since jQuery calls length under the hood, it's faster to use that instead of the size() call.

Well, I just saw this question, and you already accepted an answer, but I'm going to leave one anyway.
The point of the question seems to be concerned with incrementing a counter.
The fact is that jQuery's .each() method takes care of this for you. The first parameter for .each() is an incrementing counter, so you don't need to do it yourself.
$('#accordian li').each(function(index) {
// index has the count of the current iteration
console.log( index );
});
So as you can see, there is an elegant solution built in for you.

$('#accordion li').length;

Ok so the best way to do this is as follows: firstly map the wrapped set to a variable so you never have to do the sizzle dom lookup again:
var $myListItems = $('#accordian li');
Note: my preference is to put $ at the beginning of any vars that are a jQuery wrapped set, hence $myListItems as opposed to myListItems
Then set a var outside the each function that has the length of the wrapped set:
var myLength = $myListItems.length;
Note the lack of a $ here as the var is not a jQuery object.
Now run the each function with a variable for the index.
$myListItems.each(function(index){
// myLength has the length
// index has the current, 0 based index.
});
You now have what you've asked for in the OP with only one look up and so need to fumble in the each function with having to know the contents of the wrapped set just to know the length of it. the beauty of this over a counter is that on every iteration of the each you already know the length.

You can try this
const count = $('#accordion li').each(function() {
// Do something
}).length;
It works because .each() returns a jQuery object which has a length property.

One of the big issues, which I have not found a fix for yet, is if you have two sets of the same thing you will iterate through.
For instance, 2 table heads that you are iterating through number of column headers. If you use length on ( $item.children().find('thead tr th').each... ) for your each clause and you have 2 tables it will double the amount of your length that you are trying to walk through potentially [5 column headers in each table will return length of 10].
You could do both on a id name and have those different but if adding dynamically then this can become a headache.
The only way I found once inside the foreach is to use $(this):
$item.children().find('thead tr th').each(function(i, th) {
// I am working with a table but div could be used, or something else.
// I have 2 tables with 5 column headers, the below len return
var len = $(this).closest('table').find('thead tr th').length;
if (i < len) {
// ...your code here
}
});

Related

How to count through a tags and apply +1rem padding to each one?

I'm trying to style some breadcrumbs programmatically, because I don't know how many breadcrumbs there will be (due to child categories being set up by the client, etc). I've tried a few ways to count through the a tags and do this, without much success. Basically I just want each a tag to have one more 1rem of left padding than the previous one. Here is my code:
for(var i = 0; i < jQuery('.woocommerce-breadcrumb a').length; i++){
jQuery('.woocommerce-breadcrumb a:nth-child(' + i + ')').css('padding', i + 'rem');
}
Please excuse my bad coding as I'm just a novice with JavaScript/jQuery. And thank you in advance for any help.
If you want to use jQuery inside of pure CSS, you don't have to use the nth-child selector to get each a tag, you could just loop through each a tag by using the jQuery .each() function.
Something like this should work:
$('.woocommerce-breadcrumb a').each(function(i, ele) {
$(ele).css('padding-left', i + 'rem');
});
This will loop through every tag that matches .woocommerce-breadcrumb a, and then apply the relevant css. the .each() function let's you declare an index i in the function, so that lets us use incrementing padding-left for each element.
See this following code pen - http://codepen.io/anon/pen/vLyOMj - for an example as well.
jQuery.each automatically passed an index so there is no reason to do the for loop.
$('.woocommerce-breadcrumb a').each(function(i) {
$(this).css('padding', i + 'rem');
});
https://jsfiddle.net/SeanWessell/79Lnpze8/
I'd suggest, based on your stated requirements:
// selects the <a> elements contained within an element with a
// class-name of 'woocommerce-breadcrumb',
// uses the css() method to update the 'padding-left' property
// with an anonymous function, in which the first argument
// here 'i' (the index of the current element in the set returned
// by the selector):
jQuery('.woocommerce-breadcrumb a').css('padding-left', function (i) {
// returns a concatenated string, formed by the number of 1
// added to the current index and finally with the units
// added:
return 1 + i + 'rem';
});
References:
css().

jQuery append() for multiple elements after for loop without flattening to HTML

I have a loop:
for (index = 0; index < total_groups; index += 1) {
groups[index].list_item = $(list_item_snippet);
// Closure to bind the index for event handling
(function (new_index) {
groups[index].list_item.find('.listing-group-title')
.html(groups[index].Group.Name)
.click(function(e){
fns.manageActiveGroup(new_index, groups);
return false;
});
})(index);
// Append to DOM
mkp.$group_listing.append(groups[index].list_item);
};
I would rather not call append() each time the loop fires.
I know that I could use a String and concatenate the markup with each loop iteration and append the string to mkp.$group_listing at the end, however this flattens the object and the bindings are lost (I am not relying on IDs).
Is there a way to perhaps add my objects to an array and append them all in one go at the bottom without flatening to HTML?
Assumptions:
$(list_item_snippet) contains some HTML defining a list item (and includes an element with class .listing-group-title).
groups is a block of JSON defining a 'group' in my script
The closure works perfectly
Edit:
Found that I can use the following syntax to append multiple elements:
mkp.$group_listing.append(groups[0].list_item, groups[1].list_item );
But i obviously need to automate it - it's not an array it's just optional additional function parameters so I'm not sure how to do this.
To append an array of elements to a selector you can use this:
$.fn.append.apply($sel, myArray);
In your case, since it's actually the .list_item property of each array element that you need you can use $.map to extract those first:
$.fn.append.apply(mkp.$group_listing, $.map(groups, function(value) {
return value.list_item;
}));
Instead of bind it the way you've done, if you bind it using on() like below,
$(document).on('click', '.listing-group-title', function() {
// click handler code here
});
You can flatten the HTML and append it in one statement and it'll still work.
Note: For better efficiency, replace document in the above statement to a selector matching the closest parent of .listing-group-title
Yes. Use the jQuery add method to add all your items to a jQuery object. Then append that one object.
http://api.jquery.com/add/
EDIT: Example:
var arr = $();
for (index = 0; index < total_groups; index += 1) {
groups[index].list_item = $(list_item_snippet);
// Closure to bind the index for event handling
(function (new_index) {
...
})(index);
// Add to jQuery object.
arr.add(groups[index].list_item));
};
mkp.$group_listing.append(arr);

Determine # of items in jQuery array and loop through them?

I have an AJAX script that receives a string from a mySQL query returned by PHP.
This string is then parsed and put into an array in Jquery and the results are printed to the screen using .html()
The length of this array varies from 0 items to many, how would I count the items in the array then loop through and print them to the screen.
Here is my UPDATED code per the advice below, though I am still not sure if the for loop goes inside the .html() function or outside?
UPDATED CODE TO INCLUDE .each()
UPDATE 2: Replace (this) in the .html() function with the element I want the text written in and it is working partially, issue is now it is only printing the last item in the array?
UPDATE 3: Seems you can only have a single .html() function run, for instance if I add another .html() statement under the one that is returning the last item in my array it will only now echo on the screen the test value.
$("#n_detail").html(partsArray[index]+"<br />").addClass('jText').fadeTo(900,1);
$("#n_detail").html("Test").addClass('jText').fadeTo(900,1);
It will only print "Test", not the last item in the array like it was previously?
<script type="text/javascript">
$(document).ready(function() {
$("#primary").change(function()
{
$.post("lib/ajax_load_job_detail.php",{ _primaryid_n:$(this).val() } ,function(data)
{
var string = data;
var partsArray = string.split('|');
$("#n_detail").fadeTo(200,0.1,function() //start fading the messagebox
{
$.each(partsArray, function(index) {
$("#n_detail").html(partsArray[index]+"<br />").addClass('jText').fadeTo(900,1);
});
});
});
});
});
Sample value of array partsArray[0]12/12/2005, partsArray[1]This is a sample note from December, etc...
partsArray.length
will give you the items in the array. You can loop either with
for(var i=0;i<partsArray.length;i++){
or using the jquery addon
$.forEach
If you are iterating through an array then you could use the jQuery function each().
Here's a link to the docs: http://api.jquery.com/jQuery.each/
Here's a sample from the docs using your array:
$.each(partsArray, function(index, value) {
alert(index + ': ' + value);
});
EDIT - based on a comment the OP added to another answer, here's a better example using the OPs code:
$.each(partsArray, function(index, value) {
value.addClass('jText').fadeTo(900,1);
});
EDIT 2 - you need the part of the code that is per element of the arry inside the loop and based on your edits I think it should look like this:
$.each(partsArray, function(index) {
$(this).append(partsArray[index]+"br />").addClass('jText').fadeTo(900,1);
}
Cheers,
James
Here is a typical loop structure:
var partsArray = string.split('|');
for(var x=0;x<partsArray.length;x++) {
//...your code ...
//x is the index., so partsArray[x] is the current element
}
Use for ... in, it's significantly faster than jQuery's $.each method, and isn't much different - it provides you with the index of the item in i, rather than the value.
for (var i in partsArray)
{
// You can access values via...
console.log( partsArray[i] );
// Alternatively, this will make it an exact clone of $.each
var value = partsArray[i];
console.log( value );
}

Selecting from a list of previously selected elements via jQuery

I'm having a bit of a brain fart here, and hoping someone can help me find a 1-line solution to this problem, without having to call .each().
So I get a list of all checkboxes within a container like this:
var checkboxes = $(':checkbox', '#surveyModal');
At some point later, I need to find out if any (or none) of the checkboxes are checked within that list.
I expected something like these to work:
$(':checked', checkboxes)
// or
checkboxes.attr(':checked')
// or
$(checkboxes).attr(':checked')
But it doesn't. The only thing I've had success with is calling each() and then checking each individually. But that means I'll have to keep a separate variable (.e.g. someAreChecked at a higher-level scope, which I don't feel is optimal.
checkboxes.each(function () {
if ($(this).attr('checked')) {
someAreChecked = true;
}
});
I was hoping that I can easily in a single line do such a check:
if (checkboxes.('get checked count') == 0)
{
}
Thanks in advance.
The filter function is what you're looking for :)
checkboxes.filter(':checked').length;
.attr returns the value of an attribute, and you have to pass the attribute's name to it, not a selector.
Just use .is instead.
Description: Check the current matched set of elements against a
selector, element, or jQuery object and return true if at least one of
these elements matches the given arguments.
$(checkboxes).is(':checked')
This should do it:
$("input[type=checkbox][checked]").length

Why is this jquery so slow?

I'm using a jquery plugin which fixes the headers on a html table that I generate. Unfortunately the performance of the plugin is very slow and I've narrowed it down to the following code:
var $tbl = $(this);
var $tblhfixed = $tbl.find("thead");
$tblhfixed.find("th").each(function ()
$(this).css("width", $(this).width());
});
This is taking about 40 seconds on a table with 2,000 rows in ie. Does anyone know why it's so slow and more importantly how can I make this faster? I've tried many other plugins and this is the only one which works how I want it to. Thanks for any help
I guess you faced with the same problem that I had some time ago. It called a "Recalculate layout" or something.
Try to separate this script onto two loops, like this:
var $tbl = $(this);
var $tblhfixed = $tbl.find("thead");
var widths = [];
// 1.
$tblhfixed.find("th").each(function ()
widths.push($(this).width());
});
// 2.
$tblhfixed.find("th").each(function (index, element)
$(this).css("width", widths[index]);
});
First one will calculate all the widths. Second one will apply them to TH's
UPD:
You may increase performance by placing this code between 1. and 2.:
$tblhfixed.hide();
and show it again after 2.:
$tblhfixed.show();
the culprit is probably the .each.
The reason is that when you iterate using .eachinstead of a normal loop, you call a function for each iteration. a function call has a pretty big overhead in this case, since a new callstack has to be created for each iteration.
To make it faster change
$tblhfixed.find("th").each(function ()
$(this).css("width", $(this).width());
});
to
var elms = $tblhfixed.find("th");
for(var i=0, elm;elm = elms[i];i++) {
elm.css("width", elm.width());
}
First, you should use find() only when you need to pass through all nested nodes. Right here you can use children().
Second, each time $(this) creates new instance of jQuery object, while you can create it once:
var $this = $(this);
Each time $(this).width() is recalculated. Make sure that you need it to be recalculated. Or do:
var tableWidth = $this.width();
And third, according to #Martin Jespersen, each iteration the function object is created.
Also you don't need jQuery here at all. You can access DOM directly:
var tableWidth = ...; // get the table width the way you want
var thead = this.tHead;
var thRow = thead.rows[0];
for (var i = 0; i < thRow.length; ++i) {
thRow.cells[i].style.width = tableWidth + "px";
}
you should not repeat $(this) inside your function passed into .each(). wrapping an element has non-trivial overhead, which is not ok when you have 20k elements. you want to eliminate as much work as possible inside the .each() call, or eliminate it altogether.
Also, why query find() twice, when you can do this instead, which should give you the same results:
$ths = $('table thead th'); //or tableid, or something
$ths.css('width', $ths.width());
it appears that $.width() is 99 times slower than the native get(0).clientWidth, check out this test: http://jsperf.com/jq-width-vs-client-width

Categories

Resources