Course: JavaScript

Progress (0%)

  1. Foundation

  2. Numbers

  3. Strings

  4. Conditions

  5. Loops

  6. Arrays

  7. Functions

  8. Objects

  9. Exceptions

  10. HTML DOM

  11. CSSOM

  12. Events

  13. Drag and Drop

  14. opt Touch Events

  15. Misc

  16. Project: Analog Clock

Exercise: append() Polyfill

Exercise 49 Easy

Prerequisites for the exercise

  1. HTML DOM — Selecting Elements
  2. HTML DOM — Attributes
  3. All previous chapters

Objective

Create a polyfill for the append() method of the Element interface.

Description

In the chapter HTML DOM — Elements, we got to know about the append() method of the Element interface that allows us to bulk-insert nodes into the DOM.

It can accept a variable number of arguments and then insert them all at once into the DOM.

If you're unaware of the append() method, then please consider understanding how it works, in HTML DOM — Elements — Bulk-inserting Nodes, before proceeding with this exercise.

Now the thing is that some browsers don't support the append() method. That is, we just can't invoke append() on these browsers as it's just not there!

In this exercise, you have to create a polyfill of append(), i.e. define it manually in JavaScript.

Your implementation must behave exactly like the native implementation of append(). That is,

  • The method MUST accept a variable number of arguments.
  • If an argument is not a Node instance, it MUST be coerced into a string.
  • The given nodes MUST be inserted into the DOM, all at once.
View Solution

New file

Inside the directory you created for this course on JavaScript, create a new folder called Exercise-49-append()-Polyfill and put the .html solution files for this exercise within it.

Solution

Let's start by setting up the wireframe of the method:

Element.prototype.append = function() {
   // Code to go here.
}

append() is a method of the Element interface, hence, it's defined on Element.prototype.

To implement append() as a variable-argument method, we don't need to define any parameter on it. The arguments object would be sufficient to address all the given arguments.

So focusing on arguments, what we'll need to do is to iterate over all the given arguments, and for each one, check if it is a Node instance or not.

  • If it's an Node instance, we must insert it right away into a document fragment.
  • Otherwise, we must coerce it into a string (using the String() function), create a text node based on the string, and then insert this text node into the same document fragment that we mentioned above.

Once all the nodes are added into a document fragment, we ought to insert this document fragment into the calling element node (i.e. the one on which append() was invoked) using its appendChild() method. This will insert all the nodes, at once, into the DOM, just as desired.

Altogether, this gives us the following code:

Element.prototype.append = function() {
   var fragment = document.createDocumentFragment();

   for (var i = 0, len = arguments.length; i < len; i++) {
      // If the argument is a Node, add it right away to the fragment.
      if (arguments[i] instanceof Node) {
         fragment.appendChild(arguments[i]);
      }

      // Else, coerce the argument into a string, create a text
      // node out of it and then add this node to the fragment.
      else {
         fragment.appendChild(document.createTextNode(String(arguments[i])));
      }
   }

   // Since this method is append(), dump fragment into the
   // last-child position of the calling element node.
   this.appendChild(fragment);
}

Note that the bodies of both if and else above call the fragment.appendChild() method with just a different argument provided to the method. We could simplify all of this using the ternary operator.

Here's the rewritten version of the code above, with the comments removed:

Element.prototype.append = function() {
   var fragment = document.createDocumentFragment();

   for (var i = 0, len = arguments.length; i < len; i++) {
      fragment.appendChild(
         arguments[i] instanceof Node
         ? arguments[i]
         : document.createTextNode(String(arguments[i]))
      );
   }

   // Since this method is append(), dump fragment into the
   // last-child position of the calling element node.
   this.appendChild(fragment);
}

Time to test this implementation.

Consider the following example:

<div id="main" style="border: 1px solid red;">
   <h1>A heading</h1>
</div>
Element.prototype.append = function() {
console.log('append() called'); /* ... */ } var h2Element = document.createElement('h2'); h2Element.textContent = 'A smaller heading'; var italicElement = document.createElement('i'); italicElement.textContent = 'italic text'; var mainElement = document.getElementById('main'); mainElement.append(h2Element, 'Simple ', italicElement);

Here we're just adding an <h2> inside the #main element, after <h1>, followed by a simple piece of text (normal text followed some italic text).

Note the console.log() statement that we've added in the append() method above for the sake of testing whether the method is actually called or not.

Alright, let's now see the output produced by this code:

Live Example

And it's just how it should be.

This completes this exercise.