Internet browser “wars” have been going on for a while, at least since the Internet Explorer vs. Netscape war of the 90s, which Microsoft won by a land slide. These days there are more than 2 browsers competing and by my measure it’s not getting any easier to get a web application to look and behave the same on all of the different browsers. One (bad) solution for developers is to target the most popular browser. A quick glance at this graph demonstrates why Firefox and Safari users often can’t view a web page the way the author intended. Of course an even worse solution would be to only make sure your web app works for your favorite non-MS browser, though you might *think* you’re making a statement to Microsoft, you’re really just alienating about 80% of the possible users that may want to use your application.
I recently came across a frustrating example of cross browser incompatibility when rendering the content in a <td> tag of an HTML table. One nice aspect of using JSF and ASP.NET is the myriad of components already available to page authors. And with all of the hype surrounding AJAX and the “new” Web 2.0, AJAX enabled controls/components are freely available to ASP.NET and JSF developers. ASP.NET data grids and JSF data tables are among the most popular components used to display tabular content to a user. I’m getting off topic so let’s get back to the <td> issue. I recently used an ICEFaces DataTable in an application. It is very simple to populate a table with data from my model, add sorting, add paging capabilities, and add columns that are links or other components. What is NOT simple is getting IE, Firefox, and Safari to render the <td> data the same. The specific problem is that I want each cell’s height and width to remain the same no matter how much data is available for a particular cell. So, for example, let’s look at this data table and it’s accompanying paginator:
<ice:form partialSubmit=”true”>
<ice:dataTable id=”userSortedData” rows=”5″ styleClass=”userDataTable”
columnClasses=”firstNameColumn, lastNameColumn”
sortColumn=”#{UserListBean.sortColumnName}”
sortAscending=”#{UserListBean.ascending}” border=”1″
value=”#{UserListBean.userList}” var=”user”>
<!– the unique id of the user, not displayed –>
<ice:column id=”column0″ rendered=”false”>
<ice:outputText value=”#{user.id}” />
</ice:column>
<!– the first name column –>
<ice:column id=”column1″>
<f:facet name=”header”>
<ice:commandSortHeader columnName=”#{UserListBean.firstNameColumnName}” arrow=”true”>
<ice:outputText value=”#{UserListBean.firstNameColumnName}” />
</ice:commandSortHeader>
/f:facet>
<ice:outputText value=”#{user.firstName}” />
</ice:column>
<!– the last name column –>
<ice:column id=”column2″>
<f:facet name=”header”>
<ice:commandSortHeader columnName=”#{UserListBean.lastNameColumnName}” arrow=”true”>
<ice:outputText value=”#{UserListBean.lastNameColumnName}” />
</ice:commandSortHeader>
</f:facet>
<ice:outputText value=”#{user.lastName}”></ice:outputText>
</ice:column>
<!– Paginator with page controls –>
<ice:dataPaginator id=”dataScrollUsers” for=”userSortedData”
paginator=”true” fastStep=”3″ paginatorMaxPages=”4″>
<f:facet name=”first”>
<ice:graphicImage
url=”./xmlhttp/css/xp/css-images/arrow-first.gif”
style=”border:none;” title=”First Page” />
</f:facet>
<f:facet name=”last”>
<ice:graphicImage
url=”./xmlhttp/css/xp/css-images/arrow-last.gif”
style=”border:none;” title=”Last Page” />
</f:facet>
<f:facet name=”previous”>
<ice:graphicImage
url=”./xmlhttp/css/xp/css-images/arrow-previous.gif”
style=”border:none;” title=”Previous Page” />
</f:facet>
<f:facet name=”next”>
<ice:graphicImage
url=”./xmlhttp/css/xp/css-images/arrow-next.gif”
style=”border:none;” title=”Next Page” />
</f:facet>
<f:facet name=”fastforward”>
<ice:graphicImage url=”./xmlhttp/css/xp/css-images/arrow-ff.gif”
style=”border:none;” title=”Fast Forward” />
</f:facet>
<f:facet name=”fastrewind”>
<ice:graphicImage url=”./xmlhttp/css/xp/css-images/arrow-fr.gif”
style=”border:none;” title=”Fast Backwards” />
</f:facet>
</ice:dataPaginator>
</ice:form>
Notice the styleClass and the columnClass attribute, these are used to associate CSS style sheet classes with the rendered table and it’s columns. In this example we want the data in the cells of the firstName and lastName columns to be of a specific height and width, no matter how large a first or a last name gets. With IE this is relatively easy, we just add a text-overflow:ellipsis item to a column’s style sheet class, like this:
.firstNameColumn {
height: 28px;
width: 90px;
text-overflow: ellipsis;
}
Now, supposing we want Firefox users to get the same effect, we can use the following hack. First, add table-layout:fixed to the table’s class, then add overflow:hidden and white-space:nowrap the the column’s class:
.userDataTable {
table-layout: fixed;
}
.firstNameColumn {
height: 28px;
width: 90px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
It took a while to find the hack for Firefox, but much to my dissatisfaction Safari still displays the <td> width depending on the data. So I’ll be spending more time trying to get this to work in Safari, and then I have to test Opera. So essentially browser compatibility issues for a simple data table are costing more time than the model layer, data access layer, and paging and sorting functionality combined.
Supposedly the reason for this issue is that the W3C standards for HTML don’t dictate the layout of data with a <td> tag. My question is if the W3C is so great, why does conforming to the standard make developers’ lives harder? It would be much easier for me to just use the IE proprietary functionality to lay a site out and not worry about a “standard” that 80-90% of internet browsers (i.e, IE) ignore anyways. And if Safari and Firefox can’t adhere to the standard in the same manner, what’s the point of having a standard? This is one reason why I support GWT and other similar frameworks, they aim to deal with the browser compatibility issues while web developers can focus on the functionality of their sites, not waste time researching and finding ways to get 4 different browser engines to render content the same!