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
- clone the HTML stuff inside the template,
- modify its contents for a particular checkbox, and
- 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:
- clone the template
- modify the clone
- add the clone to the page
- You can use CSS to hide the template