Explaining by example:
$(selector).each(function () {
if (expression) {
$(this).next().remove();
}
....
});
Based on an expression I remove an element that also are a part of the selector, and in effect are removing the next element the .each() function will get.
The .each() function doesn't seem to care and are running it's code on the removed element breaking the iteration process on my code in the each function. Basically meaning a counter in the .each() function will count the deleted element even though it shouldn't.
Is there a way to refresh or update the .each() function so it skips elements that where removed after its initiation?
Better to filter your elements before:
$(selector).filter(function() {
if (expression) {
return false;
}
}).each(function() { .. });
I do not think you can modify the collection from inside the each call.
Even better, you can split this in two calls, since if you do all of it in one call only, the selector will be cached:
$(selector).filter(function() {
return expression;
}).next().remove();
// and then
$(selector).each(function() { ... }); // now *without* removed elements.
The first time you query the DOM with $(selector) you are given back a jQuery collection containing all the elements (specifically, references to the elements) that satisfy that selector. Any changes to the DOM do not affect that collection. If you happen to modify the DOM in such a way that the elements in the collection no longer satisfy the initial selector, this will not be reflected.
The correct solution to this problem is to not modify the DOM in the each loop, and basically find another way to solve your problem. But a simple (computationally expensive) solution is just to recheck the element on the initial selector upon iteration... you can use the .is method to achieve this:
$(selector).each(function () {
var $this = $(this);
if ($this.is(selector)) {
if (expression) {
$this.next().remove();
}
}
....
});
Related
For example, my element is a DOM element div, and within it are other divs.
So I generally use elem.querySelector('.whatever') to find a child within it. But I'd like to rename querySelector to find.
But how can I do this for all DOM elements, is there an easy way? I'd rather not just have "function find(elem, what)", but rather I'd like to do it on the element itself so "elem.find('.whatever')"
You can create a function to achieve the same thing, but will give extra work to the browser
HTMLElement.prototype.find = function(selector){
//or querySelector for one element depends on need
return this.querySelectorAll(selector);
};
elem.find('.whatever');
Warning! Modifying the core prototype is a bad practice.
Another solution from "connexo"
HTMLElement.prototype.find = HTMLElement.prototype.querySelector
You can also create your own version of "jquery" if you are afraid of modifying the core prototype
function $$(element) {
return {
find: function(selector) {
return element.querySelectorAll(selector);
}
}
}
//then just wrap any object
$$(elem).find(selector);
I almost always use the global document.querySelector form, and it's definitely too verbose for my taste.
While not exactly an alias, I really like this simple solution:
function get (selector, context=document) {
return context.querySelector(selector)
}
function getAll (selector, context=document) {
return context.querySelectorAll(selector)
}
// get single element
get("#id")
// get all elements
getAll("nav li")
// all within an element
getAll("li", element)
In 'jQuery' I can execute callback for all elements of a class like this way -
jQuery(form_id).find(".multiple_upldprev").each(function () {
But I want to execute only last element of the class. I tried this way -
jQuery(form_id).find(".multiple_upldprev").last(function () {
It doesn't work.
How can I do that?
last() will reduce the set of matched elements to the final one in the set. And it does not take a handler. You can use:
$(...).last().each(function() {
});
Which usually doesn't make sense since .last() just returns the element:
var $lastElement = $(...).last();
// Do something with $lastElement
In cases where you have a predefined handler each might make sense:
$(...).last().each(myHandler);
// The same can roughly be archived with:
myHandler.call($(...).get(-1));
You dont event need a callback.
Since you are interested only in executing a function on last element, You can just do this with a self executing function and the last element in a variable.
(function(){
var $last = jQuery(form_id).find(".multiple_upldprev").last();
// now use this $last
})();
Or for some reason you want to use callback by using each but still execute the callback function only for the last element then you can do this with the help of .is()..
jQuery(form_id).find(".multiple_upldprev").each(function () {
var isLastElement = $(this).is(".multiple_upldprev:last"); //return true only for last element in loop
if(isLastElement ){
//execute your logic for last element
}
});
Seemingly-easy problem here: I'm trying to create a streamlined way to hide/show a collection of DOM elements using some jQuery/vanilla JS. This was from a refactor where several fragmented functions were re-done as better encapsulated versions of their former selves.
What these functions are trying to do take elements from an array (by ID), use map to convert them to jQuery objects, and then hide or show them.
Using jQuery 1.11 in an Angular project, but the angular aspect doesn't seem to interfere with this case, since it won't work in a jsFiddle either.
main problem: When I run the function (usually using a click event), I don't get any sort of error from console and I don't get any sort of result in the DOM either. Any ideas? I'm sure it's a simple thing I'm missing, but I need other eyes on it.
Here's a jsFiddle with the below code loaded in, ready for fixin'. Thanks!
http://jsfiddle.net/sLgqvdku/
function showItem(item) {
return item.show();
}
function hideItem(item) {
return item.hide();
}
function showItemsWithIDs(ids) {
ids.map($).forEach(showItem);
}
function hideItemsWithIDs(ids) {
ids.map($).forEach(hideItem);
}
var itemsHiddenToFocusTribute = ['#form', '#ask', "#submitButton", "#sidebar", "#AmountCtrl", "#giftbutton"];
It appears that only the first element in the array is actually being converted into a jQuery object in your code.
Here's what's happening: vanilla-JS .map passes three arguments to the specified callback function: the current element, the index, and the array.
If the callback function takes only one argument, the second and third are ignored. However, jQuery's $ actually allows two arguments: a selector, and a context (container element). So your code is passing (as the second argument) the array index as a context, resulting in an empty jQuery object -- except for the first element in itemsHiddenToFocusTribute, which has index 0 which is interpreted as no context at all.
You can fix this with an anonymous function that only passes the selector string to $:
function hideItemsWithIDs(ids) {
ids.map(function (i) {
return $(i);
}).forEach(hideItem);
}
http://jsfiddle.net/mblase75/e23qgad5/
However, a more jQuery-friendly way would be to create a single jQuery object of all the desired elements and loop through them using .each:
function hideItem(i,item) {
return $(item).hide();
}
function hideItemsWithIDs(ids) {
$(ids.join()).each(hideItem);
}
http://jsfiddle.net/mblase75/mm2L4xn1/
This is probably more efficient, too, since you're calling $ just once instead of array.length times.
All you're wanting is to send each id through the foreach loop? Then I'd just use each like so:
$(ids).each(function(index, id) {
hideItem(id);
});
You don't need to use map($) to convert them to jQuery objects, just put the object inside the dollar sign function call, like so: $(ids).
Also make sure you pass the actual id to showItem and hideItem when you call them, like so: hideItem(id). You also need to make sure that you use a jQuery object in your hideItem and showItem functions. I changed your code to this:
function showItem(item) {
return $(item).show();
}
function hideItem(item) {
return $(item).hide();
}
function showItemsWithIDs(ids) {
$(ids).each(function(index, id) {
showItem(id);
});
}
function hideItemsWithIDs(ids) {
$(ids).each(function(index, id) {
hideItem(id);
});
}
var itemsHiddenToFocusTribute = ['#form', '#ask', "#submitButton", "#sidebar", "#AmountCtrl", "#giftbutton"];
$('#clicker').click(function(){
hideItemsWithIDs(itemsHiddenToFocusTribute);
});
And here's the updated Fiddle
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);
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