For loop in Javascript/Jquery - javascript

I am making use of Gridster widgets on webpage.Each widget is having a button on it which changes the color to red of widget after that is clicked(It basically should imply that this widget is selected).Once that button is pressed the parent element(of the button clicked that is a particular widget) gets added to an array as well
The meta information about variables in for loop is as follows
parentElement:An array which should have widgets which are highlighted/selected
currentElement: The widget which is currently selected or for which the button is pressed
I have written a for loop and the expected logic of that loop should be as follows
If the array parentElement is empty (That is no element in the array)
then the element which is clicked currentElement should get added in the array
If the array parentElement is not empty then the element which is clicked currentElement should be checked for its presence in the entire parentElement array
If currentElement found then currentElement should not be added in parentElement else it should be added
My main aim is to remove the currentElement from parentElement array if it is already present in the parentElement array
My JS for same is as follows
var parentElement = [];
$(document).on("click", ".change-widget-color", function() {
currentElement = $(this).closest('li');
$(this).closest('li').toggleClass("red") //Highlighting the element which is selected
if (parentElement.length === 0) {
parentElement.push(currentElement);
}
else {
for (var i = 0; i < parentElement.length; i++) {
if (JSON.stringify(parentElement[i]) === JSON.stringify(currentElement)) {
console.log("Element already present");
}
else {
parentElement.push(currentElement);
}
}
}
});
Overall Expected Output
My main intention is that before adding currentElement in the array parentElement it should be checked.If it is present then I should remove that element
I am not able to figure out how I will remove the current element if it is already present.
The Problem in my for loop
The above for loop behaves in a very wierd way.I dont know whats wrong with the logic but when I do console.log it executes both if and else condition
Any help will be really appreciated
Fiddle

You are modifying the array as you loop through it.
So, in one loop, you are adding the currentElement to the array. This increments parentElement.length. Thus, you do another iteration of the loop where the index points to the currentElement that you just inserted.
EDIT: Also you are calling JSON.stringify() on a jquery element, which is not what you want. You should just do a direct compare on the element. Notice the [0] part which takes the first DOM element out of jquery.
You should check the array to see if it's unique, and then insert it like so:
if (parentElement.every(element => element[0] !== currentElement[0])) {
parentElement.push(currentElement);
}

Base on AnilRedshift's answer, you can remove the same element by the following code:
$(document).on("click", ".select-element", function() {
currentElement = $(this).closest('li');
$(this).closest('li').toggleClass("red") //Highlighting the element which is selected
if (parentElement.length === 0) {
parentElement.push(currentElement);
console.log("Parent Element");
console.log(parentElement);
} else {
// check if the array already has the same element
if (parentElement.every(element => element[0] !== currentElement[0])) {
parentElement.push(currentElement);
}
else{
console.log('Element already present')
// find the index of the existed element
var index = parentElement.findIndex(x=>x[0] === currentElement[0])
// remove it
parentElement.splice(index,1)
}
console.log("Final Parent Element");
console.log(parentElement);
}
});

Related

JavaScript: How to skip over current item in array during a for loop? (continue?)

