lunr
lunr

Reputation: 5269

DOM: cropping an element and using parentNode

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

Answers (2)

RobG
RobG

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.

Edit

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

Paul S.
Paul S.

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 parentNodes 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 childNodes 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

Related Questions