All:
I am new to DOM, I got one question about DOM reference, for example(suppose I use D3.js or jQuery):
var domelement = d3.select("div#chart");
d3.select("div#chart").remove();
console.log(domelement);
When I print domelement, it still show an Object in the console even though it has been deleted from the DOM structure.
So I am wondering, why this variable still has access to the DOM object?
How can I decide if a reference is invalid?
Thanks
So I am wondering, why this variable still has access to the DOM object?
You retrieved a reference to an object in memory and your variable will retain it for as long as you have it in scope.
You can mutate an object having a reference to it but you cannot destruct it (not in JS).
How can I decide if a reference is invalid?
There is no such a thing as "invalid" reference. If you want to check if the element is still mounted in the DOM - you can just try to search for it. If it's there - you'll find it, and you will not otherwise.
.remove() returns the value (like a return function in javascript). When you use console.log this value is printed but it no longer exists in the DOM. HTML elements can exist as data nodes in javascript (document.createElement).
In this state, they exists as data, but haven't been added anywhere where they'd be visible. .remove() cuts the element out of the body and returns it in its data form, then console.log prints it.
Related
A search didn't quite get me the answer I was looking for.
I am curious if setting a variable to a queryselector is a reference, pointer or value.
Example 1:
// lets assume there is only one ".class"
var element = document.queryselector(".class");
element.classList.remove("effect1");
element.classList.add("effect2");
Example 2:
document.queryselector(".class").classList.remove("effect1");
document.queryselector(".class").classList.add("effect2");
Are these exactly the same thing? Or by setting the variable to the selector, are you saving overhead?
My thought is that if the variable is a value, then the query selector won't search the DOM when the variable is used.
Hope that makes sense.
In Javascript objects are always passed by reference so both examples have the same effect since document.querySelector returns a DOM Element (which is an object).
As a short FYI:
Primitive types are always passed by value
Arrays are also passed by reference
Your first example is much more efficient because you only have to search the DOM once and there is absolutely no difference in their effect.
Javascript is always pass by value, but when a variable refers to an object (including arrays), the "value" is a reference to the object.
var element = document.queryselector(".class"); // element is an object
So both examples are exactly the same. Under the hood is just accessing classList property of element object.
Yes, first code snippet is efficient because we are saving a cycle to fetch DOM nodes again. If we are just accessing dom element nodes a few times let say 10-100 (normally in a mid to large project) then the performance won't be impacted much. I mean you can just ignore that. That won't be a drastic effect.
But yes if we are accessing dom nodes a million times then that effect is noticeable.
Let's check for 1 million times, just to access DOM element nodes.
first = performance.now();
new Array(1000000).fill(1).forEach(() => {
div = document.querySelector('div');
})
second = performance.now();
console.log(`${second - first} milliseconds`); //337.2850000159815 milliseconds
// Result can alter from machine to machine
Hope it is clear now.
I can understand the purpose of clone() method when appending a copy element like this:
$aObject = $('.className').clone();
$aObject.removeAttr('id');
$('#add-line').click(function() {
$('#container').append( $aObject.clone());
});
But what I don't understand is, if I get rid of the clone method, just using
$('#container').append( $aObject);
I should still be able to add multiple same object to the container, but it seems like I can only add the aObject once? can't we add same object many times on purpose just like an array of same objects?
When you assign an object to a variable in JavaScript, you aren’t actually assigning the object value stored in memory – rather a reference that points to the object’s location in memory.
So, when you declare $aObject, you have now stored a reference to a particular object. When you append it, it behaves as you would expect, appending the object that you are referencing. When you try to do the same thing again, it is referring to the same object that now already exists in the DOM and simply takes that object and re-appends it (what Scott Marcus meant when he said it acts as move).
If you clone it first, then you are referencing an entirely different object, which can be appended in addition to any objects you've already appended.
Hopefully the title of the question is clear, but:
What happens when the following happens:
<div id="parent">
<img src="..." id="myImage"/>
</div>
<script>
var i=document.getElementById('myImage');
document.getElementById('parent').innerHTML='';
// variable i should not be 'undefined' right? chrome debugger says nope!
</script>
I have tested this in chromes debugger, and it appears that the reference still "lives" on even after the DOM object no longer exists... Am I correct? Doing a console.log(i) (for the above example), still returns the object and all its properties... Shouldnt the garbage collector kick in and 'undefined' this reference since it no longer exists in the DOM?
Does anyone know how something like this is handled across other browsers (more specifically, IE6...) besides chrome?
The reason I am asking is because we globally store some references (depending on user actions), to be used if the user performs another action later on... And we basically need a way to test if the DOM object still exists or not..
I have created a small jsfiddle to make everything clear: http://jsfiddle.net/9HCKt/
Thanks.
UPDATE: I see now the question wasn't quite clear... How do I test to see if this object still exists in the DOM in a cross browser compatible way?
Your fiddle makes things worse :) Why you need to know type of removed element? It will be object before it removed, and after too. If question what happens: Your element is Element. You can check it with i instanceof Element. It's just disconnected from DOM. You actually can connect it again, in other or the same place of DOM and it will be legal. If question how to check element is connected to DOM: just check if it's parent defined.
if (i.parentElement) {
// i connected to DOM
} else {
// i disconnected
};
Think about: when you add new element with document.createElement, your new element not attached yet. But it obviously not undefined.
if you can use jQuery,
$(document).has($(i)).length
should be 1 if i is still in the DOM, 0 if not.
Doing a console.log(i) (for the above example), still returns the
object and all its properties... Shouldnt the garbage collector kick
in and 'undefined' this reference since it no longer exists in the
DOM?
In fact, with your i variable, you are maintaining a reference to the object, preventing it from being garbage collected.
I'm using .data() to store an object-instance to a DOM element. This works within the frame I'm in but due to regulation within the application I'm working on all jQuery dialogs are filled using an iframe. For some reason I can't seem to access the .data() stored object from outside the frame. Any way how I can access it?
My code to store the object (which is an CodeMirror-instance).
$('#MyTextArea').data('CodeMirrorEditor', editor); (where editor is the instance)
When I want to access it I'm using:
var context = document.getElementById('DialogFrame').contentWindow.document);
console.log($('#MyTextArea', context);
console.log($('#MyTextArea', context).data('CodeMirrorEditor'));
The first log results in the textarea being logged. Which is correct, because the ID referred to is a <textarea>.
The second log results in undefined. For some reason the stored instance of editor is lost or not accessible from outside the frame.
Any suggestions on how to approach this problem?
Your problem isn't with the context, but with the jQuery object itself.
Since the data() properties are set within the jQuery object that was defined in the original page, of course the new jQuery object can't access them.
To solve this, use the original jQuery object:
myOrigWindow.jQuery('some-selector').data(...);
Does removeChild function really delete the child node completely? Or it just removes the element being child of the specified parant node? If it doesn't really deletes the element, is there a way to delete the element completely?
The removeChild method simply removes it from its parent. If it’s a visible element of the page, it will be removed from the page.
But Javascript has garbage collection. This means that the node object itself will remain in existence as long as any variable refers to it. So you can assign a node to a variable, use removeChild to 'prune' it from its parent node, and later on, insert or append it to some other node, thereby effectively moving it around on the page.
The following code will remove a node, and wait 10 seconds before re-adding it to the tree (and thus, to the page):
var oldNode = someNode.removeChild(...);
setTimeout(function () {
document.documentElement.appendChild(oldNode);
}, 10000);
This means that the node object hasn’t been deleted from memory, because there’s still a variable pointing to it (namely, oldNode).
Another case:
var node = document.getElementById('test');
// ... do stuff
node.parentElement.removeChild(node);
// 'node' still exists, but has been removed from the page
// ... do some more stuff
node = document.getElementById('hello');
// The variable 'node' now points to something else;
// this means the original node will be deleted from memory
If, on the other hand, you don’t reassign the removed node to another variable, it can’t be accessed anymore (not via the document tree, since it’s been removed from there; and not via a JS variable); so Javascript will automatically purge it from memory:
someNode.removeChild(...);
Assigning the removed node to a variable, and then assigning null (or anything else) to that variable — like Marc B suggests in his answer — is completely unnecessary and, IMHO, silly.
This will completely delete the node:
someNode.removeChild(...);
This will remove the node from the DOM so it's not visible but will save it so that you can insert it elsewhere:
oldNode = someNode.removeChild(...);
removeChild removes the element from the dom, but it's also returned from the function in case you're doing the removal to re-insert it elsewhere. You'd have to kill that return value to really get rid of the removed node:
oldNode = someNode.removeChild(...);
oldNode = null;
If you want to really delete a dom element . removeChild alone is not enough. This is as per Steve Sounders who is the author of YSlow. You need to use delete