1
\$\begingroup\$

My site consists of a lot of asynchronous calls, and because of this a lot of content is dynamically added to the page. For this section of code I've dynamically added tabs to a page and need to create the selected tab section. This function is fired when a tab is clicked (note that $devices_page is a pre-existing divider):

function populateDevicesPage(deviceArray) {
    // Hide the current page
    $devices_page.stop().slideUp(animationSpeed, function() {
        // Check if the $devices_page has any current children
        if($devices_page.children().length > 0)
        { /* Ignore this part, it's the below else that matters */ }
        // If not, create page elements
        else
        {
            // Create the main devices page elements
            var
                $device_name = $('<h1/>').appendTo($devices_page),
                $device_inner_page = $('<div id="devices-page-inner"/>').appendTo($devices_page),
                $commands_table = $('<table/>').appendTo($device_inner_page),
                $commands_thead = $('<thead/>').appendTo($commands_table),
                $commands_tbody = $('<tbody/>').appendTo($commands_table),
                $commands_thead_row = $('<tr/>').appendTo($commands_thead)
            ;

            // Create the table headings
            $('<th/>').text('Name').appendTo($commands_thead_row);
            $('<th/>').text('Command').appendTo($commands_thead_row);

            // Loop through each of the commands to populate the table
            for(var i=0;i<deviceArray.commands.length;i++)
            {
                var $command_tbody_row = $('<tr/>').appendTo($commands_tbody);
                $('<td/>').text(deviceArray.commands[i].name).appendTo($command_tbody_row);
                $('<td/>').text(deviceArray.commands[i].com).appendTo($command_tbody_row);
            }

            $device_name.text(deviceArray.deviceName);
        }

        // Show the newly populated page
        $devices_page.stop().slideDown(animationSpeed);
    });
}

As you can see there are a lot of elements being created and added to the pre-existing $devices_page divider. This does only happen once (when a tab is selected for the first time), but I was wondering if there's a much better approach I could adopt instead.

The full code can be seen here if anyone wants a further poke around: https://github.com/JamesDonnelly/WebRemoteNotifier/blob/master/js/generic.js. Do note that this is a very rough draft of a project I've only spent about 3-4 hours working on, so I imagine there are a lot of imperfections; for now I'm only really concerned about the above function.

Thanks a lot!

\$\endgroup\$
  • \$\begingroup\$ Check out knockout.js. It provides data (JSON) binding to an existing markup template (HTML). This will remove your need to manually generate as much markup. \$\endgroup\$ – Paul Fleming May 19 '13 at 14:12
1
\$\begingroup\$

If you don't need the data as JSON, you could just have the server send you the table markup. Then just inject it straight into the page using .load()

Alternatively, if you want JSON data, or can't/won't mess with the server side, you might consider having a hidden, empty table in the markup to begin with, so you don't need to create the entire thing from scratch. Then you can just populate the header cells with the relevant text, and only worry about adding the rows to tbody.

Of course, it'll introduce tighter coupling between the markup and the JS, which you might want to avoid. Since this is a new project that's probably changing all the time, it might be best to simply stick with you current approach of doing everything in JS, to keep things flexible. But my thinking is that since you're basically hardcoding things like the table headers in JavaScript, you're better off hardcoding it the markup where it belongs, rather than generating it.

And you can also make the coupling a little looser, by using some data-* attributes in the "template" table to declare their intended content.

For instance, have this in the page markup

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Command</th>
    </tr>
  </thead>

  <tbody>
    <!-- hidden row template -->
    <tr class="template">
      <td data-property="name"></td>
      <td data-property="com"></td>
    </tr>
  </tbody>
</table>

Then, on the JS-side, do something like:

var tbody    = $("tbody"),
    template = tbody.children(".template").removeClass(".template").remove(), // removed, but still in memory
    newRow, item;

for(var i = 0, l = deviceArray.commands.length ; i < l ; i++) {
  item   = deviceArray.commands[i];
  newRow = template.clone();
  newRow.children("[data-property]").each(function () {
    var cell = $(this),
        prop = cell.data("property");
    cell.text(item[prop]);
  });
  newRow.appendTo(tbody);
}

That should populate table nicely.

Of course, for such a simple two-column table, it might be overkill. Still, if you need to populate more tables like this, you can create a pretty generic function that you just pass a "scaffold" element like the empty table above plus a data array, and use that anywhere it's needed.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.