Modifying the document

  1. Creating elements
  2. Adding elements
  3. Removing nodes
  4. Tasks and examples
  5. Summary

DOM modifications is the key to making pages dynamic. Using the methods described below, it is possible to construct new page elements and fill them on-the-fly.

The DOM manipulation methods described above are defined in DOM Level 1.

Creating elements

There are following methods for creating new elements in DOM:

document.createElement(tag)
Creates a new DOM element of type node:
var div = document.createElement('div')

document.createTextNode(text)
Creates a new DOM element of type text:
var textElem = document.createTextNode('Robin was here')

The createElement is the most commonly used method, but createTextNode is also good. It creates a text node that can be appended to another element.

For an empty element, creating a text node and appending it works much faster than innerHTML in most modern browsers.

But innerHTML is simpler and supports nested tags, so they both find their place in the world.

Cloning

An element can also be cloned:

elem.cloneNode(true)
Clones an element deeply, with all descendants.
elem.cloneNode(false)
Clones an element only, with attributes, but without children.

Adding elements

To do something with the element, you need to call the corresponding method of its parent:

parentElem.appendChild(elem)
Appends elem to the children of parentElem.

The following example demonstrates creating and adding new element to BODY:

<div>
  ...
</div>
<script>
  var div = document.body.children[0]

  var span = document.createElement('span')
  span.innerHTML = 'A new span!'
  div.appendChild(span)
</script>

The new node becomes a last child of the parentElem.

There’s an empty DOM node elem. What’s the difference?

elem.appendChild(document.createTextNode(text))

versus
elem.innerHTML = text

Open solution
Solution

createTextNode escapes node content.

Compare the two examples.

  • createTextNode makes text '<b>tag</b>':
    <div></div>
    <script>
      var text = '**tag**'
    
      var elem = document.body.children[0]
      elem.appendChild(document.createTextNode(text)) 
    </script>
    
  • innerHTML assigns HTML <b>tag</b>:
    <div></div>
    <script>
      var text = '**tag**'
    
      var elem = document.body.children[0]
      elem.innerHTML = text
    </script>
    

parentElem.insertBefore(elem, nextSibling)
Inserts elem into the children of parentElem before the element nextSibling.

The following example inserts a new node before the first child:

<div>
  ...
</div>
<script>
  var div = document.body.children[0]

  var span = document.createElement('span')
  span.innerHTML = 'A new span!'
  div.insertBefore(span, div.firstChild)
</script>

Note that you insertBefore with second argument null works as appendChild.

elem.insertBefore(newElem, null) // same as
elem.appendChild(newElem)

All insertion methods return the inserted node.

Removing nodes

There are two main methods for removing nodes from DOM:

parentElem.removeChild(elem)
Remove the elem from the children of parentElem.
parentElem.replaceChild(elem, currentElem)
Replace the child element of parentElem, referenced by currentElem with the elem.

Both of them return a removed node which can later be reinserted into the DOM.

If you want to move an element, you don’t have to remove it first.

elem.appendChild/insertBefore remove elem from it’s previous place automatically.

The following example moves the last child to the top of children list:

<div>First div</div>
<div>Last div</div>
<script>
  var first = document.body.children[0]
  var last = document.body.children[1]

  document.body.insertBefore(last, first)
</script>

The removal occurs automatically when insertion methods are called for a node which already has a parent.

Tasks and examples

The following tasks can serve as examples depending on whether you read or try to solve them. Of course, it’s always better to solve Wink

Write a function which removes an element from DOM without referencing its parent.
The syntax should be: remove(elem)

<div>Very</div>
<div>Secret</div>
<div>Child</div>

<script>
  var elem = document.body.children[0]

  function remove(elem) { /* your code */
*!*
  remove(elem)   // <-- should remove the element
*/!*
</script>

Open solution
Solution

The parent of elem is referenced by the parentNode property.

And better don’t forget to return the removed element for compatibility.

So the code can be like this:

function remove(elem) {
  return elem.parentNode.removeChild(elem)
}

Write a function insertAfter(elem, refElem) to insert elem right after refElem.

<div>Very</div>
<div>Secret</div>

<script>
  var elem = document.createElement('div')
  elem.innerHTML = '**Child**'

  function insertAfter(elem, refElem) { /* your code */

  insertAfter(elem, document.body.firstChild) // <--- should work
  insertAfter(elem, document.body.lastChild)  // <--- should work 
  
</script>

Open solution
Solution

To insert an element after refElem, we can insert it before refElem.nextSibling.

But what if there is no nextSibling? That means refElem is the last child of its parent, so we can just call appendChild.

The code:

function insertAfter(elem, refElem) {
    var parent = refElem.parentNode
    var next = refElem.nextSibling
    if (next) {
        return parent.insertBefore(elem, next)
    } else {
        return parent.appendChild(elem)
    }
}

But the code could be much shorter if it used insertBefore null second argument feature:

function insertAfter(elem, refElem) {
    return elem.parentNode.insertBefore(elem, refElem.nextSibling)
}

If there is no nextSibling then the second argument of insertBefore becomes null and then insertBefore(elem,null) works as appendChild.

Write a function removeChildren to remove all children of an element.

<div>Very</div>
<div>Secret</div>
<div>Children</div>

<script>
  function removeChildren(elem) { /* your code */

  removeChildren(document.body) // makes BODY absolutely empty  
</script>

Open solution
Solution

First, let’s check a way how it shouldn’t be done:

function removeChildren(elem) {
  for(var k=0; k<elem.childNodes.length;k++) {
    elem.removeChild(elem.childNodes[k])
  }
}

If you check it in action - you’ll find that it doesn’t work.

That’s because childNodes always starts from 0, it autoshifts when first child is removed. But k increases +1 every iteration. So, k skips half of nodes.

The right solution:

function removeChildren(elem) {
   while(elem.lastChild) {
       elem.removeChild(elem.lastChild)
   }
}

Another solution:

function removeChildren(elem) {
   elem.innerHTML = ''
}

Create an interface to generate the list.

For each item:

  1. Prompt the user for it’s contents.
  2. Create the item and append it to UL.
  3. The process ends when user presses ESC.

All elements must be created dynamically.

The working example is here: tutorial/browser/dom/createList.html

P.S. prompt returns null if user pressed ESC.

Open solution
Solution

The solution is self-descriptive:

<!DOCTYPE HTML>
<html>
<body>
<h1>Creation of the list</h1>

<script>
  var ul = document.createElement('ul')
  document.body.appendChild(ul)

  while (true) {
    var data = prompt("Enter the contents for the item", "")

    if (data === null) {
       break
    }

    var li = document.createElement('li')
    li.appendChild(document.createTextNode(data))
    ul.appendChild(li)
  }
</script>
     
</body>
</html>
Open the code in new window

Note checking for null value as a loop-breaking condition. The prompt only returns it when ESC is pressed.

LI contents is populated by document.createTextNode to ensure proper work of <, > etc.

Summary

Creation methods:

  • document.createElement(tag) - creates a new element node.
  • document.createTextNode(value) - creates a new text node with given value
  • elem.cloneNode(deep) - clones the element

Inserting and removing methods are called from parent node. All of them return elem:

  • parent.appendChild(elem)
  • parent.insertBefore(elem, nextSibling)
  • parent.removeChild(elem)
  • parent.replaceChild(elem, currentElem)

Tutorial

Donate

Donate to this project