The previous post provided code for using tabs to navigate the content of a multi-column table. It includes a feature that lets a user select the columns to display. If there are many columns in the table, the list will get long and it may be necessary to scroll the page. I just found a small jQuery that can easily reformat the list. The plug-in is available at: http://fredkelly.net/multi-column-lists-meet-jquery.
See the updated tabbed table example.
It can be implemented using the single line of code at the end of this code block.
$('#tabSelCols').click(function() {
var popList = "<ul>";
var chkbox = "<li><label><input type='checkbox'>";
$('#tabTable tr:first td').each(function() {
var colName = $(this).text();
popList = popList + chkbox + colName + "</label></li>"
})
$('#colList').html(popList);
$('#selectColumns').slideDown(300);
$('#colList ul').multilists({ cols: 2 });
})
Thanks to Fred Kelly for this plug-in.
August 23rd, 2010 in
Uncategorized |
No Comments
It appears that people generally do not like horizontal scrolling. This creates a design dilemma for presenting information in multi-column tables. A possible alternative is to hide and show groups of columns with a tab design. This code lets you
- Define and create tabs for groups that can contain any number of columns from any location in the table.
- Let users select their own group of columns. The Select Columns tab pop-ups up a list of all the columns with a checkboxes.
- Click a row to display a pop-up with all the data in that row.
View Working Example
HTML
Tabs
- The custom ‘columns’ attribute contains a comma delimited list of the columns that will appear when that tab is clicked.
- The class “activeTab” applies a style to the selected tab
- The ‘tabSelCols’ id is used to capture the click on the Select Columns tab and display the pop-up list of all columns
1: <ul id="colTabs">
2: <li class="activeTab" columns="0,1,2,3,6" >Column Group 1</li>
3: <li columns="0,7,8,9,10" >Column Group 2</li>
4: <li columns="0,11,12,13">Column Group 3</li>
5: <li columns="0,14,15,16">Column Group 4</li>
6: <li id="tabSelCols" columns=''>Select Columns</li>
7: </ul>
Select Column Pop-up
1: <div id="selectColumns" class="popSelectCol">
2: <div class="popTitle">
3: <span style="float:left">Select Columns to View</span>
4: <span id="closePop">X</span></div>
5: <div id="colList"> </div>
6: <div align="center">
7: <button id="btnShow">Show</button></div>
8: </div>
Row Details Pop-up
1: <div id="rowDetails" class="popSelectCol">
2: <div class="popTitle">Details</div>
3: <div id="details"> </div>
4: <div align="center"><button id="btnClose">Close</button></div>
5: </div>
jQuery
Hide and Show the Columns Groups
This code will add a column specific class to the <td> tag in each column. We can then reference the class to hide and show the <td> tags.
1: $('#tabTable tr').each(function() {
2: $(this).find('td').each(function(i){
3: tag = 'c' + i;
4: $(this).addClass(tag);
5: })
6: })
This code find the comma separated list of column numbers in the default tab and displays them when the page loads.
1: var colNums = $('.activeTab').attr('columns');
2: $('#tabTable').hide().showColumns(colNums);
This code initially hides all the columns and then shows only the columns that are passed in the array ‘colids’.
1: $.fn.showColumns = function(colids) {
2: $('#tabTable td').addClass('elHide');
3: var colShow = colids.split(',');
4: for(i=0; i<colShow.length; i++) {
5: var thisCol = colShow[i];
6: $('.c' + thisCol).removeClass('elHide'); }
7: $('#tabTable').show();
8: }
This code change the active tab style and passes the array of column numbers to the above function to show the columns that belong to the group.
1: $('#colTabs li').live('click',function() {
2: $('#colTabs li').removeClass('activeTab');
3: var colNums = $(this).attr('columns');
4: $('#tabTable, #selectColumns, #rowDetails').hide();
5: $(this).addClass('activeTab').showColumns(colNums);
6: })
Select Column Popup
This code creates the pop-up based on the column heading labels.
1: $('#tabSelCols').click(function() {
2: var popList = "<ul>";
3: var chkbox = "<li><label><input type='checkbox'>";
4: $('#tabTable tr:first td').each(function() {
5: var colName = $(this).text();
6: popList = popList + chkbox + colName + "</label></li>"
7: })
8: $('#colList').html(popList);
9: $('#selectColumns').slideDown(300);
10: })
This code displays the selected columns
1: $('#btnShow').click(function() {
2: var chkSelected = '';
3: $('#colList input:checked').each(function(i) {
4: var checkStatus = $(this).attr('checked');
5: chkSelected = chkSelected + i + "," ;
6: $('#tabTable').hide().showColumns(chkSelected);
7: $('#selectColumns').slideUp(300);
8: })
9: })
Show All Data in a Row
This code gets the column heading labels and the text in each <td> tag for the selected row.
1: $('#tabTable tr').live('click',function() {
2: var colLabels = [];
3: var rowData = [];
4: var tbl = $(document).rowDetails(2);
5: $('#details').html(tbl);
6: $('#tabTable tr:first td').each(function(e) {
7: colLabels[e] = $(this).text();
8: })
9: $(this).children().each(function(e) {
10: rowData[e] = $(this).text();
11: })
12: $('#rowValues tr').each(function(e) {
13: $(this).children(':first').text(colLabels[e] + ':');
14: $(this).children(':eq(1)').text(rowData[e]);
15: })
16: $('#rowDetails').slideDown(300);
17: })
18:
This code builds a 2 column table. The first column contains the column heading labels and the second the text in the selected column.
1: $.fn.rowDetails = function(cols) {
2: var tblRow = $('#tabTable tr:first td');
3: var rows = tblRow.length;
4: var tblTag = "<table cellpadding=3 cellspacing=0 border=0 id=rowValues>";
5: var colTags = "";
6: var rowTags = "";
7: var grid = " ";
8: for(c=0; c<cols; c++) {
9: colTags = colTags + '<td> </td>';
10: }
11: for(r=0; r<rows; r++) {
12: rowTags += '<tr>' + colTags + '</tr>'
13: }
14: grid = tblTag + rowTags + '</table>';
15: return(grid); }
In a future post I hope to provide the code to view details for selecting multiple rows. This would be good for a side by side comparison.
To use this code it would be best to download the example, replace the table element and update the style to match your site styles.
Thanks to GENERATEDATA.COM for the data that contained in the example
I hope you find this interesting.
August 15th, 2010 in
Uncategorized |
No Comments
Quickly, what’s this number: 100000
Is it 1,000 or 100,000 or 1,000,000
So how long did it take to figure out it was 100,000? Was it instantaneously or did you need to count the number of zeros? Imagine having a job where you frequently had to enter large amounts in data fields that did not dynamically add the thousand separator. What kind of error rate do you think you might have?
This posting contains a JavaScript for dynamically adding a thousand separator to a value in a numeric field. The function is a combination of code posted on the internet. The code for adding the separator came from mredkj.com. While this code works well for adding the commas with an onchange event it does not work well when using the onkeyup event. Dynamically updating the amount with the correct placement of thousand separator required a function to locate and reset the position of the caret in the field. The code for this came from a posting on webdeverloper.com
Here is an example
Here is the HTML for the field
<input type="text" id="numTest" style="text-align:right" onkeyup="addSeparator(this)" />
Here is the code
1: function addSeparator(fldID) {
2: var posCaret = getPosition(fldID);
3: var fldVal = fldID.value;
4: if((fldVal.length === 3 || 7 || 11) && (fldVal.length === posCaret)) {
5: posCaret = posCaret +1;
6: }
7: nStr = fldVal.replace(/,/g,'');
8: nStr += '';
9: x = nStr.split('.');
10: x1 = x[0];
11: x2 = x.length > 1 ? '.' + x[1] : '';
12: var rgx = /(\d+)(\d{3})/;
13: while (rgx.test(x1)) {
14: x1 = x1.replace(rgx, '$1' + ',' + '$2');
15: }
16: fldID.value = x1+x2;
17: setCaretPosition(fldID, posCaret);
18: }
19:
20: function setCaretPosition(elem, caretPos) {
21: if(elem != null) {
22: if(elem.createTextRange) {
23: var range = elem.createTextRange();
24: range.move('character', caretPos);
25: range.select();
26: }
27: else {
28: if(elem.selectionStart) {
29: elem.focus();
30: elem.setSelectionRange(caretPos, caretPos);
31: }
32: else
33: elem.focus();
34: }
35: }
36: }
37:
38: function getPosition(amtFld) {
39: var iCaretPos = 0;
40: if (document.selection) {
41: amtFld.focus ();
42: var oSel = document.selection.createRange ();
43: oSel.moveStart ('character', - amtFld.value.length);
44: iCaretPos = oSel.text.length;
45: }
46: else if (amtFld.selectionStart || amtFld.selectionStart == '0')
47: iCaretPos = amtFld.selectionStart;
48: return(iCaretPos);
49: }
If you have any ideas for improving this code, please feel free to post in in a comment. I’m not a highly experienced or knowledgeable JavaScript coder so there if probably a better way to do this.
January 3rd, 2010 in
Code |
No Comments
View example
Business processes are getting increasingly more complex. To help people perform processes correctly we often resort to training or providing some type of reference material. that’s typically on the corporate intranet. Since the processes are often governed by a complex set of rules a person must read and remember all the different conditions and rules that must be applied to a process. If the process is complicated and not performed very often, the error rate goes up along with the number of support calls.
This article presents a technique that uses a jQuery function to enable process experts to create decision guide or trouble shooter by simply entering information in an HTML table using a visual web page editor. When the page is viewed, the jQuery script reads the table and presents the first question along with the options.
When a person clicks an option/answer, the script finds and presents the next question. This continues until the final answer is found. Each question is displayed below the previous question so a person can see the flow of questions and quickly take another course by skipping back to the first or any previous question.
Here is what the table might look like.
| # |
Question/Heading |
Options/Answer |
| 1 |
What’s wrong with the printer |
- It does not print
- It prints but the text is too light
- The paper is crimpled
- It doesn’t print the whole document
|
| 2 |
Does the printer make any noise? |
- It does not make any noise
- It makes a loud and unusual noise
- It makes the normal noise
|
| 3 |
How light is the print? |
- It’s not readable
- The colors aren’t right
- It’s readable but not as dark as expected
|
| 4 |
|
The printer needs to be replaced. Follow these procedures for requesting a replacement printer. |
The first column contains unique number from 1 to whatever is required. The second column contains the questions or a heading for a final answer. The 3rd column contains a bullet list of options for each question or the final answer. Each of the options in the bullet list has an id attribute that matches the number of the table row that’s presented when the bullet item is clicked. The HTML would be <li id=”3”>The paper is crimpled</li>. Since many visual editors allow authors to add attribute values it would not be necessary for an author to view the HTML code.
View example
To utilize the script you need to:
- Add a link to the jQuery js file
- Copy and paste the function and css from the example page
- Add a table with id=”questions” in the table tag.
- Have an expert enter the questions and answers.
Including an “Don’t know” or “Not sure” option/answer with each question provides an opportunity to present information about how to find an answer to a question.
This electronic performance support system (EPSS) can help you to reduce process errors, improve service, and reduce support calls.
July 15th, 2009 in
Ideas |
4 Comments
When developing content for a corporate Intranet it can be difficult to avoid creating long scrolling content pages. Dividing the content into a number of smaller pages doesn't always work because it can make users pogo-stick through a number of pages to find the information they need.
Sometime a page author will add jump links to the top of the page so users can jump to the section they need. Studies by User Interface Engineering found that users are sometimes confused by these links. The links add to the length of the page and increases visual complexity.
UIE also found that users don't mind scrolling pages as long as they think the information they want is on the page. But in a long scrolling page it is sometime difficult to communicate the page content within the initial viewable area.
More recent eye tracking studies by Software Usability Research Laboratory, Jackob Nielsen, and Google seem to show that users spend most of their time looking at the top portion of the page and not much time scrolling or looking at the content below the fold. All these studies have found that people tend to look at web pages in a F pattern were the left side and top of the page get the most views as illustrated in this heat map image.
According to Jakob Nielsen 79% of web users scan the content of pages rather than read the entire page.
Given this understanding of how people look at web pages, it would seem that an optimal design would be one where:
- the keywords that best communicate the content of the page appear on the left side.
- users did not need to scroll the page.
- the content could be quickly scanned.
This example illustrates a design of a text only content page that could meet this criteria. It uses a jQuery function similar to the one used for the usable FAQ design. The design lets authors enter content in a simple HTML table where one column contains the section titles with keywords that contain the scent of information for the content. The content for each section is then entered in the next column. This lets the author focus on the content and not be bother with layout or entering jump links. Using a hover over to display the content for each section lets a person quickly scan the content to find what they need. Given the content and probably usage, the hover over could easily be replace with a click activation.
May 31st, 2009 in
Design |
No Comments
I have to admit that I'm not a fan of FAQs (Frequently Asked Questions). They are very text heavy and a little overwhelming. It just seems like you have to do a lot of reading to find the question and then jump to the question on a long page. It some times seems like they are not really frequently asked questions, but just the author's way of conveying information without having to think about about organization and presentation.
My opinion of FAQs changed a little when I looked that the FAQ on NetFlix. This is a good way to reduce the visual complexity of FAQ and simplify the navigation.
If you work for a large organization like I do, your Intranet if probably cluttered with FAQs. Using the Netflix style of FAQ would probably be difficult to implement, because web authors are generally not web designers and would probably not have the skill to implement something like this.
There is a way to use this design while enabling your content authors to add and update their FAQs without having to touch code or worry about adding anchors or return to top links. With this design a web author can just enter the questions and answers in a 3 column HTML table. The first column contains the a number, the second the questions, and the third the answers. The jQuery code hides the table when the page is loaded, reads the second column of the table to create the list of questions, and display the answer when a reader clicks or hovers over a question.
This basic example shows only the questions when the page is loaded. A person can then click on the question to view the answer.
This example uses a hover over to display the answers. It's a quicker way to scan all the answers.
If you have a long list of FAQs try this example with a built-in search functions. If a person is truly using the FAQ as they are intended - the person already has a question in mind - they can just enter a keyword and filter the list of questions to the one that might best answer their question. The search uses John Resig's jQuery LiveSearch plugin.
To create your own usable FAQs, just view the source in an example and copy it into your own templates. You can change the CSS styles to fit the color and style of your site or intranet.
May 12th, 2009 in
Design |
No Comments
One of the things I like about Google's Chrome browser is the simplicity of the user interface. It has only a few icons and some of the standard interaction have been combined. As the web grows in complexity there will be a need to continue to develop elegant ways to combine functionality in a way that is intuitive and does not require a lot of learning and remembering.
One of the interesting new ways to access web functionality is the Ubiquity add-on for Firefox. This add-on lets a person navigate and mashup content through an auto suggestion text box interface. For example when a person selects content on a page and presses Ctrl + Spacebar they get a pop-up with some basic suggestions and access to functions that allow them to perform a number of actions like emailing, translating, twittering, searching, or mapping the selected text. Ubiquity also lets you perform unrelated functions like looking up an email address in your list of contacts.
Ubiquity pop-up when text is selected. 
Ubiquity pop-up with selected text and 'T' entered

