today i've got this struggle to solve, so i have a array of html elements (e.g 3 inputs)
and i want to delete every single one from the dom. so i must iterate loop over them but i also should not increment the value because it will skip every other elements and that means i must only delete first element of array until it exists, so for this task i use this code
while(inputs[0]) {
inputs[0].parentNode.removeChild(inputs[0]);
}
and this works perfectly and removes all elements.
but what about for...of loop? it also gets every value of array and what if i just delete first input every time it's getting a element from array ? like this:
for(input of inputs){
inputs[0].parentNode.removeChild(inputs[0]);
}
i also tried this and in the 3 inputs it left third one (did not delete it)
so i want to know why? can someone show me how it missed third one? (graphical explanation will be the best)
Thank You
At the first iteration, the iterator is at position zero, there are three elements. You delete one.
v
0 1 2
At the second iteration, the iterator is at position one, there are two elements. You delete one.
v
0 1
At the third iteration, the iteration stops, as there is only one element, and the index is at three, so it is outside of the array. No delete operation is done.
v
0
The main problem/advantage here is that nodes is a live collection. If it would not be live (e.g. if you use querySelectorAll), then the second version would work:
// v declare variables!
for(const input of document.querySelectorAll("input"))
input.remove(); // < remove is way easier
Related
I'm creating a To-Do list app, pretty basic and I copied the idea from Youtube (still learning JS). So, each time you add a new To-Do it's stored (just the text you typed and whether it's done or not) in an array and the HTML element is added, everything's good until here.
The problem begins when I try to delete that element from the array. Each item (To-do) has an ID which is basically the index where it's stored in the array, so I coded array.splice(item.id, 1) and It works great if you delete the items patiently one by one, but if you click the delete button faster the items deleted in the array doesn't match, it's like the index passed messes up. I was wondering if I could make like a wait until the current delete() function ends or something like that. Btw the list container has an eventListener and if the delete button of any item was clicked it runs the delete() function passing the item by e.target.parentElement (which is the item container).
I want the array for a localeStorage. Thanks!
First time posting and English isn't my first language, sorry for any mistake.
Great question. So .splice() is indeed the Array method you want to call, but it doesn't quite use the syntax you're expecting.
First of all, I'm going to point you to the MDN docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
MDN is managed by Mozilla, and is an authoritative source on most things javascript-in-the-browser. If you're just getting started with javascript, that resource will be invaluable. The MDN documentation is some of the best written technical documentation out there, and is largely written so that people with minimal experience in the language can understand them.
That being said, let's go over what Array.splice is actually doing.
So you have an array of todos:
const todos = [] // Array of to-dos
You want to delete one of the the todos, and you have access to the id. I'm going to assume your Todos have a shape similar to this:
const todo = {
id: 1 // number
name: 'my todo' // string
description: 'my todo description' // string
}
In order to delete an element from this array, given the id of the item you're deleting, you would actually need to do the following:
1) Find the location in the array of the todo that has the id you are looking for.
2) Splice that element out of the array using the index you just found.
Let's see how to do that:
function removeItemFromTodos(itemId, todos) {
// find the index of the todo with the id you are looking for
const indexOfTodoToDelete = todos.findIndex((todoInArray) => todoInArray.id === itemId));
// remove that todo:
todos.splice(indexOfTodoToDelete, 1) // delete the todo
}
Okay, so let's unpack that:
First, the findIndex method loops over the array. It will start at index 0, and work up until it reaches the end of the array. If the item it is currently looking at has an id that matches the id we are looking for, then the function will immediately return the index of that todo's location in the array, and stop searching in the array.
Once you have the index, you can delete the item. The splice function takes in the location that you want to start cutting elements as the first argument, and the number of elements you want to cut as the second argument. The splice method returns the elements that were deleted. So it is actually mutating the array in place, and not making a copy of it in memory to perform its operation.
Let me know if this solution doesn't work for you or if it isn't clear!
Okay so I'm looping through all the elements on the page (3) that have the class 'possible'. Then I'm creating a variable called 'other' that randomly gets a value from the array otherAnswers. Then I'm taking those values and putting them into the respected elements as text. Only thing is, most of the time the loop selects the same variable from the otherAnswers array (sometimes once, sometimes twice, others three, and rarely none of the time). How do I make sure that once the .each loops through one of the values in the array, it doesn't get it again?
$('.possible').each(function(i, obj) {
var other = otherAnswers[Math.floor(Math.random()*otherAnswers.length)];
$(this).text(other);
//otherAnswers.splice(this);
});
I've tried the commented piece of code but that just removes the values so they don't show up on my page. I have a feeling that it has something to do with the variable 'i' in the function but i'm not sure.
You can do this by taking the following steps:
Before looping, take a copy of the otherAnswers array, to avoid that you destroy it with what follows. For instance, with spread syntax:
var remainingAnswers = [...otherAnswers];
Then -- still before the loop -- shuffle the copied array randomly. Take the shuffle code from here.
shuffle(remainingAnswers);
Finally, inside your loop, access the values from that shuffled array, using the index i you already have:
var other = remainingAnswers[i];
I would like to run code by testing only the current selection (not the whole document) and I'm having difficulty understanding exactly how the array "app.selection" and its methods work. To start, I use a "for" loop to cycle through each item selected by using:
for(loop = 0; loop < app.selection.length; loop++){
var sel = loop;
}
This works okay, but when I want to get into determining what each item IS, it gets a little weird. For example,
for(txt = 0; txt < app.selection[sel].textFrames.length; txt++){
// do something to each text frame in the selection here.
}
does not work as expected, but
for(img = 0; img < app.selection[sel].allGraphics.length; img++){
// do something to each graphic in the selection here
}
seems to work fine, regardless if the selection includes more than just graphics alone, or whether it is inside or outside a group.
At times, it seems like app.selection[0] is the only way to access the item by itself. In other words, if a text frame is selected, app.selection[0] might be the same as app.document.textFrames[0], in which case it would be redundant (and incorrect) to say
app.document.textFrames[0].textFrames[0]
And yet the same concept on different page items works like a charm. It is quite puzzling to follow. Furthermore, it seems impossible to determine what kind of object the item is. I want to say something along the lines of:
if (app.selection[0] == [object TextFrame])
but that does not seem to work for me. Is there a way to clearly test if the current item is a group, a graphic or a text frame and do different things depending on the result?
app.selection returns an array of Objects, so each item in the array can be of a different type, and the properties and methods available to it will differ. When using the Extendscript Javascript Console you can see what a particular item in the array is on the fly by just typing
app.selection[0]
(or whatever number). The result will be something like [object TextFrame].
While looping through the selection array, you could use app.selection[0].constructor.name to determine the type of each. Or, if you're only interested in certain types,
if (app.selection[i] instanceof TextFrame){}
At that point you'll know more about which properties you can access, depending on the type.
To answer the second part of the question, there isn't an allTextFrames property, but there is an allPageItems property. This returns an array of pageItems (textFrames, groups, etc.), and you can work with it similarly to app.selection. So, if I have three text frames grouped on the first page of my document (and nothing else), I can see that the following are all true:
app.activeDocument.pages[0].textFrames.length == 0;
app.activeDocument.pages[0].allPageItems.length == 4;
app.activeDocument.pages[0].allPageItems[0] instanceof Group;
app.activeDocument.pages[0].allPageItems[1].constructor.name == "TextFrame";
So you could probably cycle through that array if it's more useful to you than the textFrames collection. Just keep in mind that you don't have access to the special collection properties of TextFrames (like everyItem()).
App.selection is indeed an array which every item can be accessed by its index:
var sel = app.selection //May be null on no open documents ! An empty array on no selection with an open document. One to n length array in case of selection.
then given that you selected one or several items, you can reach those objects by its index
sel[0] //This returns the first item of the array. Javascript starts counting at zero.
Once that said if you access, say sel[4] and selection count less than 5 items or column 5 is empty, then you get an undefined value. So you need to carefully check for selection item validity before using it and never presume it will return something.
HTH,
Loic
http://www.ozalto.com
So...I have this array:
val['an_element'][0]['another_element'][2]['text']
I want to get rid of the entire "2" node.
Now...I THOUGHT the way to do this would be:
delete val['an_element'][0]['another_element'][2];
BUT...it doesn't actually drop the element, but simply empties it out.
I also tried:
val['an_element'][0]['another_element'][2] = null;
...but that just resulted in my console log nearly bleeding it was so red with errors.
Basically, i want that [2] node to NO LONGER EXIST. Basically, I want it to NOT BE FOUND AT ALL.
What do I do??? And I know the ".splice" method will NOT actually modify the original array, so please don't suggest it. :)
The splice method will, in fact, modify the array. Just try:
val['an_element'][0]['another_element'].splice(2, 1);
From the docs:
Changes the content of an array, adding new elements while removing old elements.
...
If you specify a different number of elements to insert than the number you're removing, the array will have a different length at the end of the call.
Just trying to remove a table but loop is not removing all rows.
I have used alert to be sure it's the right element by id.
It does remove some rows but not all, just chunks.
console reports: DOM Exception 1,DOM Exception 8
function removeThis(unsetElement)
{ unsetElement.parentNode.removeChild(unsetElement); }
While you remove rows, their indices change; you need to do the loop from the top, i.e.
i=rowCounter-1;
while(i>=0){unsetTable.deleteRow(i);i--;}
Yet the better idea is just to purge the whole table; rows will be garbage-collected.
Another solution could be to use the special -1 index, which deletes the last row.
var i = thisTrAry.length;
while(i--) {
unsetTable.deleteRow(-1);
}
But if you remove the whole table anyway (in your last line) then there is no need to remove the rows first.