i have a div, with 3 tables in it.
var x = tempDiv.getElementsByClassName("hiscore_table");
I know this for sure because when i console log it, it prints like this:
I make a new div to append the tables on
var newDiv = document.createElement('div');
for (let i = 0; i < x.length; i++) {
newDiv.appendChild(x[i]);
}
I then append to the body, but only 2 tables show. I debugged the for loop and its only running the loop 2 times. If i print x.length i get 3. Im not too good at debugging but when i append child it seems to be deleting it from the old div, maybe thats the cause.
You’re creating a new <div> and appending elements that currently exist in the DOM to it. This means that you’re removing those elements from the DOM in order to append them to the <div> (which is not in the DOM).
Next, the thing you need to know about HTMLCollections (the return value of document.getElementsByClassName) is that they’re a live list. This means that any changes to the DOM are immediately reflected in the collection.
So, i is 0, you append the first element, your collection now only contains two elements.
Next, i is 1, you append the second element of your remaining collection, which is the third element overall. Only one element remains in the collection.
Next, i is 2, which is out of bounds for your now-one-element collection.
This is similar to removing items from an HTMLCollection.
There are a few approaches to solve this, like iterating in reverse order. But I prefer a functional approach:
Array.from(x).forEach((table) => newDiv.appendChild(table));
This converts the HTMLCollection to an array right away, so it’s no longer a live list.
Just for completeness, if you want to support older browsers without the need for pollyfills or similar (i.e. write robust, easily maintained code), you can use a while loop:
while (x[0]) {
newDiv.appendChild(x[0]);
}
Oh, another simple fix is to use querySelectorAll, which returns a static NodeList:
var x = tempDiv.querySelectorAll(".hiscore_table").
Related
When I appendChild() to dom[0] you would think that it moves dom[1] and dom[2] but instead it actually duplicates/copies the elements underneath dom[0], since they still remain elements of actual dom array.
//JS
var dom = [];
var element = new Array("div","p","h1");
for(var i=0; i < element.length; i++){
dom.push(document.createElement(element[i]))
}
dom[0].appendChild(dom[1]);
dom[0].appendChild(dom[2]);
console.log(dom); //div,p,h1
console.log(dom[0].children);//p,h1
console.log(dom.length); //3
Question: When if ever, does the element(s) get removed from memory?
Your question arises from a very false assumption:
The array dom does NOT mirror the actual DOM structure.
You are creating three elements and you are assigning a reference to those elements into your array.
You are also assigning a reference to those elements in the DOM itself.
So, each element ends up with two references that point to it -- the dom array and the DOM itself.
The references are completely independent from the elements themselves. You could create a half-dozen local variables that all point to the same elements.
The elements will be freed when:
Nothing is still referring to them
That includes dom
And the DOM
and any local variables
The garbage collector runs.
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 can I remove all the elements with a specific tag name using Javascript. For example, I did the following:
var els = document.getElementsByTagName("center");
and it returned an array of all the center elements. How may I remove all these elements?
Coming from Remove all child elements of a DOM node in JavaScript and JavaScript DOM remove element I know that I can loop through els, find the parent of each element and then remove that specific node. But is there anyother way provided by javascript. Like we can do $('center').remove() in jquery and it removes all the elements with center tag. Anything similar to that in Javascript?
With the mention that you still loop over the elements (there is no way to avoid that), you can do this:
Array.prototype.slice.call(document.getElementsByTagName('center')).forEach(
function(item) {
item.remove();
// or item.parentNode.removeChild(item); for older browsers (Edge-)
});
DEMO: http://jsbin.com/OtOyUVE/1/edit
Some notes on slice:
document.getElementsByTagName doesn't return an array, it returns a live list with a length
property. That is why it is needed to first convert it into an array (Array.prototype.slice does that for any object with the length property). By doing that you eliminate the problem of the list being live (gets updated when you change the DOM) and you also get the other array functions to work (like forEach) which have a more functional syntax for looping.
"it returned an array of all the center elements."
Well, it returned an array-like object (a live NodeList or an HTMLCollection depending on the browser).
"How may I remove all these elements?"
You have to loop through the list removing them one at a time as you mentioned later in your question. If you find yourself doing that a lot, encapsulate the looping inside a function so that you don't have to repeat it.
"we can do $('center').remove() in jquery and it removes all the elements with center tag. Anything similar to that in Javascript?"
jQuery is a collection of JavaScript functions, so it can't do anything JavaScript can't do. jQuery's .remove() method (like most other jQuery methods) will loop internally, it just saves you having to think about it. So that comes back to what I already mentioned above, encapsulate the loop/remove code in a function so that you can use it from any part of your code.
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.
I have a little problem concerning performance of jQuery.show.
This problem occurs in IE8 (probably also versions below, but IE8 is of my interest).
I have an array of jQuery objects, lets call it elements.
I want to show them, so i did:
for (var i = elements.length - 1; i >= 0; i--) {
elements[i].show();
}
The bottleneck seems to be the call to show. The array is already generated, so that takes no time. looping through an array should not be a problem either.
I thought of reducing this to one call to show by creating a new jQuery element containing all my elements. but I was not sure how to do this. I tried by jQuery.add.
var $elements = elements[0];
for (var i = elements.length - 1; i >= 1; i--) {
$elements = $elements.add(elements[i]);
}
$elements.show();
Now, this time it seems to be a problem with jQuery.add. Probably because it always creates a new object.
So, I thought of three different approaches that could solve my problem. Maybe you can help me with one of them:
Is there a jQuery method like add that does not return a new object, but instead adds it to the current jQuery element?
Is there an easy and fast way to create a jQuery element by an array of jQuery elements?
Is there a way to show all jQuery objects in an array in a faster way?
Answering just one of the question would probably help me out here. My problem is, that jquery always expects an array of DOM elements and not an array of jQuery elements in its methods.
Thanks a lot in advance!
-- EDIT about the contents of elements
I have a jstree that dynamically creates nodes (that is, li elements). These elements have an attribute data-id to identify them.
Now, I could always query for something like $('li[data-id=xxx]'), but this would be very slow. So instead, when li elements are created, i cache them into a dictionary like object (key is the data-id, value is the node). Out of this object, i generate my array elements. This happens very fast.
The array can have a size of up to 4000 nodes. Every jQuery Element in the array only contains one DOM-Element (li).
Edit
After reading your update, and the comments, this Very small one-liner is most likely going to be the answer:
elements.each($.fn.show);
Using the jQ each method to apply (well, internally: call is used) the show method to all elements.
The easiest, and fastest way to create a jQuery object from an array of jQuery objects would be this:
var objFromArr = $(jQarr);
I've just tested this in the console:
objFromArr = jQuery([jQuery('#wmd-input'),jQuery('#wmd-button-bar')]);
objFromArr.each(function()
{
console.log(this);//logs a jQ object
console.log(this[0]);//logs the DOMElement
});
But having said that: you say elements is an array of jQ objects, but if it's the return value of a selector ($('.someClass')) then really, it isn't: in that case, your loop is "broken":
elements = $('.someClass');
for (var i=0;i<elements.length;i++)
{
console.log(elements[i]);//logs a DOMElement, not a jQuery object
}
//It's the same as doing:
console.log(elements[0]);
//or even:
console.log($('.someClass').get(0));
But in the end, if you have an array of jQuery objects, and you want to invoke the show method on each and every one of them, I suspect this is the best take:
elements.each($.fn.show);//use the show method as callback