Generating and injecting HTML content with Javascript and jQuery

Although I am a .NET developer, a recent small project I was working on wasn’t .NET but just plain ol’ HTML pages. I’m our team’s jQuery expert but that’s not saying much at all – I only earned that title by buying jQuery in Action and I have it sitting on my desk.

DISCLAIMER: I’m not a Javascript or jQuery expert! I’m still learning this stuff.

The project was a Knowledge Base. The requirements were that the user would choose a topic from a drop-down, then a subtopic, then a sub-sub topic and finally the “Questions” relating to that topic would be displayed. So away we went, writing and implementing “GetSubTopicsForTopic” web services which would populate the cascading drop downs. Fine and dandy.

Obviously, depending on which sub-subtopic they choose, the number of “Questions” returned will vary. Some subtopics (e.g. Contact Us”) will only have one Question while others would have 5 or 6. We would need to dynamically generate the HTML to display each Question (and it’s Answer) and then inject it.

Step 1: Getting the Answers

Here’s our jQuery.ajax call to get the Questions for the sub-subtopic. We first used .getJSON but were having some concurrency issues with it, so changed to .ajax and set async to false.


function showAnswers(subSubTopicID) {
$.ajax({
    async: false,
    url: '/_layouts/kb/services/getquestions.ashx',
    data: { subSubTopicid: subSubTopicID },
    type: 'GET',
    dataType: 'json',
    success:
       function(questions) {
          if (questions == null || questions.length == 0) {
             return;
          }

          // set header text
          var resultText = "Your query returned " + questions.length +  ((questions.length == 1) ? " result." : " results.");
          $('.header').text(resultText);
          renderQuestions(questions);
       }
   });
}

Now that we’ve gotten an array of Questions back we need to render them.

Here’s what it looks like again:

The HTML we need to generate for each Question and Answer (Q&A) is quite long, about 70 lines, as follows:

<ul id="results-list">
   <li style="border-bottom: #c4c3c3 1px dotted" id="Question77">
      <a id="question_77" href="#">
         What developments will contributions be charged on?
      </a>
      <div style="zoom: 1; display: block">
         <div class="answerText">
             Contributions will be charged on any development or change in use or service connection that creates demand on the city’s infrastructure. The only exception is for residential
   ...

A parent <li> followed by a bunch of child nested <div>, all within a <ul>.

Generating HTML from Javascript was something I’d never done before,  but I had worked on existing projects which do this.

Option 1: Embedding HTML in the Javascript

I’ve seen this done on a number of projects. e.g.

function renderQuestion(question) {
   // generate the html for the question
   var html = '<li style="border-bottom: #c4c3c3 1px dotted" id="Question' + question.QuestionID + '">';
   html += '<a id="question_' + question.QuestionID + '" href="#">';
   html += question.QuestionText;
   html += '</a>';
   html += '<div style="filter: ; zoom: 1; display: block">';
   html += '<div class="answerText">';
   html += question.Answer;
   html += '</div></div></li>';

   // add it to the DOM tree
   var results = document.getElementById('results-list');
   results.innerHTML += html;
}

The thing I don’t like about this is you’re mixing the HTML in with the Javascript, so that if the HTML design needs to change then you need to change the Javascript too. On the bright side, it should be reasonably simple to change the HTML, but as a programmer it smells bad.

Option 2: Creating DOM elements

Use Javascript (or jQuery) to create the <li> and <div> and then append them, like so.

function renderQuestion(question) {
   var listItem = document.createElement('li');
   listItem.setAttribute('style', 'border-bottom: #c4c3c3 1px dotted');
   listItem.setAttribute('id', 'Question' + question.QuestionID);

   var a = document.createElement('a');
   a.setAttribute('href', '#');
   a.setAttribute('id', 'question_' + question.QuestionID);
   a.innerHTML = question.QuestionText;
   listItem.appendChild(a);

   var answerDiv = document.createElement('div');
   answerDiv.className = 'answerText';
   answerDiv.innerHTML = question.Answer;
   listItem.appendChild(answerDiv);

   var results = document.getElementById('results-list');
   results.appendChild(listItem);
} 

Option 2 is more code than Option 1. As a programmer it feels cleaner, since I’m manipulating properties on objects rather than having hard-coded HTML. But it’s less readable than Option 1 and would be more difficult to change the HTML.

Option 3: Duplicate an HTML sample row and add it to the DOM

This was an idea that I came up with, it’s probably been done somewhere else before but I’ve never seen it.

Update: Ha, although I came up with this idea myself, Rick Strahl blogged this exact same technique way back in October 2008, he calls it ‘Manual’ Templating.

I’ll have an invisible “sampleQuestion” which contains the <li> and all its <div> embedded in the HTML.

help.html:
<ul id="results-list">
   <!--    Sample Question used for cloning   -->
   <li id="sampleQuestion" style="display: none;border-bottom:1px dotted #c4c3c3">
      <a href="#" class="questionLink"></a>
      <div style="zoom: 1; display: block">
      <div class="answerText"></div>
...

I’ll use jQuery to make a copy of this sample Question, change some attributes, make it visible, and then add it to the DOM.

help.js:
function renderQuestion(question) {
   var sampleQuestion = $("#sampleQuestion");
   var listItem = sampleQuestion.clone(false);
   listItem.attr('id', 'Question' + question.QuestionID);

   var questionLink = listItem.find('.questionLink');
   questionLink.attr('id', 'question_' + question.QuestionID);
   questionLink.text(question.QuestionText);

   listItem.find(".answerText").text(question.Answer);

   listItem.show();
   listItem.insertBefore(sampleQuestion);
}

I like this approach because the page design and layout is kept in the HTML file where it belongs. If your designer wants to change the layout he can, as long as he doesn’t change the ids and the class names too much. The only thing the Javascript does is set the IDs and set the content.

Update – Option 4: jQuery Templating

I only just found out about this, but a number of existing jQuery solutions exist for dealing with this exact scenario. Rick Strahl’s blog post looks at a few, and now Microsoft have proposed their own solution which might be added to jQuery in the future.

Summary

On the Knowledge Base project I implemented a ‘manual’ templating solution because I didn’t know better, but jQuery has a number of solutions for generating and injecting HTML content which are worth further investigation.

  1. John Resig’s Micro-Templating looks like a good option
  2. jTemplates
  3. PURE (Pure Unobtrusive Rendering Engine), a Javascript library (not jQuery specific)
Advertisements

2 thoughts on “Generating and injecting HTML content with Javascript and jQuery

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s