MSIE6 CSS issue ‘dotted’ behaves like ‘dashed’

Another fix in MSIE7 (broken before), ‘dotted’ is now implemented, in MSIE6 dotted had the same visual representation as ‘dashed’.

This explains why you might expect to see a line of “……” that appear to be “——“, even when you’re absolutely positive that you’re CSS is correct!
CSS:

border:1px dotted #fff;

HTML Example:

<html>
<head>
<title>dotted-dashed Example</title>
<style type=”text/css”>
fieldset {background-color:#fcfcfc;
width:95%;
padding:15px 10px 0 10px;margin:0 0 20px 0;
border:1px solid #999;
border-top-width:2px;
overflow:hidden;}
fieldset div.buttons {clear:both; padding-top:10px;padding-bottom:10px;margin:3px 0 0 0;border-top:1px dotted #b5b5b5;text-align:left;}
</style>
</head>
<body>
<fieldset>
Some form fields go here…
<div class=”buttons”>
Some buttons go here…
</div>
</fieldset>
</body>
</html>

Cheers!

Clientside Session Timeout’s

There comes a time in web application development that you need to ‘timeout’ idle users. This comes in a variety of ways, here’s a few common reasons that you may desire this activity.

  • Security – you don’t want to leave sensitive data on a users screen when they’ve gone to lunch or left for the day.
  • Server Resources – persisting/keeping an active ‘session’ available on the server takes resources (the exact type varies, but this is usually database, memory or file resources)
  • Server ‘enforced’ session timeout’s and the potential errors and lost data experienced by the users in that circumstance.

My personal approach to this has evolved over time, here’s a brief synopsis:

  1. Use standard server-side session timeout, often leading to a bad user experience when they loose data on a form submit.
  2. Use META REFRESH…where timeout is in seconds, in this example it’s 60 seconds (1 minute).
    <meta http-equiv="refresh" content="60;url=http://www.giantgeek.com/" />
  3. Use javascript 'timeout' (problem is that this is not 'measureable')
    
    <script type="text/javascript">
    setTimeout("javascript:myTimeout();",minutes*60000); // code minutes
    </script>
  4. Use javascript countdown timer and custom code event.

<html>
<head>
<title>Timeout example</title>
<script type=”text/javascript”>
var build=’testing’;
var timerID = 0;
var loadTime = null;
var stopTime = null;
function xload(){
loadTime=grvMillis();
grvWindowStatus(build);
grvSetTimeout();
}
function xclose(){
grvWindowStatus(”);
}
function grvMillis(){
return new Date().getTime();
}
// Start timer
function grvTimerUpdate(){
timerID = grvTimerClear(timerID);
if(loadTime == null){
loadTime=grvMillis();// Start Time
}
// Calculate Current Time in seconds
var timeNow = grvMillis();

var think = calcMinSec( calcTimeDiff(timeNow,loadTime) );
var remain = calcMinSec( calcTimeDiff(stopTime,timeNow) );
grvWindowStatus(build + ” ” + think + ” ” + remain );
timerID = setTimeout(“grvTimerUpdate()”,1000);
}
function calcMinSec(diff){
var mm = removeDecimal(diff/60);
var ss = zeroPad(removeDecimal(diff-(mm*60)),2);
return (mm + “:” + ss);
}
function calcTimeDiff(tmpStart,tmpStop){
var diff = (tmpStart – tmpStop)/1000;
return diff;
}
function removeDecimal(val){
var rc=””;
val = val + “”;
if(val!=””){
var pos = val.indexOf(“.”);
if(pos > -1){
rc=val.substr(0,val.indexOf(“.”));
} else {
rc=val;
}
}
return rc;
}
function zeroPad(x,sz){
x = x + “”;
while(x.length < sz){
x = “0” + x;
}
return x;
}
function grvTimerClear(x){ // this clears a timer from the queue
if(x){
clearTimeout(x);
x = 0;
}
return x;
}
function grvSetTimeout(){
var min=45; xID=grvTimeout(“javascript:grvTimeoutUSER()”,min); // EXAMPLE: this could be conditional!
stopTime = grvCalculateTimeout(min);
grvTimerUpdate();
}
function grvCalculateTimeout(mins){
var timeNow = grvMillis();
var exp = timeNow + (mins*60*1000);
var timeExp = new Date(exp).getTime();
return timeExp;
}
function grvTimeout(x,minutes){ // this sets a timer(request) in a queue
return setTimeout(x,minutes*60000);
}
function grvTimeoutUSER(){
alert(‘Session Inactivity Timeout [USER]’);
// DO WHAT YOU NEED TO HERE!}
function grvWindowStatus(txt){
window.defaultStatus=txt;
}
</script>
</head>
<body onunload=”xclose();” onload=”xload();”>
</body>
</html>

Another benefit of this last solution is that you also have access to the user “Think Time” and can therefore measure how long the user spends on a given page.

Cheers!

Clientside sorting of HTML TABLE in JavaScript

To save network bandwidth and server resources, it is often beneficial to to sorting of tabular data on the client. Here’s a workable solution that I’ve implemented several times.

Additionally, the need to make the solution ‘accessible’ to screen-reader technology and be backward compatible for users without JavaScript often become challenging.

There are some small “quirks” that you should be aware of…

  • The ‘sortTable’ method uses the ID of the ‘TBODY’. Working to remove this requirement through better use of the DOM.
  • Will likely rework this to use Prototype framework which should result in smaller code.
  • Future enhancement will add a class to the header indicating the sort order of the column(s).
  • Creating the ‘SPAN’ for dates to be sorted is best handled by a taglib.
  • Large TABLE’s can take a significant amount of time to sort on a client, so it’s sometimes better to use a server side solution. Developers should use their experience to make this judgment call.

Example code (XHTML logic removed as usual for brevity):

<html>
<head>
<title>Client Side TABLE sorting</title>
<style type=”text/css”>
/* SCROLL */
div.scroll {width:100%;overflow:scroll;}
html>body div.scroll {width:100%;overflow:scroll} /* fixes IE6 hack */
/*** TABULAR ***/
tr.even {background-color:#eee;}
th.first, td.first {border-width:0;}
th.memo {text-align:left;padding:0;}
td.sorted {background-color: #f0f0f0;}
th.sorted {background-color: #99f;}
tr.even td.sorted { background-color: #d0d0d0; }
table.sorted tr.error { background-color:red; }
table.sorted tr.scroll th { background-color:#99f; text-align:left;}
table.sorted tr.scroll th a.sorted { color:#fff; text-decoration:none;}
table.sorted tr.scroll th a.sorted:hover { color:#fff; text-decoration:underline;}
</style>
<script type=”text/javascript”>
//—————————————————————————–
// sortTable(id, col, rev,xcase)
//
// id – ID of the TABLE, TBODY, THEAD or TFOOT element to be sorted.
// col – Index of the column to sort, 0 = first column, 1 = second column, etc.
// rev – If true, the column is sorted in reverse (descending) order initially.
// xcase – makes sort NOT case sensitive.
//
// The following is an example of jsp code for setting up the reformatted copy of a date
// field to allow sorting by date:
//
// <fmt:formatDate var=”varSortDate” value=”${searchResult.dateOfBirth}” pattern=”${sortableDateTimePattern}” />
// <span title=”<c:out value=”${varSortDate}” />”><fmt:formatDate value=”${searchResult.dateOfBirth}” pattern=”${dateFormatPattern}” /></span> | etc.
//
// The above code creates html code that contains, for example, a line like the following:
//
// <span title=”19640101000000″>01/01/1964</span>
//
// This sort routing concatenates the title element and the text node
// content to sort on the following string:
//
// 1964010100000001/01/1964
//
// This effective ignores the date containing slashes and used the yyyyMMdd etc. value.
// Fields that are not dates should not use the span element and title attribute, unless
// it is desired to sort on something other than the text node content.
//—————————————————————————–
function sortTable(id, col, rev, xcase) {
// Get the table or table section to sort.
var tblEl = xgetHelper(id);
if(tblEl != null){
// The first time this function is called for a given table, set up an array of reverse sort flags.
if (tblEl.reverseSort == null) {
tblEl.reverseSort = new Array();
// Also, assume the column zero is initially sorted.
tblEl.lastColumn = 0; // was 1
}

// If this column has not been sorted before, set the initial sort direction.
if (tblEl.reverseSort[col] == null)
tblEl.reverseSort[col] = rev;

// If this column was the last one sorted, reverse its sort direction.
if (col == tblEl.lastColumn)
tblEl.reverseSort[col] = !tblEl.reverseSort[col];

// Remember this column as the last one sorted.
tblEl.lastColumn = col;
// Set the table display style to “none” – necessary for Netscape 6 browsers.
var oldDsply = tblEl.style.display;
tblEl.style.display = “none”;
// Sort the rows based on the content of the specified column using a selection sort.

var tmpEl;
var i, j;
var minVal, minIdx;
var testVal;
var cmp;
for (i = 0; i < tblEl.rows.length – 1; i++) {

// Assume the current row has the minimum value.
minIdx = i;
minVal = getTextValue(tblEl.rows[i].cells[col], xcase);

// Search the rows that follow the current one for a smaller value.
for (j = i + 1; j < tblEl.rows.length; j++) {
testVal = getTextValue(tblEl.rows[j].cells[col], xcase);
cmp = compareValues(minVal, testVal);
// Negate the comparison result if the reverse sort flag is set.
if (tblEl.reverseSort[col])
cmp = -cmp;
// If this row has a smaller value than the current minimum, remember its
// position and update the current minimum value.
if (cmp > 0) {
minIdx = j;
minVal = testVal;
}
}

// By now, we have the row with the smallest value. Remove it from the
// table and insert it before the current row.
if (minIdx > i) {
tmpEl = tblEl.removeChild(tblEl.rows[minIdx]);
tblEl.insertBefore(tmpEl, tblEl.rows[i]);
}
}

// Make it look pretty.
makePretty(tblEl, col);

// Restore the table’s display style.
tblEl.style.display = oldDsply;
}

return false;
}

//—————————————————————————–
// Functions to get and compare values during a sort.
//—————————————————————————–

// This code is necessary for browsers that don’t reflect the DOM constants
// (like IE).
if (document.ELEMENT_NODE == null) {
document.ELEMENT_NODE = 1;
document.TEXT_NODE = 3;
}

function getTextValue(el, xcase){
var i;
var s;
var spanTitleValue;

// Find and concatenate the values of all text nodes contained within the element.
s = “”;

for (i = 0; i < el.childNodes.length; i++) {
if (el.childNodes[i].nodeType == 1) {
if (el.childNodes[i].nodeName != null) {
if (el.childNodes[i].nodeName == “SPAN”) {
spanTitleValue = el.childNodes[i].getAttribute(“Title”);
s += spanTitleValue;
}
}
else {
}
// Use recursion to get text within sub-elements.
s += getTextValue(el.childNodes[i]);
}
else if (el.childNodes[i].nodeType == document.TEXT_NODE) {
s += el.childNodes[i].nodeValue;
}
else {
// Gets here when element is empty! <span title=””></span>
//alert(‘Error — Not element or text node’);
}
}
return normalizeString(s, xcase);
}

function compareValues(v1, v2) {

var f1, f2;
// If the values are numeric, convert them to floats.

f1 = parseFloat(v1);
f2 = parseFloat(v2);
if (!isNaN(f1) && !isNaN(f2)) {
v1 = f1;
v2 = f2;
}

// Compare the two values.
if (v1 == v2)
return 0;
if (v1 > v2)
return 1
return -1;
}

// Regular expressions for normalizing white space.
var whtSpEnds = new RegExp(“^\\s*|\\s*$”, “g”);
var whtSpMult = new RegExp(“\\s\\s+”, “g”);

function normalizeString(s, xcase) {

s = s.replace(whtSpMult, ” “); // Collapse any multiple whites space.
s = s.replace(whtSpEnds, “”); // Remove leading or trailing white space.

var rc = s;
if(xcase == true) {
rc = s.toUpperCase();
}
return rc;
}

//—————————————————————————–
// Functions to update the table appearance after a sort.
//—————————————————————————–

// Style class names.
var rowClsNm = “even”;
var colClsNm = “sorted”;

// Regular expressions for setting class names.
var rowTest = new RegExp(rowClsNm, “gi”);
var colTest = new RegExp(colClsNm, “gi”);

function makePretty(tblEl, col) {
var i, j;
var rowEl, cellEl;

// Set style classes on each row to alternate their appearance.
for (i = 0; i < tblEl.rows.length; i++) {
rowEl = tblEl.rows[i];
rowEl.className = rowEl.className.replace(rowTest, “”);
if (i % 2 != 0)
rowEl.className += ” ” + rowClsNm;
rowEl.className = normalizeString(rowEl.className);
// Set style classes on each column (other than the name column) to
// highlight the one that was sorted.
for (j = 0; j < tblEl.rows[i].cells.length; j++) { /* was j=2 */
cellEl = rowEl.cells[j];
cellEl.className = cellEl.className.replace(colTest, “”);
if (j == col)
cellEl.className += ” ” + colClsNm;
cellEl.className = normalizeString(cellEl.className);
}
}

// Find the table header and highlight the column that was sorted.
var el = tblEl.parentNode.tHead;
rowEl = el.rows[el.rows.length – 1];
// Set style classes for each column as above.
for (i = 2; i < rowEl.cells.length; i++) {
cellEl = rowEl.cells[i];
cellEl.className = cellEl.className.replace(colTest, “”);
// Highlight the header of the sorted column.
if (i == col)
cellEl.className += ” ” + colClsNm;
cellEl.className = normalizeString(cellEl.className);
}
}
function xgetHelper(id){
var obj = null;
try {
obj = document.getElementById(id);
} catch(z) {
//var dummy=alert(“Error:” + z);
}
return obj;
}
</script>
</head>
<body>
<fieldset>
<div id=”ex_div” class=”scroll”>
<table summary=”” id=”names” class=”sorted” cellspacing=”0″>
<colgroup>
<col style=”first labels” />
<col style=”form_fields” />
</colgroup>
<thead>
<tr class=”scroll”>
<th scope=”col” id=”ex_0″><a href=”javascript:void(0);” class=”sorted” onclick=”return sortTable(‘ex_sort’,0,true,true);”>Alpha</a></th>
<th scope=”col” id=”ex_1″><a href=”javascript:void(0);” class=”sorted” onclick=”return sortTable(‘ex_sort’,1,true,true);”>Date</a></th>
<th scope=”col” id=”ex_2″><a href=”javascript:void(0);” class=”sorted” onclick=”return sortTable(‘ex_sort’,2,true,true);”>Url</a></th>
</tr>
</thead>
<tbody class=”scroll” id=”ex_sort”>
<tr class=”even”>
<td headers=”ex_0″>Alpha</td>
<td headers=”ex_1″><span title=”20070701″>July 2, 2007</span></td>
<td headers=”ex_2″><a href=”javascript:void(0);” onclick=”alert(‘view.php?userid=6’);”>5</a></td>
</tr>
<tr class=”odd”>
<td headers=”ex_0″>Bravo</td>
<td headers=”ex_1″><span title=”20050903″>Sept 3, 2005</span></td>
<td headers=”ex_2″><a href=”javascript:void(0);” onclick=”alert(‘view.php?userid=8’);”>4</a></td>
</tr>
<tr class=”even”>
<td headers=”ex_0″>Charlie</td>
<td headers=”ex_1″><span title=”19700709″>July 9, 1970</span></td>
<td headers=”ex_2″><a href=”javascript:void(0);” onclick=”alert(‘view.php?userid=4’);”>2</a></td>
</tr>
<tr class=”odd”>
<td headers=”ex_0″>Delta</td>
<td headers=”ex_1″><span title=”20001213″>Dec. 13, 2000</span></td>
<td headers=”ex_2″><a href=”javascript:void(0);” onclick=”alert(‘view.php?userid=5’);”>3</a></td>
</tr>
<tr class=”even”>
<td headers=”ex_0″>Echo</td>
<td headers=”ex_1″><span title=”20010911″>Sept 11, 2001</span></td>
<td headers=”ex_2″><a href=”javascript:void(0);” onclick=”alert(‘view.php?userid=2’);”>1</a></td>
</tr>
</tbody>
</table>
</div>
</fieldset>
</body>
</html>

Cheers!

Adding Support for ‘disabled’ OPTION tags in MSIE

This is a very annoying bug/oversight in MSIE (including the recently released MSIE7!).

For some reason, Microsoft didn’t implement the ‘disabled’ attribute on <option> tags.
All other modern (even the old Netscape 4.x) browsers support this, why would they not do the same.
This is probably for the same reason that all versions of MSIE (prior to MSIE7) left the rendering of the SELECT tag to the operating system itself, above the browser HTML.
Thankfully, you can still access the attribute on the DOM element with javascript!

My solution has evolved over time, here’s the current release code.

To emulate this behavior in MSIE, you’ve got several different challenges to overcome.

  1. You must ‘persist’ the current value of the SELECT so that you can ‘restore’ it when the user chooses a disabled field, I do this during the onload event.
  2. When the ‘onchange’ event for the SELECT tag is invoked, the currently selected OPTION’s attributes must be read and checked.
  3. If the selected OPTION is ‘disabled’, then the previous value must be restored.
  • As this solution only stores one value per SELECT, the ‘multiple’ SELECT is not currently supported.
  • In the future I’ll probably do some ‘event injection’ so that the HTML itself is cleaner. Problem is that in more complex solutions like ‘dependent dropdowns’ this becomes tricky.
  • The [if IE] ‘comment’ is critical as it is conditional logic supported only in MSIE and simplifies what was previously done via various ‘browser-sniffing’ tricks.
  • The example code in this example excludes the CDATA escapes and several tags required for valid XHTML for brevity.
  • FYI, the example also contains the MSIE background-image cache fix discussed in a previous post.

<!DOCTYPE >
<html>
<head>
<script type=”text/javascript”>
var isMSIE=false;
<!–[if IE]>
<script type=”text/javascript”>
isMSIE=true;
</script>
<script src=”/js/grv-msie-hacks.js” type=”text/javascript”></script>
<![endif]–>

<script type=”text/javascript”>
function xload(){
if(isMSIE){
grvMsieInitHacks();
}
}
function xchange(obj){
// note: javascript emulation of <option disabled…> (for MSIE)
if(isMSIE){
grvMsieSelectFix_restore(obj);
}
}
</script>
</head>

<body onload=”xload();”>

<form action=”#” method=”GET”>
<select name=”s” id=”s” size=”1″ onchange=”xchange(this);”>
<option value=”n1″>Normal1</option>
<option value=”di” disabled=”disabled”>Dis</option>
<option value=”n1″>Normal2</option>
</select>
</form>
</body>
</html>

JavaScript file (grv-msie-hacks.js):

/*
* Code library to add several ‘broken’ features in MSIE 6 and 7
* @version $Id: $
*/
function grvMsieInitHacks(){
grvMsieCacheFix();
grvMsieSelectFix_init()
}
function grvMsieCacheFix(){
try{
document.execCommand(“BackgroundImageCache”,false,true);
}catch(e)
{}
}
/* Added new functions to support <option disabled…> emulation
*
* First part, necessary for <body onLoad…>
* builds array of all initial selections for <select>s on page (for LT or EQ MSIE7)
* WAS: disabledOptionEmulation();
*/
function grvMsieSelectFix_init() {
if (document.getElementsByTagName) {
var allSelects = document.getElementsByTagName(“select”); // build array of all <select> tags
if (allSelects.length > 0) { // if array has values…
window.allSelectsCurrentIndex = new Array(); // new array to hold initial selections
for (var i=0, individualSelect; individualSelect = allSelects[i]; i++) { // crawl through all <select> tags
individualSelect.onfocus = function(){ window.allSelectsCurrentIndex[this.id] = this.selectedIndex; } // fill array with selectedIndex values
}
}
}
}

/* companion code for grvMsieSelectFix_init()
* resets <option> selection if disabled to last good selection (for LT or EQ MSIE7)
* WAS: restoreSelection(inOptionChoice)
*/
function grvMsieSelectFix_restore(inOptionChoice) {
if (inOptionChoice.options[inOptionChoice.selectedIndex].disabled){
// if new choice is disabled…
inOptionChoice.selectedIndex = window.allSelectsCurrentIndex[inOptionChoice.id]; // deny selection, revert back to last ‘known good’ choice (typically, the initial selection at page load)
} else { // if new choice isn’t disabled…
window.allSelectsCurrentIndex[inOptionChoice.id] = inOptionChoice.selectedIndex; // update array so last ‘known good’ choice is now the latest user selection
}
}

Hopefully, Microsoft will get around to fixing this correctly some day… maybe for MSIE8!

MSIE Conditional code

This is not so much as a MSIE bug, but rather a non-standard “feature” (added in MSIE 5.5) that is often helpful when constructing websites that you try to build with valid XHTML and CSS. As all non-Microsoft browsers will see these as simple
HTML markup comments, it gives you the flexibility to deal with the inconsistencies and quicks in the various versions of MSIE. This is particularly important for the numerous changes introduced with MSIE7.

I’ve tested this with <style />, <script /> and <meta /> tags, but wouldn’t doubt that it works anywhere (but don’t see any typical situations for that!)

Some simple instructions:

  1. Start with an HTML comment
  2. Add the opening brackets, so it resembles <!–[if …]>
  3. Add the closing brackets too… <![endif]–> (don’t forget that extra less than and bang/exclamation point!)
  4. Add the versioning info, this should be pretty obvious, but i’ll list the most common ones.
    IE 7 = MSIE7
    IE 6 = MSIE6
    IE 5.5000 = MSIE 5.5
  5. Some modifiers, (should you want a range of versions):
    lt = Less Than
    gt = Greater Than
  6. Make sure that the body content of the tag (which now resembles an HTML comment) is coded.
  7. That’s it, now be sure to test for expected behaviors!

Some Examples:

<!–[if IE 6]>
<style type=”text/css”>
/* <![CDATA[ */
html {
overflow-y: scroll;
}
/* ]]> */
</style>
<![endif]–><!–[if lt IE 5.5000]><style type=”text/css”>@import “/css/IE50Fixes.css”;</style><![endif]–>
<!–[if IE 5.5000]><style type=”text/css”>@import “/css/IE55Fixes.css”;</style><![endif]–>
<!–[if IE 6]><style type=”text/css”>@import “/css/IE60Fixes.css”;</style><![endif]–>
<!–[if IE 7]><style type=”text/css”>@import “/css/IE70Fixes.css”;</style><![endif]–>
<!–[if lt IE 7]><script type=”text/javascript” src=”/js/IEFixes.js”></script>
<meta http-equiv=”imagetoolbar” content=”no” /><![endif]–>

Cheers!

TEXTAREA maxlength – or lack of!

I’m not sure why this was previously overlooked in HTML4/XHTML1, but it’s been a real pain for developers for years. The ‘rows’ and ‘cols’ attributes are useless (like ‘size’ is in the INPUT tag) as they are based on display size of fixed-width fonts like Courier and not the actual input limitations. INPUT has always supported a ‘maxlength’ attribute for this purpose.

Good news, this is part of WebForms2 and HTML5 (proposal) plans to add it!

A decent fix that I currently use… optimally you would use a common ‘maxsize’ function.

[textarea name=”junit” id=”junit” onkeyup=”maxsize(this,100);”][/textarea]
[script type=”text/javascript”]
function maxsize(obj,mx){
if(obj.value.length>mx)
{
obj.value=obj.value.substring(0,mx); }
}
[/script]

WARNING:

You should ALWAYS perform server-side validation of the length too, otherwise you leave the door open for someone to hack your form and submit longer data.

References:

Cheers!

Custom JavaScript error notification

Debugging JavaScript errors is a time-consuming effort requiring keen eyes and a sharp mind.

MSIE typically only gives a cryptic ‘Object Expected’ error message and little more (even with the Microsoft Script Debugger installed!).

Some tools like FireBug and the Venkman debugger (both for Mozilla/Firefox) help in this matter, but often it helps to have an alert when an issue occurs.

Here’s a simple implementation that I’ve found useful…

[script type=”text/javascript”]
window.onerror=myErrorHandler;

function myErrorHandler(msg,url,l){
var txt=”There was an error on this page.\n”;
txt+=”Error: ” + msg + “\n”;
txt+=”URL: ” + url + “\n”;
txt+=”Line: ” + l + “\n\n”;
txt+=”Click OK to continue.\n\n”;
alert(txt); return true; }
[/script]

REFERENCES:

That’s it….

MSIE6 background-image caching (or lack of it…) and flickering

This has been an annoyance of this (IMHO very buggy) browser since it was first beta tested. Earlier (5.x) and newer (7.x) versions do not exhibit this problem.
For some reason Microsoft developers broke the caching mechanism for background images, particularly when defined in CSS. This makes for slow screen painting as well as wasted network traffic as each occurrence of the image becomes a new HTTP request to the webserver. This also causes a notable delay in those images painting on the screen and ‘flicker’ when the images are used in CSS rollover effects. Since the image obviously isn’t changed it results in many ‘HTTP 304 Not Modified‘ entries in the server logs.

Fixes…

1. You CAN/SHOULD set the Expiry for the images, however this can be problematic. Since I typically run Apache HTTPD, those instructions follow:

a) Set an explicit expiry time based on MIME types in the http.conf file.

[instructions in separate post]

b) Enable .htaccess for the server and allow its usage in individual folders on the server.

[instructions in separate post]

c) Use client-side technologies to hack around the problem…. you can use many CSS tricks, but I’ve found that JavaScript is the easiest (most compatible) method.

Add the following to a method executed in the onload event of the page…

<script type=”text/javascript”>
try {
document.execCommand(‘BackgroundImageCache’, false, true);
} catch(e) {}
</script>
NOTE: MSIE will execute the Javascript, Mozilla and other browsers will throw an exception and wind up in the catch block… which ignores the problem.

UPDATE:
With the use of conditional comments, this can be added to an MSIE specific JS file, or even better an MSIE specific CSS file containing the following:


html {
filter: expression(document.execCommand("BackgroundImageCache", false, true));
}

REFERENCES:

CSS2 Colors

CSS Level 2 defines several additional color names that represent the special system-specific colors used by the operating system. These names should look look very familiar to prior developers of “Fat Client” software, primarily VisualBASIC and PowerBuilder.

NOTE: These work with MSIE 5.0+ and Mozilla/Netscape 5.0+, prior browsers “try” to interpret these colors as hexadecimal RGB (Red/Green/Blue) equivalents, resulting in a huge mess.

The colors shown below will be mapped from your current operating system settings and as such MAY vary from computer to computer!

CSS 2 Color Name Example (Using Background-color) Description
ActiveBorder Active window border.
ActiveCaption Active window caption.
AppWorkspace Background color of multiple document interface.
Background Desktop background.
ButtonFace Face color for three-dimensional display elements.
ButtonHighlight Dark shadow for three-dimensional display elements (for edges facing away from the light source).
ButtonShadow Shadow color for three-dimensional display elements.
ButtonText Text on push buttons.
CaptionText Text in caption, size box, and scrollbar arrow box.
GrayText Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.
Highlight Item(s) selected in a control.
HighlightText Text of item(s) selected in a control.
InactiveBorder Inactive window border.
InactiveCaption Inactive window caption.
InactiveCaptionText Color of text in an inactive caption.
InfoBackground Background color for tooltip controls.
InfoText Text color for tooltip controls.
Menu Menu background.
MenuText Text in menus.
Scrollbar Scroll bar gray area.
ThreeDDarkShadow Dark shadow for three-dimensional display elements.
ThreeDFace Face color for three-dimensional display elements.
ThreeDHighlight Highlight color for three-dimensional display elements.
ThreeDLightShadow Light color for three-dimensional display elements (for edges facing the light source).
ThreeDShadow Dark shadow for three-dimensional display elements.
Window Window background.
WindowFrame Window frame.
WindowText Text in windows.

Cheers!

Auto focus ‘first visible’ form field on page…

Occassionally there comes a need to set the focus within a web page to the first ‘visible’ form field, here’s the most convenient I’ve found thus far…

Implementation:
1. Add the following Javascript to the HEAD section of your page.

function formfocus() {
if(document.forms.length > 0)
{
var formElements = ["text", "checkbox", "radio", "select-one", "select-multiple", "textarea"];
var form = document.forms[document.forms.length-1];
for (var j = 0; j < form.elements.length; j++)
{
var field = form.elements[j];
for(var x = 0; x < formElements.length; x++)
{
if (field.getAttribute("type") == formElements[x])
{
field.focus();
return false;
}
}
}
}
}

2. Add the function call to the BODY tag…

<body onload="formfocus();">

That’s it! Enjoy!