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.
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.
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:
And it's just how it should be.
This completes this exercise.