EDIT: I don't want to skip index 1. I want to skip the current (clicked on) element. Also, see below for more of the code as requested. You'll see that I have a class CatListItem and five instances of that class in an array allCatListItems.
Here's some context for the question: I have a list of cats. When I click on a cat's name (list item), I want that cat's picture and other info to be appended to the page (got that down). When a cat is clicked, I also want any other cat that is being displayed to be hidden (that way there is only one cat on the screen at a time).
I'm trying to accomplish this with a for loop, but obviously if it iterates over every item in the array, then when I click an item, the cat being clicked will be hidden as well.
I want to skip the current item in the array and only run the code on the other items. Using continue, I know I can skip a specific item (item 1 in the below example). This will run my code on every item in the array except that at index one. But how can I make that continue dynamic? Meaning... how can I hide all of the cats, except the one being currently clicked?
Here's the loop that skips index 1:
CatListItem.prototype.hideCats = function() {
allCatListItems.forEach(function(cat) {
cat.a.addEventListener('click', function() {
for (var i = 0; i < allCatListItems.length; i++) {
if (i === 1) {
continue;
}
allCatListItems[i].img.className = 'hide';
};
});
});
}
var allCatListItems = [
catListItem1 = new CatListItem('El', 'images/el.jpg', 'el'),
catListItem2 = new CatListItem('Widdle Baby', 'images/widdle-baby.jpg', 'widdle-baby'),
catListItem3 = new CatListItem('Mama', 'images/mama.jpg', 'mama'),
catListItem4 = new CatListItem('Legion', 'images/legion.jpg', 'legion'),
catListItem5 = new CatListItem('Boy', 'images/boy.jpg', 'boy'),
];
EDIT: Here's a fiddle.JSFIDDLE Click the names to see the functionality without the hideCats function. Then uncomment where it says to to see my issue.
I'm starting to think maybe a for loop isn't the best option?
In that case compare the event.target(its the element clicked)
EDIT: allCatListItems[i] needs it's .a property attached to it in the if statement (this is what contains the anchor element). This is because the event listener is grabbing an anchor tag, so e.target will be returning an anchor tag as well. The if statement will never return as true if you aren't comparing the same type of element.
cat.a.addEventListener('click', function(e) {
for (var i = 0; i < allCatListItems.length; i++) {
if (allCatListItems[i].a === e.target) {
continue;
}
allCatListItems[i].img.className += ' hide';
}
});
Here is a jsfiddle, it doesn't use the same element names, but it should be doing what you want. https://jsfiddle.net/5qb4rwzc/
$('li').on('click', function() {
var index = $(this).index();
var items = document.getElementsByTagName('li');
for(var i = 0; i < items.length; i++) {
if(i === index) continue;
items[i].style = "display:none;";
}
});
Its really depend on how you call the function "hideCat". Realizing that each time that function is called, more eventListeners are add to every cat item. Each time a cat is clicked, more than one event fired. Perhaps you should re-structure how to attach eventListeners to each cat item.

How to make this div clickable?

