Monday, January 30, 2012

Refreshing a Dojo DataGrid

Here's a little trick for refreshing the contents of a Dojo DataGrid to the currently selected position. You may wish to do this if your data source is being updated by something else, and you need to force a refresh.

dojo.require("dojo.aspect");
var grid = dijit.byId("myGrid");
grid.store.close();
var handle = dojo.aspect.after(grid, "_onFetchComplete", function() {
    handle.remove();
    this.scrollToRow(this.selection.selectedIndex);
});
grid.sort();

This simplified example closes the store and calls sort() to force a refresh of the data.

In the normal course of processing, Dojo tries to get you back to where you were, but due to the clearing of the data, the scroller height is reduced, and when Dojo tries to set the scrollTop property of the grid div, it remains as it's reset value of 0.  Therefore, we use aspect.after() to set the row after the fetch from the sort has completed.

We don't need this happening every time data is fetched for the grid, so we record the aspect handle, and force the aspect function to remove itself from the chain, once it has been called.  Since the scrollToRow call is likely to fetch more data, we remove the aspect handle before calling it, so we don't have the aspect function called twice.

This was done using Dojo 1.7.1.

Friday, January 27, 2012

Fixed layouts for tables

I'm still fiddling around with Dojo grids. It's the Enhanced Grid in 1.7.1 at the moment, but most of the CSS comes from DataGrid anyway.

I came cross an interesting difference between Chrome, Firefox and IE with regards to how Dojo implement their grids.

To get grids to render quickly, Dojo like you to specify the width columns in the grid structure. This means they don't have to run any tricky rendering calculations, and their arrangement of tables nested in div tags works out nice and quick.

However, Chrome and Firefox have different ideas on how to render what Dojo has done to make use of this quick rendering.

Dojo does the following: Makes each table have a table-layout of fixed, give the table a width of 0 and explicitly specifies the width of each column in the TH and TD tags. That width is the width you specified in the grid structure.

Based on this, I expect the widths I've supplied to be the total widths of the columns. I also use the sum of these widths, plus a bit more for the vertical scroll bar for the node holding the grid.

Chrome does the following: Pretty much as expected, from a "setting up Dojo" point of view. Each column is as wide as I configured. However, from the CSS point of view, it's a bit strange. Dojo puts padding in the cells (5px each side) and there's a border as well (1px all around). So when you look at the Metrics tab in the developer tools, the actual width displayed is less than the what you put it. The box-sizing of the TH and TD elements is content-box. It looks like Chrome as reverse engineered the supplied width to fit with the content-box model. My rationalisation is that Chrome forces the TH and TD elements to be box-sizing: border-box, given a table-layout : fixed, but instead of just saying that, it changes the width to suit box-sizing: content-box. Well, it all looks good in Chrome, so what do I care?

A lot, because my clients aren't using Chrome. They're using Firefox and IE.

Firefox does the following: Completely ignores the width on the table (which is 0), in favour of the widths on the column headings. And then proceeds to render them using box-sizing: content-box. This means all the "exact" column widths I asked for are now increased by the padding and the borders in the column headings.

The specification for table-layout : fixed at W3C is particularly vague when it comes to determining what part column heading widths, and their paddings, should play when determining the total width of the table.

On one hand, Chrome seem to have taken their lead from the second paragraph, and have used the block width algorithm, to determine that a supplied width should be applied as though there was a box-sizing : border-box applied. Even then, that's not quite right, because border-box doesn't include margins, where as the block width does. Lucky for us, TH and TD elements lack a margin to speak of.

On the other hand, Firefox have taken their lead from the first rule of the fixed table layout algorithm, and just use the width property as the width according to the box-sizing: content-box model. And why wouldn't they: it's what it says on the tin.

The work around, to get consistent behaviour across both browsers, is to force the column headings to have a box-sizing: border-box.

Since I'm using Compass/SASS, I can create a mixin to include at the top level of any Dojo grid to fix the problem.

@import "compass/css3";

@mixin dojo-grid {

    .dojoGridRowTable > tbody > tr > th,
    .dojoGridRowTable > tbody > tr > td {
        @include box-sizing(border-box);
    }
}


Luckily, IE9 plays along as well. I'm not sure, and I care less about IE8. Google can get regular updates out for Chrome, regardless of the platform. Firefox is doing it's best to follow suit. I'm inclined not to care much at all for IE if the only way for HTML and CSS bugs fixes to be released is with the next major version of the product (or platform it was designed for).