creating/appending html elements (browser memory) - javascript

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.

Related

How can I make my JS variables refer dynamically to DOM elements instead of simply saving their properties

I have a large table of <div>s on a page which get updated via JS all the time. Now I am writing a Chrome Extension to read these updates. At some point I'll have to refer to these <div> elements and their innerHTML. For some reasons, I'd rather save this whole table into one simple array, and then later update the array with the innerHTML of its elements. So I assign
savedElement = document.getElementById("ID1"); and later use console.log(savedElement.innerHtml);, However, the output is always the initiated value of the original Dom element, and never changes.
I know, when assigning a new object to a variable, the properties of the object are all copied to the variable, and future references to that variable will not output different values not matter how many times the actual DOM element has been changed.
Now, How can I save these DOM elements into my JS variables in a way that does not just keep those variables in their initiated state, but rather changes dynamically every time the DOM element is called through the variable?
console.log (dElement.innerHtml); // output Hello World
wait (3000); // this element "Obj1" is changed in the meanthile
console.log (dElement.innerHtml); // output Hello World
The problem is, as the above shows, the value of Obj1 doesn't change even if its value has been modified on the DOM itself. This output must also be synced.
Not: please let me know how to save a DOM element into a variable in a way that its innerHTML keeps updating and not just showing the initiated value.

Appending all the tables in a div onto the body

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").

How do HTMLCollections work? Can I create my own HTMLCollection?

I'm working on javascript serialization library that will allow me to serialize parts of DOM. In DOM operations, we often use HTMLCollections. Now HTMLCollection isn't just an ordinary array:
var collection = document.getElementsByTagName("p");
console.log(collection.length);
document.body.appendChild(document.createElement("p"));
console.log(collection.length);
This outputs:
Obviously not an ordinary array. Obviously, it's a matching rule on dom element that updates the list every time element child node list changes. On that, I have two questions:
Can I reliably serialize HTMLCollection not as a list of elements, but as a matching rule it represents?
Can I create HTMLCollection that is instance of HTMLCollection and follows my own matching rule, without having to re-implement the algorithm that decides when to re-update the list?

document.getElementsByTagName() will dynamically change

I use document.getElementsByTagName to get all the images in the page and store the array in a variable called imgs. Then every time I use document.createElement and document.appendChild to create a new image tag and append it to the document, the content of the variable imgs would dynamically change, which means it will include the newly-added image tag. According to my understanding, I already saved the content in the variable imgs before, so it should keep constant all the way. For example. if I save the length of imgs in another variable called imgsLength, it doesn't change even if I add another img tag dynamically to the document. So I'm a little bit confused about this inconsistency between imgs and imgsLength.
You're misunderstanding objects.
getElementsByTagName() returns a NodeList object, containing a live view of the matching elements.
This object is mutable; it will change in response to DOM mutations.
var img = getElementsByTagName() creates a variable that references this same object. It doesn't copy anything.
imgs.length returns an immutable number representing the current length of the NodeList.
var imgLength = imgs.length creates a variable that references this immutable number.
When the NodeList changes, imgs.length will refer to a different number; that does not affect the variable that refers to its previous value.

Call show on array of jQuery objects

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

Categories

Resources