Using inline HTML as templates

I was tired of embeding HTML inside Javascript: How many times have you gotten right on the first try
var template = '<p id="'+base_id+'-a">This is the template. ' + 
  '<b id="'+base_id+'-b">And this is the tempalate, too.</b></p>';

Where as we always seem to get right
<p id="xxx-a">
  This is the template. <b id="xxx-b">And this is the tempalate, too.</b>
</p>

So, instead I wanted to place the HTML inline in the document and clone it from Javascript when needed. This allowed me to more easily code and then syntactically and visually check the HTML by rendering in the browser.

Unfortunately, there is no deep clone for DOM elements. And even if there was one I suspect it would not handle ID attributes as I wanted. So I wrote my own clone_dom and clone_dom_element functions.


<html>
<head>
<script type="text/javascript" src="http://prototypejs.org/javascripts/prototype.js"></script>
<script type="text/javascript">

  /*
   * clone_dom
   *
   * clone's the DOM element tree starting at template_id and appends to
   * element at target_id. The id_map is a simple hash that maps an id in
   * the template elements to an id in the cloned elements.
   */
  function clone_dom( template_id, target_id, id_map ) {
    var clone_template = clone_dom_element( $(template_id), id_map );
    $(target_id).appendChild( clone_template );
  }

  /*
   * clone_dom_element
   *
   * Clone's and returns the DOM element tree. The id_map is a simple hash
   * that maps an id in the template elements to an id in the cloned elements.
   */
  function clone_dom_element( element, id_map ) {
    var clone_element = element.cloneNode(false);
    if ( clone_element.getAttribute ) {
      var id = clone_element.getAttribute('id');
      if ( id != null ) {
        if ( id_map[id] ) {
          clone_element.setAttribute('id', id_map[id]);
        }
        else {
          clone_element.removeAttribute('id')
        }
      }
    }
    var child_nodes = element.childNodes
    for( var i = 0; i < child_nodes.length; i++ ) {
      var clone_node = clone_dom_element( child_nodes[i], id_map );
      clone_element.appendChild( clone_node );
    }
    return clone_element;
  }

</script>
</head>
<body>

  <!-- Place the templates in a non-displaying part of the document. Since the
     templates will be used in building the document keep them near the top.
     (When debugging the templates display them.)
  -->
  <div id="templates" style="display: none">

    <p id="template-a">
      This is the template. <b id="a-b">And this is the tempalate, too.</b>
    </p>

    <p id="template-x">
      <table border="1">
        <tr><th>Column 1</th><th>Column 2</th></tr>
        <tr><td>Value 1</td><td>Value 2</td></tr>
      </table>
    </p>

  </div>


  <p>Something before the cloned elements</p>

  <!-- There is where the templated DOM will be placed when cloned
  -->
  <div id="location-m"></div>

  <p>Something after the cloned elements</p>

  <!-- We are going to clone template-a and template-x into location-m
  -->
  <script type="text/javascript">
    clone_dom( 'template-a', 'location-m', { 'template-a': 'foo1', 'a-b': 'bar1' } );
    clone_dom( 'template-x', 'location-m', { 'template-x': 'foo2'                } );
    clone_dom( 'template-a', 'location-m', { 'template-a': 'foo3', 'a-b': 'bar3' } );
    clone_dom( 'template-x', 'location-m', { 'template-x': 'foo4'                } );
  </script>

</body>
</html>