The fundamental basis of Ubiquity is that you can use language to perform actions. So to twitter something on a page you can select the appropriate text on the page, press the Ubiquity short cut key, and start to type "twitter". Entering "tw" is all it takes.
This sounds great, but one of the possible problems with this design is the "out of sight - out of mind" cognitive barrier. While I installed Ubiquity several months ago I have not used it until I started writing this article. I even had to look up the short cut key to launch it. It just does not occur to me to use it.
Ubiquity can do a lots of things and the functionality will continue to expand, but how will I know what it is I can do? To find a list of my currently installed functions, I can either enter keywords like Twitter, but that's a lot of exploration. The alternative is to type help and then navigate to the list of the functions that I have installed. A user interface that depends on recall rather than recognition tends not be as usable or learnable. Using Ubiquity is like going to a site with only a search box vs a site with links, tabs, and buttons. The search field can work well for finding specific content, but I can't even see what the content might be.
One way to increase Ubiquity's visibility would be to use some of the concepts presented in the previous posts on improving search suggestions. Rather than using a separate pop-up, Ubiquity could be combined with what I call the Search/Command Portal field that combines the use of function key for quick choice selection with a layered menu for enabling people to perform a wide variety of searches and actions.
If a person selects text on a page, the Search/Command Portal field could have some type of animation or state change to indicate it's ability to do something with the selected text. Clicking the field would display a searchable and scrollable list of all the available actions for the selected text. The key difference here is a list of all actions vs a list that just shows a few default actions or only the actions starting with a particular letter. The use of the web is based on a combination of navigating and searching where some actions are based on selecting visible options and others are based on searching for terms that may not be visible. It seems that providing a visible and scrollable list provides user with the opportunity to either search or browse the list to learn the options.
February 27th, 2009 in
Ideas |
No Comments
When a list is long it is nice to have some way to conveniently filter out just those things that most interest you. Kayak.com, the travel site, has found a way to present filtering options that are easy to use and gives you a good sense of what's getting filtered out and what's remaining. The following screen capture shows the how Kayak uses simple checkboxes to quickly filter out airlines and number of stops. To make the airline filters more meaningful Kayak shows the best price each airline offers.
The sliders are particularly useful for showing the flights within a specific time range. This lets you set a time range that is broader than the typical morning, afternoon, or evening choices on other airline travel sites. To kept the scale less clutter, Kayak shows the time range and day below the scale..

