Hide Collection of jQuery elements in Array Using Hide()/Show() - javascript

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

Related

How can I make an alias for element.querySelector?

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)

jQuery find a class and create callback for its last 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
}
});

How to refresh the jQuery .each() function?

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();
}
}
....
});

TypeError: <Array>.each is not a function

I have three references to three drop downs on my page, and as each one is changed, I want to run a JavaScript function called validateForm();
My code is below:
jQuery(document).ready(function() {
var drpSupplier = document.getElementById('supplier');
var drpChargeRate = document.getElementById('formElementChargeRate');
var drpIDSEmail = document.getElementById('formElementEmailIDS');
var formLevel2DDs = new Array();
formLevel2DDs.push(drpSupplier);
formLevel2DDs.push(drpChargeRate);
formLevel2DDs.push(drpIDSEmail);
formLevel2DDs.each(function() {
$(this).change(function() {
validateForm()
});
});
});
But this code is giving me the error:
TypeError: formLevel2DDs.each is not a function
I am using jQuery version 1.8.3 (it is a legacy system).
There is no each function on arrays.
As Anton points out in the comments, you don't need each at all for what you're doing; see below the fold.
But if you want each, you have three choices:
Wrap your array in a jQuery instance and use jQuery's each: $(formLevel2DDs).each(function(index, entry) { ... });
Use jQuery's $.each: $.each(formLevel2DDs, function(index, entry) { ... });
Note that this is not the same function as above.
Use forEach (MDN | Spec): formLevel2DDs.forEach(function(entry, index, array) { ... });
Note that forEach is new as of ECMAScript5. All modern browsers have it, but you'll need a shim/polyfill for older ones (like IE8). Also note that the order of the arguments to the callback is different than either of the options above.
But to Anton's point, you can do that much more simply:
There's no reason to use getElementById directly in this case, it's not in a tight loop or anything, so:
jQuery(document).ready(function() {
$("#supplier, #formElementChargeRate, #formElementEmailIDS").change(validateForm);
});
Note that I've also removed the wrapper function from around validateForm. You may need to add it back if validateForm has a return value, and you don't want that return value to be used by jQuery (specifically: if it returned false, jQuery would stop propagation and prevent the default action of the change event).
If you really wanted to have direct access to the DOM elements using those variables:
jQuery(document).ready(function() {
var drpSupplier, drpChargeRate, drpIDSEmail;
var formLevel2DDs = [
drpSupplier = document.getElementById('supplier'),
drpChargeRate = document.getElementById('formElementChargeRate'),
drpIDSEmail = document.getElementById('formElementEmailIDS')
];
$(formLevel2DDs).change(validateForm);
});
If you want to use .each() you must wrap the array with jQuery like this
$(formLevel2DDs).each(function() {
It's not necessary to use a loop in this case, just use .change() on the array wrapped with jQuery
$(formLevel2DDs).change(function(){
validateForm()
});
The native iterator function is forEach, not each.
The jQuery .each function takes just one argument, that being the callback. The callback function is passed two parameters: the index into the list, and the value. The callback is invoked such that the list value is also the this value.
With forEach, the parameters are passed in reverse order: the value is first, followed by the index. The native function also passes the entire array as the third parameter. The native function does not bind this when the callback is invoked unless a second parameter is passed to .forEach after the callback function. If there is such a parameter, it is used as the value of this in the callback.
The native iterator skips elements of the array that have not been set. The jQuery .each() does not skip such elements, instead always iterating from index 0 to length - 1.
try
$(formLevel2DDs).each(function() {
$(this).change(function() {
validateForm()
});
});
added one line
jQuery(document).ready(function() {
var drpSupplier = document.getElementById('supplier');
var drpChargeRate = document.getElementById('formElementChargeRate');
var drpIDSEmail = document.getElementById('formElementEmailIDS');
var formLevel2DDs = new Array();
formLevel2DDs.push(drpSupplier);
formLevel2DDs.push(drpChargeRate);
formLevel2DDs.push(drpIDSEmail);
formLevel2DDs = jQuery(formLevel2DDs);//this line
formLevel2DDs.each(function() {
$(this).change(function() {
validateForm()
});
});
});
use a for ... in loop
for(key in formLevel2DDs) { ... }

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);

Categories

Resources