Styling ExtJS Grid Cells

Styling grid cells is a common topic for help requests on the Sencha forums. There are a number of techniques available depending on exactly what styling should be applied where. This article introduces the most widely-used of these techniques along with some of the theory behind how they work.

Default Grid Styling

Let's start by taking a look at the default styling for a grid using the blue theme:

Some key styling features to note include:

  • Rows alternate between white and light grey. It's intended to be subtle but it varies a lot between monitors so you may find it difficult to see.
  • There is a light grey dividing line between each row. There isn't an equivalent vertical line between columns.
  • Moving the mouse cursor over the rows adds a grey highlight. The top and bottom border of the row also darkens when highlighted.
  • Selecting a row adds a light blue background and a dotted blue border.

For reference, the code for this grid looks like this:

        Ext.create('Ext.grid.Panel', {
            height: 230,
            width: 300,
            columns: [
                {dataIndex: 'name', flex: 1, text: 'Name'},
                {dataIndex: 'age', flex: 1, text: 'Age'},
                {dataIndex: 'sex', flex: 1, text: 'Sex'}
            ],
            store: {
                fields: ['name', 'age', 'sex'],
                ...
            }
        });
    

The Grid Markup

Most grid styling is done using CSS. To understand the CSS it's necessary to be familiar with the HTML markup of a grid. Below is a simplified version of that markup. At its heart it's really just an HTML table. It's worth taking a moment to familiarize yourself with this markup if you haven't studied it before. You can refer back to it later when we start looking at the CSS rules.

        <div class="x-panel x-grid">
            <!-- The column headers -->
            <div class="x-grid-header-ct">...</div>

            <!-- The panel body -->
            <div class="x-panel-body x-grid-body">
                <!-- The grid view -->
                <div class="x-grid-view">
                    <table class="x-grid-table">
                        <tbody>
                            <!-- A dummy row used to resize columns -->
                            <tr class="x-grid-header-row">...</tr>

                            <!-- The first grid row -->
                            <tr class="x-grid-row">
                                <td class="x-grid-cell x-grid-cell-first">
                                    <div class="x-grid-cell-inner">Timothy</div>
                                </td>
                                <td class="x-grid-cell">
                                    <div class="x-grid-cell-inner">34</div>
                                </td>
                                <td class="x-grid-cell x-grid-cell-last">
                                    <div class="x-grid-cell-inner">M</div>
                                </td>
                            </tr>

                            <!-- The second grid row -->
                            <tr class="x-grid-row x-grid-row-alt">
                                ...
                            </tr>

                            ...
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    

There are a few CSS classes that are particularly worthy of note in this markup:

  • x-grid-row – Applied to the <tr> element for each row of the grid.
  • x-grid-row-alt – Applied to the <tr> element for alternate rows of the grid.
  • x-grid-cell – Applied to the <td> element for each cell.
  • x-grid-cell-first – Applied to the <td> element for the first cell in each row.
  • x-grid-cell-last – Applied to the <td> element for the last cell in each row.

Column Lines

One simple change that can be made without any custom CSS is to add vertical lines between the columns. This just needs the setting columnLines to be set to true.

        Ext.create('Ext.grid.Panel', {
            columnLines: true,
            ...
        });
    

This setting adds a handful of CSS classes to the outermost <div> element of the grid. The most important of these is x-panel-with-col-lines, which has an accompanying rule in the default stylesheet to put a grey border down the right-hand side of each grid cell.

Styling All Grid Cells

        Ext.create('Ext.grid.Panel', {
            cls: 'custom-grid',
            ...
        });
    

The setting cls is common to all ExtJS components. It adds a CSS class to the main element for the component. In this case it will put the class custom-grid on the outermost <div> element.

Making the grid cells appear yellow needs some CSS like this:

        .custom-grid .x-grid-cell {
            background-color: #ffa;
            border-bottom-color: #ffc;
            border-top-color: #ff5;
            color: #009;
        }
    

The selector for this rule will match all the <td> cells for this grid. Notice how the styling is applied to the cells rather than the rows. A common mistake is to try to change the background-color for the <tr> element rather than the <td>. This doesn't work because the cells have their own background-color, which completely hides the row.

A little more CSS is needed to handle mouse-over and selection. In both cases the grid automatically adds a CSS class to the <tr> element that we can use to style the cells in those rows.

        /* Grid cells when the mouse cursor is over the row */
        .custom-grid .x-grid-row-over .x-grid-cell {
            background-color: #ff6;
            border-bottom-color: #999;
            border-bottom-style: dashed;
            border-top-color: #999;
            border-top-style: dashed;
        }

        /* Grid cells in the selected row */
        .custom-grid .x-grid-row-selected .x-grid-cell {
            background-color: #ff0 !important;
            border-bottom-color: #999;
            border-bottom-style: solid;
            border-top-color: #999;
            border-top-style: solid;
        }
    

