Reputation: 5269
Consider this simple page:
<html>
<head>
<script>
var upper;
var lower;
function crop() {
//upper = document.getElementById('upper');
lower = document.getElementById('lower');
document.body.innerHTML = '';
document.body.appendChild(lower.cloneNode(true));
}
function up() {
document.body.innerHTML = '';
document.body.appendChild(lower.parentNode);
}
</script>
</head>
<body>
<div id="upper">
Upper Div
<div id="lower">
Lower Div
<button onclick="crop()">Crop</button>
<button onclick="up()">Up</button>
</div>
</div>
</body>
</html>
So Crop
button clears the body, clones the lower div and appends it to the body. The purpose of the Up
button is to make the parent of the lower div visible ( upper
div ) . In my first attempt, i tried to use lower.parentNode
, which doesn't work because it is null
. I think that is because the variable lower
is now pointing to a DOM element, which is not part of the body. (Is my understanding correct?) But now consider the commented line:
//upper = document.getElementById('upper');
which does nothing but get a reference to the upper div. When i uncomment this line, up()
starts working. What is strange here is that i am not using this variable in any way. Can you explain this behaviour?
Upvotes: 3
Views: 333
Reputation: 147403
You create a global variable called upper. When you run crop, you assign a reference to the element with id upper, which is stored as a global and remains available as a global variable referencing that element regardless of what you do to the DOM.
Just to explain further, by preserving upper, you also preserve all its descendants and their relationships so lower.parentNode still exists after you clear the body. But if you don't keep a reference to upper, then clearing the body also deletes any parent or sibling of lower, but not the descendants of lower.
Upvotes: 1
Reputation: 66334
When you set document.body.innerHTML = '';
, you're removing all child nodes of <body>
from the DOM tree, but not destroying the tree structure of those nodes itself. If there exist no references to these nodes, they are also destroyed and garbage collected.
You have set (global) lower = document.getElementById('lower');
, so there exists a reference to <div id="lower">
, this means that although it may be removed from the DOM tree, it doesn't get destroyed or garbage collected, as it is "kept alive" by this reference.
Now here comes the weird stuff, because you're mixing String methods (.innerHTML
) with DOM methods (getElementById
, appendChild
), the browser isn't sure what to save from garbage collection. It ends up deciding that childNodes
of referenced nodes can stay, but parentNode
s aren't a strong enough bond, so get destroyed. This means you don't uniformly get null
, you can also get
Uncaught Error: NotFoundError: DOM Exception 8
When you set (global) upper
, this saves <div id="upper">
, because you now have a reference keeping it alive. It also saves <div id="lower">
, because of upper
's childNode
s being protected from garbage collection, too. This preserves the childNode / parentNode
link between them.
It's also worth noting that cloneNode
may cause id attribute conflicts, as an id must be unique, but by definition, a clone is not unique.
Upvotes: 3