Cloning

Earlier in the course, we learned how we can dynamically built HTML structure. In CoffeeRun, we built checkboxes, and we've built quite elaborate structure in the Quizzes assignment.

The strategy was to use jQuery to build each element, add attributes, and using the jQuery .append() method to combine them all. Like:

function makeRow(coffeeOrder) {
    var $div = $("<div>", {attributes})
    var $label = $("<label>", ...);
    $div.append($label);
    ...
}

That works well for small bits of structure but it doesn't scale. At some point, if you're building a lot of structure, there's a better way, namely cloning. (One could argue that we should have done that with the Quizzes assignment, but it's good to learn the basics before we get to cloning.)

Cloning

Of course, to best motivate cloning over piecemeal building of structure, I should have an example with a huge amount of HTML. But that's annoying and difficult to read, so I'll keep the HTML modest in size, and you should check that these techniques are no harder if the HTML is a lot bigger.

Our example is going to be a checklist structure, very similar to CoffeeRun. The only change I'll make is to put the text that is next to the checkbox inside a span, so that it has a convenient container. We'll see why in a moment.

Static Checklist

Here's an example of a checklist, each element of which is a checkbox, plus surrounding structure. This list just has two checkboxes, but it could have more.

<div id="static-checklist">
   <div data-coffee-order="checkbox"
        class="checkbox">
       <label>
           <input type="checkbox" value="scott@bignerdrand.com">
           <span class="description">
               grande mocha mocha mucho, (scott@bignerdranch.com) [57x]
           <span>
       </label>
   </div>
   <div data-coffee-order="checkbox"
        class="checkbox">
       <label>
           <input type="checkbox" value="fred@bignerdranch.com">
           <span class="description">
               short Earl Grey, (fred@bignerdranch.com) [16x]
           <span>
       </label>
   </div>
</div>

Here it is in action:

Templates

As you can see, each checklist structure is the same, varying only in the value attribute of the checkbox and the content of the span.

We can write the structure out in HTML, putting it inside a template element. The template tag that was added to the HTML language for the purpose of templating like we will do. It holds structure that will not be rendered. It might look like this:

<template>
  <div data-coffee-order="checkbox"
        class="checkbox">
       <label>
           <input type="checkbox" value="email here">
           <span class="description">
               description here
           </span>
       </label>
  </div>
</template>

Here it is:

Unsurprisingly, since it's not rendered, it's invisible. But you can inspect the element and see it in the structure of the page.

DIV Templates

The disadvantage of template is that it's not rendered, so we can't easily see whether we made a mistake in our HTML. Being able to write HTML and debug it in a normal way is, to my mind, one of the great advantages of cloning, so I usually do not use the template tag. Instead, I use an ordinary div, but I then use CSS to make it disappear once I have the HTML debugged.

Here's the DIV template:

<div id="checkbox-template" class="template">
  <div data-coffee-order="checkbox"
        class="checkbox">
       <label>
           <input type="checkbox" value="email here">
           <span class="description">
               description here
           <span>
       </label>
  </div>
</div>

Here's what it looks like:

It looks like the static checklist above, so there's no surprise here.

Hiding the DIV Template

You'll notice that the template div (the outer one) has an ID and a class. Either can be used for styling. When we are done debugging all our templates, we can make them all disappear by adding this to our CSS file:

.template { display: none }

We used the class for styling (in this case, hiding) and we will use the ID used to specify which template we will use in a particular cloning operation.

The Cloning Technique

Our strategy will be to

  1. clone the HTML stuff inside the template,
  2. modify its contents for a particular checkbox, and
  3. add the clone to the page.

Note that the clone is a complete copy of all of the HTML stuff, so the programming effort involved in step 1 is the same whether the template is a little bit of HTML or a lot. Therein lies the great advantage of cloning. Also, note that we are cloning the stuff inside the div, so the clone will not have an ID. Since IDs need to be unique identifiers, it doesn't make sense to have the same ID on every clone. Our clones will not have IDs.

In step 2, we insert or modify whatever stuff needs to be customized. For our checkbox example, we have to modify the value attribute of the checkbox and the contents of the span.description. In this step, we can use jQuery methods to .find() parts of the clone and modify them.

Finally, in step 3, we add the clone to the page. The clone is initially offstage, so it's useless until it's added to the page.

Creating a Checkbox By Cloning

Let's see an example using checkboxes. We'll write a JS function to create a checkbox, supplying two arguments for the custom information, and we will add it to the page. It will be added to an empty checklist element that looks like this:

<div id="dynamic-checklist"></div>

(Compare that with the outermost div in the static checklist.)

Here's our JS function:

function addCheckbox(email, description) {
    // step 1, clone
    var copy = $("#checkbox-template > div").clone();
    // step 2, modify
    copy.find("input[type=checkbox]").val(email);
    copy.find("span.description").text(description);
    // step 3, add to page
    $("#dynamic-checklist").append(copy);
}

Here is how we might use it to generate our static checklist:

addCheckbox('scott@bignerdranch.com', 
   'grande mocha mocha mucho, (scott@bignerdranch.com) [57x]');
addCheckbox('fred@bignerdranch.com', 
   'short Earl Grey, (fred@bignerdranch.com) [16x]');

Here it is in action:

Analysis

Let's say a bit more about each step.

Finding the Stuff to Clone

Notice that the selector string to find the thing to clone was #checkbox-template > div. That selects the DIV that is a child of the element with id checkbox-template. There is only one such child, so it finds exactly the thing we want. So the surrounding div#checkbox-template holds the thing we want to clone, but we clone the thing inside the container. The container has an ID (so we can specify it easily), but the clone does not have an id.

You'll also notice that the cloning takes one line of code, regardless of how big and complex our template is. Therein lies the advantage of cloning

Modifying the Clone

In this example, it takes us two steps to modify the clone. The more dynamic stuff, the more code it will take. If the template is small and almost every part needs updating, that would undercut the usefulness of cloning, but that's not often the case.

Adding to the Page

The last step of adding the clone to the page is very easy here. It usually is, unless the location to put the clone is difficult to find. But in this case, we just need to append the dynamically created checkbox to our static container, namely div#dynamic-checklist.

No IDs

The purpose of IDs on elements is to uniquely identify them. If we are making copies of the template, and the template has an ID on any of its elements, that ID will be duplicated and no longer uniquely identify the element. Therefore:

Never put IDs on Dynamic Elements

While it's certainly possible to create IDs for dynamic elements and use them successfully, it's tricky and usually unnecessary. I suggest avoiding ID on all dynamic elements.

Summary

  • Create some template HTML for your dynamic elements
  • in JS, to add one dynamic element to your page:
    1. clone the template
    2. modify the clone
    3. add the clone to the page
  • You can use CSS to hide the template