Why does .splice always delete the last element? - javascript

In the javascript, there are two arrays:tags[] and tags_java[]. I use .splice to delete certain items, which of the same index in the two arrays. The tags[] works fine, but tags_java doesn't, it seems always delete the last item.
Here is the code and the jsfiddle link.
var tag = $(this).text();
var index = $.inArray(tag, tags);
tags.splice(index,1);
tags_java.splice(index,1);

Nah, both don't work, because you're not actually finding the correct index of your tag.
Why not? Because $(this).text() includes the delete mark you added, × - e.g. "Morning×". Since that's not in your tags array, index will be -1. tags.splice(-1, 1); will remove 1 item from the end of the array.
In general, it's never a good idea to use presentation text (i.e. the text of your tag element) as data (e.g. using that text as a lookup value in an array). It's very likely that it'll be broken when something changes in the presentation - like here. So a suggestion would be to store the data (what you need to look up the tags) as data - e.g. using the jQuery-provided data() API - even if it seems redundant.
Here's a quick example - just adding/replacing two lines, which I've marked with comments starting with "JT": JSFiddle
Now, instead of looking up by $(this).text(), we're looking up by the data value "tagValue" stored with $(this).data() - that way, the lookup value is still bound to the element, but we're not relying on presentation text.

If the tag is not in the tags array, $.inArray will return -1, which would then cause the last item to be deleted.
You have to make sure that the item is actually in the array.

Related

Deleting an element from Array (To-Do list app)

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!

Target search results number in variable

I'm trying to target the number of search results on our website for each search term so that I can see how many results each one pulls in.
I'm working off of this article, but I can't get the javascript function correct to pull out the number (which could be as high as 2000) and put it into a variable.
<div class="search-results-text"><strong>732 results</strong> found for ‘<strong>search term</strong>’</div>
Hoping someone can help me out with the javascript function that would grab that number before "results". Thanks!
You would probably get away with a custom Javascript variable like this:
function() {
return document.querySelector('.search-results-text strong').innerText.split(" ")[0];
}
The querySelector with the CSS selector gets the Element, innerText is the text without the markup, the split splits the string up by whitespace, which gives you an array, and the first element of that array is your number (array are index starting with zero, so [0] refers to the first element).
This is not particularly elegant (for one you probably want to add some sort of error handling), and you could actually replace document.querySelector('.search-results-text strong').innerText with a DOM type variable in GTM (which by default returns the text of the element).
I don't think you can get the number with CSS selectors alone.

.each(function{} .split and .attr

I recently came across these codes while learning how to program an animated gallery and have no idea what they mean:
var items = $('#gallery li'),
itemsByTags = {};
items.each(function(i){
var elem = $(this),
tags = elem.data('tags').split(',');
//Add data attribute for quicksand
elem.attr('data-id',i);
I know that the var items = $('#gallery li'),itemsByTags = {};means that I am grabbing the list ID through jQuery and then setting a variable named itemsByTagsto an empty string.
After which items.each is a loop that is design to run through all the lists. But i have no idea what is function(i)? Why is there an i inside the function? What is this ithing?
Also, what does $(this) mean? Does it refer to the items?
Lastly, what does the .attr() mean with the i inside?
Just as a warning, questions like this one aren't very highly regarded on Stack Overflow because they can be answered by visiting a single page about the function you're using, which in this case would be the documentation for the jQuery .each() function. Questions like this one are often downvoted and never get an answer, so before posting a question, you should always check the documentation for the code or libraries that you are using to see if your question may already be answered there.
With that out of the way, here are a couple of points that should help answer your questions:
itemsByTags = {}; sets the variable to an empty object, not a string. This variable is not used in the code you provided, though, so it's not very relevant to the question at hand.
items.each(function(i){ is, as you noted, a function designed to loop through an array of items. Each functions normally output two different values, a key and a value. In this case, however, only one variable is supplied in the function call, and so the function will only receive the first value, which is key. The key is the 0-based index of the number of the item you are on. This number will increase by 1 each time you move to the next item in an array. A different way of explaining this would be to say that array[key] would return the value in array at item number key.
$(this) is jQuery's way of referring to the object currently being retrieved from the array. Because var items = $('#gallery li') retrieves a list of all of the objects in the page that match the selector #gallery li, $(this) refers to the actual object in the page whose array element is being retrieved. $(this) can be modified and acted on in the same way as if you targeted it using a normal selector like $('#gallery li').eq(i).
You may have determined by now exactly what the last line is doing. $(this).attr('data-id',i); finds the current array item's element in the page and sets its data-id attribute to its 0-based index relative to its siblings. If you iterate through the array in this way, the 6th (just for example) $('#gallery li') element's data-id attribute would be changed to 5.
Hope this is helpful!

How to use app.selection[0] for scripts in Adobe InDesign

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

How to remove a node from a Javascript array

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.

Categories

Resources