Kayak not only excels at providing easy and meaningful ways to directly filter lists, but also excels in methods for summarizing the content in lists. The following screen shot shows their matrix view of car rentals. The presentation make it very easy to compare prices by company and vehicle size.

If you have booked a flight online, you know that the cost of flights varies with the day of the week. Trying to find the best price for a planned trip can sometimes be a matter of changing the dates and waiting for the updated list of flights and prices. Kayak solves the problem by presenting a time chart view of the best and average prices. This lets you quickly find those dates when prices will be best.

Finding the information you want can be very arduous and time consuming. Kayak.com has show a way to give it's user a sense of control of the mountains of information about travel and accommodation choices.
February 16th, 2009 in
Design |
No Comments
In many large corporations the people and processes for maintaining Intranet content are often not part of the Information System group and therefore tend to organize content in way that can best be entered with a WYSIWYG web page editor. Consequently information that could fit into a data table structure sometime tends to be organized into many pages that contain filtered views of the data.
Here is a very simple JavaScript function that can be used to create a user filterable table that also lets a content author enter and maintain data in their familiar WYSIWYG editor. Some possible uses for this include:
- List of forms used in the corporation.
- List of the people or groups that can be contacted about various processes, such as the people who are responsible for the various software systems.
HTML
1: <TABLE border="0" cellpadding="2" cellspacing="0" id="dataTable">
2: <tr><td>Last Name<br>
3: <input name="textfield" type="text" size="10" onKeyUp="filterTbl(this.value,0)">
4: <td>First Name<br>
5: <input name="textfield" type="text" size="10" onKeyUp="filterTbl(this.value,1)">
6: </td>...
The second variable in the function call onKeyUp(this.value, 1) indicates the column number used for the search where zero is the first column.
Javascript
1: <script type="text/javascript">
2: function filterTbl(sval,col) {
3: var stbl = document.getElementById('dataTable');
4: var row = stbl.getElementsByTagName("tr");
5: var rows = row.length;
6: var val = sval.toLowerCase();
7: for (var r = 1; r < rows; r++) {
8: var cell = row[r].getElementsByTagName("td");
9: var lowtxt = cell[col].innerHTML.toLowerCase();
10: if(lowtxt.match(val) == null )
11: row[r].style.display = 'none';
12: else
13: row[r].style.display = 'table-row';
14: }
15: }
16: </script>
This is a very simple function that matches on any text in a table cell containing the entered value. Here is a simple example using a table of 100 US Tax forms.
If you need something more powerful that includes sorting, the TableFilter jQuery plugin might meet your needs.
January 29th, 2009 in
Code |
3 Comments
The previous posts described a design for a single entry point web content search feature. The same design could also be used in a corporate Intranet for searching corporate web applications, such as a personnel directory or list of forms. The following diagram illustrates how a single search field might accommodate both Intranet and Internet searching.
If a corporation has a lot of searches, then the single access search could be layered as described in the previous posts. Depending on how a corporations searchable content was accessed, it may be possible to implement the single access search field in JavaScript or a JavaScript library like jQuery and the jQuery Hotkeys plugin.
January 20th, 2009 in
Ideas |
No Comments