I wanto to make each "card" of this wordpress plugin clickable (http://www.davidbo.dreamhosters.com/?page_id=11)
So I added a Pure JS element with the following code:
document.getElementsByClassName('fc_card-container').onclick = function() {alert('It works!');}
and it is not working so I wonder how wrong this is.
Thanks !
If you want to apply a click event handler for all the cards:
// Get all the elements with a .fc_card-container class and store them on a variable
// .getElementsByClassName returns an array-like object with all the selected elements
var cards = document.getElementsByClassName('fc_card-container');
// Use [].slice.apply(cards) to convert the cards NodeList into an array
// Iterate over the cards array with a .forEach
[].slice.apply(cards).forEach(function(card, index){
// Each array element corresponds to a card element
// Use the .addEventListener( EVENT, callback ) to attach an event handler for each card element
card.addEventListener("click", function(e){
alert();
console.log(cards[index]); // Index gives us the exact array position (index) of the clicked card.
console.log(e.target); // e.target gives us access to the clicked element
});
});
document.getElementsByClassName returns an array of matched elements. In your case, an array of elements with the class name of fc_card-container. Your next step would be to iterate over the elements and assign an event listener to each or select a specific one using an index (starting with 0).
Assign a click to all
var cards = document.getElementsByClassName('fc_card-container');
for(var i = 0; i < cards.length; i++){ //iterate through each card
cards[i].onclick = function() {alert('It works!');};
};
Assign a click to a single card (eg: 3rd card)
var cards = document.getElementsByClassName('fc_card-container');
cards[2].onclick = function() {alert('It works!');}; //0,1,2

Detect child node number

I'm trying to add a event listener for clicking and I want to know the position of the node that was clicked
function evl(etna) {
document.addEventListener("click", function (el) {
alert("You clicked on " + 'the name of element that was clicked or his array code');
}, false);
};
where etna is:
document.getElementsByTagName("*");
function evl(etna){
document.addEventListener("click",function (el) {
var clickedElement = el.target || el.srcElement;
alert("Link detected a click. Cancellable: "+clickedElement.name);
for(var i = 0; i < etna.length; i++) {
if(etna[i] === clickedElement) {
//i is a position of an element in etna
break;
}
}
},false);
};
You can use this which will point to a clicked element. As to Phil H IE 8 does not work that way. But anyway, there should be used .target or .srcElement. And maybe it will be better to get its id. Name attribute is not valid for divs, spans, etc.
But also you are attaching an event to a document. And this will point to a document.
Instead of that you should use el.target || el.srcElement where .target/.srcElement is a pointer to a node where click actually happened.
Also, I do not think you can get index of an element in array (actually, node list) returned by document.getElementsByTagName("*") (well, you can get that list and iterate through it in loop and check each element if it is eaqual to this). Plus, I have no idea why it could be needed.
Add a loop and set the event listener differently for each item in the etna array:
function evl(etna){
for(var i=0; i < etna.length; ++i) {
(function() {
var thisNode = etna[i];
var localval = i;
etna[i].addEventListener("click",function (el) {
alert("Link detected a click. Cancellable: "+ thisNode.id + " which is id " + localval );
},false);
})();
}
}
Working jsfiddle: http://jsfiddle.net/5xDjE/
The function that is immediately called is merely to force the scoping of thisNode and localval, otherwise all the elements get references to the same variable (javascript scoping is interesting).
I would advise against using the index (scoped via localval) because it requires retaining the original array of nodes. Since nodes change over time and javascript does reference counting for you, you want to avoid these kinds of long arrays of nodes.
Note that this doesn't always have element that was clicked, in IE8 and below this points to the global window object.

Javascript for loop not working properly?

I'm having an issue with a function I've written to "clean" up, see the code below and I'll explain how it works underneath.
clean: function (e) {
var
els = null,
i = 0;
if (e === undefined) {
e = this.cont;
}
els = e.getElementsByTagName('*');
for (i=0;i<els.length;i++) {
if (els[i].className.search('keep') === -1) {
e.removeChild(els[i]);
}
}
return this;
},
The argument e is a dom element, if it isn't supplied this.cont is also a dom element stored earlier in the whole function and e is defaulted to it.
The function loops through all of the child elements and checks it doesn't have the class keep (fairly obvious what this does) and removes any that don't match.
It all seemed to be working but I have an element which has 2 images and 2 inputs none with the class 'keep' but the variable i only gets to 2 and the loop stops (it should reach 4 and remove all four elements)
any help would be greatly appreciated.
/* UPDATE */
Thanks to #pimvb and and #Brett Walker the final code which works great is below.
clean: function (e) {
var
els = null,
i = 0;
if (e === undefined) {
e = this.cont;
}
els = e.getElementsByTagName('*');
i = els.length;
while (i--) {
if (els[i].className.search('keep') === -1) {
els[i].parentNode.removeChild(els[i]);
}
}
return this;
},
The .getElementsByTagName function returns a NodeList which is basically an array but is 'live', which means it's updated automatically if you e.g. remove a child. So when iterating, els.length is changing, resulting in being 2 when you remove 2 children (there are 4 - 2 = 2 left). When having removed 2 children, i == 2 so the loop will end prematurely to what you expect.
To circumvent this and make it a 'static' array, you can convert it into an array like this, which does not update itself:
els = [].slice.call(e.getElementsByTagName('*')); // [].slice.call is a trick to
// convert something like a NodeList
// into a static, real array
As Brett Walker pointed out, you can also iterate backwards, like this:
http://jsfiddle.net/pimvdb/cYKxU/1/
var elements = document.getElementsByTagName("a"),
i = elements.length;
while(i--) { // this will stop as soon as i == 0 because 0 is treated as false
var elem = elements[i]; // current element
if(elem.className == "test") // remove if it should be removed
elem.parentNode.removeChild(elem);
}
This will start at the last element. The .length still gets updated (i.e. becomes less), but this does not matter as you only used it at the beginning, and not during iterating. As a result, you don't suffer from this 'quirk'.

check dynamic array against static array

I need some javascript/jquery for this. I have two arrays, one static array that is hard coded and another dynamic array that is user driven. For example the values in each array represent a div. Each div is represented in the static array. I want to be able to show only the divs that are present in the dynamic array. So if the dynamic array changes, it shows the divs present in the dynamic array and hides the divs not present. I also want to be able to run a function connected to each div, ie box1 has a function that needs called if it is displayed.
var static_list = new Array("box1","box2","box3");
var dynamic_list = new Array("box1","box3");
I'm assuming that dynamic_list is a subset of static_list.
$.each(static_list, function(index, value) {
$('#' + value).hide();
});
$.each(dynamic_list, function(index, value) {
$('#' + value).show();
});
$.each() iterates through a collection calling the callback function for each element. The first $.each() hides all elements with IDs found in the static_list. The second $.each() shows all elements with IDs found in the dynamic_list. Running both in that order hides all div elements in static_list except for those found in the dynamic_list.
You could use a simple comparator function:
// Return true if value is in array
function isIn(value, array) {
var i = array.length;
while (i--) {
if (array[i] == value {
return true;
}
}
return false;
}
then you can see if a value is in an array using:
if (isIn(value, array)) {
// do something with/to value
}
You can achieve your result by using $.each(), $.inArray(), and .toggle()
$.each(static_list, function(index, value){
var showHide = $.inArray(value, dynamic_list) >= 0;
$("#" + this).toggle(showHide);
});
Code example on jsfiddle.

Categories

Resources