Michael Wallent
Lead Program Manager
Microsoft Corporation
March 2, 1998
The following article was originally published in the Site Builder Magazine (now known as MSDN Online Voices) "DHTML Dude" column. To fully understand Michael Wallent's Dynamic HTML column, you need Internet Explorer 4.0 .
One of the roles program managers (like me) play at Microsoft is that of project manager. We track the development tasks, and communicate the status of those tasks to other groups. Our project team uses a database, created with Microsoft Access, to track the status of the various tasks. Over the last year, as more and more groups at Microsoft have become interested in what we are doing with Internet Explorer development, many have asked for access to our task database.
What's a good way to present information in a database to a wide number of people?
Hmmm. The fact that I'm the DHTML Dude probably gives you more than a little hint on the direction that this solution took.
That's right, we used Dynamic HTML. How did you ever guess?We wanted to let people quickly and easily look at the tasks and the status of each. The logical place to put such information was on our team Web site on the Microsoft intranet. Since we had the task information in a database, we could use Internet Explorer's data-binding feature to get the information onto the Web page.
Each entry has a task title, a description, and is assigned to an owner on our team. The database includes information on the initial time estimate, a place to track the current estimate, and the number of days remaining until completion.
It seemed simple and logical to present this information in a table. Data binding (using the repeated row-binding model) provides a simple way to do this. However, there are now over 700 tasks in our database; a single page with all that information would be hard to manage.
We decided to allow the user to get the tasks for any particular team in which he or she might be interested. This is also easy using data binding -- we simply refine the query based on a value in a <SELECT> combo box on the page.
For such a simple task as this, the DHTML Dude didn't even have to break a sweat. This was simple, classical data binding.
Here's a link to the page we created. Notice how the information simply appears in the table.
All the data was now available for people to peruse at their leisure, and peruse they did. The questions started to trickle in.
"Could you make the tasks that are complete some other color, and while you are at it, mark the tasks that are in-progress?""How about some totals at the bottom? How many total days for selected tasks, and what percent complete that is?"
After a little thinking, we came up with the following page, which is more functional.
The new enhanced task-list page adds color coding for in-progress and complete tasks, and also determines total day count and the percentage of completed tasks for the selected team.In an early version of this page, the analysis of the table occurred when a user clicked an "Analyze" button. Users thought this a little painful, and wondered if the analysis could be kicked off automatically when the table was loaded with data.
There is a trick to finding out exactly when that happens. Two components must complete their tasks before the table is filled out. The data source object must get all of the data, and the table has to show all of the new data. It turns out that the best way to determine if a data-bound table is loaded is to look for the onreadystatechange event, and look to see when the readystate property is set to "complete."
Here's the code I used to do this: ("Tasks" is the ID of the bound table)
Tasks.onreadystatechange = test; function test() { if (Tasks.readyState == "complete") { if (Tasks.style.display != "none") { analyze(); } } }
Note that the table's readystate property will be set to "complete" as the page is loading, even though it has no data in it. To make sure that the analysis doesn't run when the table has no data, I detect the case when the page is loaded, but not full, by looking at the display property of the table. When the page is initially loaded, the table is set to "display == none". When the user selects which team to display and data is available, the display status is reset. Therefore, by looking to see if the table is readystate == complete and display != "none", I can tell whether the page is ready and has data. Phew.
Instead of looking at the display property, I could have looked at the Tasks.children.length property, to see if there was more than one row in the table.To determine if a task is complete or still in progress, look at the actual data for the column. Doing this is surprisingly easy. In the template row for the data-bound table, add an ID to each of the columns you want to track, such as:
<TD><DIV id=AllInit DATAFLD=Init></DIV></TD> <TD><DIV id=AllAct DATAFLD=Act></DIV></TD> <TD><DIV id=AllRem DATAFLD=Rem></DIV></TD>
Note that after the table is bound, the IDs on the DIV elements are not going to point to a single element, but to a collection of elements with the same name. All three collections in this case, AllInit, AllAct and AllRem, will have collections of DIVs. Interestingly (and very usefully), the collections will all be in the same order. That is that AllInit[1] will come from the same row as AllAct[1].
To figure out if a task is complete, we check to see if the remaining days are == 0. So if AllRem[n] == 0, the task is complete. If a task is in progress then AllAct[n] > AllRem[n] && AllRem[n] != 0. Here is the code I used to color-code the tasks:
function analyze() { var rd, id, ad, i; var trd, tid, tad, pct; trd=0; tid=0; tad=0; for (i=0;i<AllRem.length;i++) { rd = parseInt(AllRem[i].innerText); trd += rd; id = parseInt(AllInit[i].innerText); tid += id; ad = parseInt(AllAct[i].innerText); tad += ad; if (rd == 0) { AllRem[i].parentElement.parentElement.style. background="green"; } else if (rd < ad) { AllRem[i].parentElement.parentElement.style. background="yellow"; } } Totals.style.display = ""; TInit.innerText = tid; TAct.innerText = tad; TRem.innerText = trd; Pct.innerText = Math.round( ((tad-trd)/tad)*100 ); }
To get the actual number from the bound element, I looked at the innerText property of the DIV, and converted it to an integer, using the parseInt() function.
To color a row, the style of the containing <TR> needs to be changed. In the case of the DIV, its parent's parent is the <TR>. Handily, each element has a property called parentElement, which points to its parent. To turn the <TR> green, we need to set the background style of the parent of the parent of the DIV.
The following line of code does just that:
AllRem[i].parentElement.parentElement.style.backgrou nd="green";
Once all the data is captured, doing the actual calculations is a snap. Notice at each step in the loop I keep a running total of the total initial estimates, current estimates, and remaining days. When the loop completes, I use the innerText properties of the cells in the table footer to show this information. A simple calculation gives the percent complete.
Data binding is a powerful way to display data in your Web pages. When you combine that with some simple DHTML concepts, you can show your data and analyze it, too.
Michael Wallent, Site Builder Magazine (now known as MSDN Online Voices) monthly columnist on Dynamic HTML, is Microsoft's group program manager for DHTML.
For a comprehensive guide to information on DHTML, from introductory overviews to advanced how-to's, consult the Dynamic HTML pages in the MSDN Online Web Workshop.
Every month, Site Builder Magazine (now known as MSDN Online Voices) columnist Nadja Vol Ochs explores Web design issues and solutions in Site Lights. And look for articles in the Design section of MSDN Online Web Workshop.