One last thing to note here is the use of the !important flag in the code above. Generally this should be avoided but it can't be helped here because it's overriding a rule in the default ExtJS CSS that also uses this flag.

Alternate Rows

As we've already seen, alternate rows of a grid have the class x-grid-row-alt. This makes it easy to do simple striping effects.

        Ext.create('Ext.grid.Panel', {
            cls: 'extra-alt',
            ...
        });
    
        .extra-alt .x-grid-row .x-grid-cell {
            background-color: #fff;
            color: #000;
        }

        .extra-alt .x-grid-row-alt .x-grid-cell {
            background-color: #000;
            color: #fff;
        }
    

If you don't want to take advantage of row striping then you can disable it like this:

        Ext.create('Ext.grid.Panel', {
            ...
            viewConfig: {
                stripeRows: false
            }
        }
    

Adding the class x-grid-row-alt to alternate rows incurs a performance penalty. Every time the row order changes the grid needs to go through all of its rows and update the CSS classes. If you aren't styling alternate rows differently from each other then you should claim back that performance by turning off row striping. The earlier example of turning all the rows yellow would be a suitable candidate for this.

Styling Rows

In many cases the styling for a row is dependent on the data for that row. That data is represented by a record in the store. We can add a CSS class to the <tr> element based on the record using the config option getRowClass:

        Ext.create('Ext.grid.Panel', {
            ...
            viewConfig: {
                stripeRows: false,
                getRowClass: function(record) {
                    return record.get('age') < 18 ? 'child-row' : 'adult-row';
                }
            }
        });
    
        .child-row .x-grid-cell {
            background-color: #ffe2e2;
            color: #900;
        }

        .adult-row .x-grid-cell {
            background-color: #e2ffe2;
            color: #090;
        }
    

Recall that the class x-grid-cell is on the <td> elements for each cell of the row.

Static Column Styling

The simplest way to style a column is to add a CSS class to every <td> element in that column. The column config setting tdCls does just that.

        Ext.create('Ext.grid.Panel', {
            ...
            columns: [
                {...},
                {dataIndex: 'age', flex: 1, text: 'Age', tdCls: 'custom-column'},
                {...}
            ],
            ...
        });
    
        .x-grid-row .custom-column {
            background-color: #ecf;
            color: #090;
            font-weight: bold;
        }
    

In the CSS selector above note that x-grid-row is on the <tr> element and custom-column is on the <td> element.

First and Last Column

As we've already seen, the cells in the first and last columns of a grid have special CSS classes on them. These can be used to style the cells in those columns. The key difference between this and a tdCls is that the tdCls follows its column when the columns are reordered. If you try reordering the columns in this example you'll see that the styling does not follow the column.

        Ext.create('Ext.grid.Panel', {
            cls: 'custom-first-last',
            ...
        });
    
        .custom-first-last .x-grid-row-selected .x-grid-cell-first {
            background-image: url(/static/lib/silk/bullet_go.png);
            background-repeat: no-repeat;
            padding-left: 12px;
        }

        .custom-first-last .x-grid-row-selected .x-grid-cell-last {
            background-image: url(/static/lib/silk/user_green.png);
            background-position: right;
            background-repeat: no-repeat;
        }
    

Dynamic Column Styling

Often the styling for a column will vary row by row. A column renderer allows the tdCls to be set individually for each cell.

        Ext.create('Ext.grid.Panel', {
            ...
            columns: [
                {...},
                {...},
                {
                    dataIndex: 'sex',
                    flex: 1,
                    text: 'Sex',
                    renderer: function(value, meta) {
                        if (value === 'M') {
                            meta.tdCls = 'male-cell';
                            return 'Male';
                        }

                        meta.tdCls = 'female-cell';
                        return 'Female';
                    }
                }
            ],
            ...
        });
    
        .male-cell {
            background: url(/static/lib/silk/user.png) no-repeat 2px 1px;
            padding-left: 16px;
        }

        .female-cell {
            background: url(/static/lib/silk/user_female.png) no-repeat 2px 1px;
            padding-left: 16px;
        }
    

While it isn't shown in this example, the third argument passed to the renderer function is the record for the row, which allows the tdCls to be derived from other fields if necessary.

Styling Dirty Fields

By default, a dirty field is marked with a red triangle in the top-left corner of the corresponding cell. As you might expect, this is achieved by adding a CSS class to the <td> element. The triangle itself is rendered using a background image. Customizing it just needs a bit of CSS.

        Ext.create('Ext.grid.Panel', {
            cls: 'custom-dirty',
            ...
        });
    
        .custom-dirty .x-grid-dirty-cell {
            background-color: #ffa;
            background-image: url(/static/lib/silk/bullet_red.png);
            background-position: right center;
            color: #090;
        }
    
-----