How to freeze the first column and the header of an optimized table

If you are rendering a table with a lot of columns, it is sometimes important to freeze the first column of the table or the header.

This article explains how to implement such functionality using a CSS-only approach and the ‘display: sticky’ attribute. This approach does not require Javascript and it will only work on modern browsers. For older browsers like IE11, it will use graceful degradation and will not freeze the column and header.

Here is a demo of the functionality using the Templates landing page in Dev Studio.

demo freeze column and header.gif

1/ Configure the table to be a read-only table and optimized

The optimized table has a simpler markup than the legacy table - For this functionality to work, you need to make sure to upgrade your table to use the optimized table.

2/ Add the following CSS snippet to a CSS File attached to your skin

Note: this code snippet uses a section name in the selector to only apply the change to this section. A better approach is to wrap the section containing the table into another section and use a helper class attached to the section include. You can also apply the selectors to a specific table format but beware that creating a new table format will increase significantly the size of the auto-generated CSS file.

The snippet will freeze both 1column and header - remove the unneeded selector if you only need to freeze the header or the column.

div[data-node-id="pzSectionsUsingDTemplate"] #gridBody_right {
    position: relative;
    max-width: calc(100vw - 80px);
    margin: auto;
    z-index: 1;
    overflow: auto!important;
    max-height: 800px;
}
div[data-node-id="pzSectionsUsingDTemplate"] #gridBody_right > table {
    overflow:auto;
    border-left:none!important;
    border-top:none!important;
}
/* implement the freeze of the first column */ 
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr > th:nth-child(2),
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr > td:nth-child(2){
    background: #FFF;
    z-index: 2;
    position: sticky!important;
    left: 0;
    border-left: 1px solid #CBCBCB;
}
/* implement the freeze of the header */ 
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(1) > th {
    background: #FFF;
    position: sticky!important;
    top: 0;
    border-top: 1px solid #CBCBCB;
}
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(1) > th:nth-child(2) {
    z-index:3;
}

the max-height and max-width will need to be changed depending on where your table is dropped. The same applies to the border color.

Freezing some of the top rows

it also possible to freeze some of the first rows using some additional CSS - here is some updated CSS that would freeze the first 4 rows - to guarantee that the row height is always the same, we set a white-space:nowrap on these rows.

div[data-node-id="pzSectionsUsingDTemplate"] #gridBody_right {
    position: relative;
    max-width: calc(100vw - 80px);
    margin: auto;
    z-index: 1;
    overflow: auto!important;
    max-height: 800px;
}

div[data-node-id="pzSectionsUsingDTemplate"] #gridBody_right > table {
    overflow: auto;
    border-left: none!important;
    border-top: none!important;
}

/* implement the freeze of the first column */

div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr > th:nth-child(2),
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr > td:nth-child(2) {
    background: #FFF;
    z-index: 2;
    position: sticky!important;
    left: 0;
    border-left: 1px solid #CBCBCB;
    white-space: nowrap;
}

/* implement the freeze of the header */

div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(1) > th,
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(2),
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(3),
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(4),
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(5) {
    background: #FFF;
    position: sticky!important;
    top: 0;
    border-top: 1px solid #CBCBCB;
}

div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(2) > td,
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(3) > td,
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(4) > td,
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(5) > td {
    white-space: nowrap!important;
}

div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(5) > td {
    border-bottom: 1px solid red;
}

div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(2),
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(2) > td:nth-child(2) {
    top: 46px;
    z-index: 3;
}

div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(3),
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(3) > td:nth-child(2) {
    top: 65px;
    z-index: 3;
}

div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(4),
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(4) > td:nth-child(2) {
    top: 85px;
    z-index: 3;
}

div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(5),
div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(5) > td:nth-child(2) {
    top: 105px;
    z-index: 3;
}

div[data-node-id="pzSectionsUsingDTemplate"] #gridTableBody > tr:nth-child(1) > th:nth-child(2) {
    z-index: 3;
}

demo freeze.gif

Conclusion

This solution allows you to easily add support for a sticky header and footer to your table. If you want a richer example with different column freeze and for a very large table, the recommendation is to use the new React-based table available in Pega 8.5 - These new table implementation can run along with your Pega Infinity application as a dedicated landing page.

for more details

https://community.pega.com/knowledgebase/articles/whats-new-pega-platform/modernize-your-interface-cosmos-react-ui-85

https://www.youtube.com/watch?v=lnYb-Eu-b2A

Hi @RichardMarsot, I found this article is very helpful!

I copied your CSS snippet and changed section name with mine, however I found there is outer scrollbar appears in the harness level. (see screenshot). Is there a way to remove this outer scrollbar?

Posting the answer on behalf of Richard.

Chunzhi,

Like I indicated in the article, ‘the max-height and max-width will need to be changed depending on where your table is dropped’

In the example I provided the max-height is set to 800px – this was just for the demo

You should change this max height to be based on the viewport (100vh) and remove the spacing used by header + other elements

For example max-height: calc(100vh – 300px) – this height will avoid seeing the scrollbar on the main content

Note that this will only work if you only have the table inside the page – if you have 2 tables, you will not be able to avoid the double scrollbar

If you are using Pega 8.5 or Pega 8.6, I would recommend to switch to the new React based table – they will provide the freeze column automatically.

-richard

Hi RichardMarsot - is there a plan/roadmap to add this extremely useful and oft requested feature to freeze column headers or even the first column going to be available as a configuration setting on the Table controls (OOTB in the platform) in upcoming versions (8.6 or 8.7)?

@RichardMarsot

Thanks for sharing this useful feature.

I’ve used this CSS code in Pega Mobile App as we have that requirement for Mobile. The header and first column are freezed but it is too flexible that as I move my fingers on the mobile screen diagonally then the Grid rows also moves diagonally (except header and first column).

Is it possible to fix the mobile behavior with any additional CSS?

Note: It worked perfectly fine in Web Portal but seeing the mentioned issue in Mobile App. By the way, we are using Pega Platform 8.6.

@RichardMarsot how to apply same functionality in Pega constellation. is there any way to set first column as default freeze ?

@VenkateswararaoK use the freeze column shown in your screen and set the table as default view

@CharanTejaS8456 mobile is working fine for me - I tested with Theme-Cosmos on a 8.6 with an Android phone. -

position sticky is supported on IOS and Android - CSS position:sticky | Can I use... Support tables for HTML5, CSS3, etc

Try to put the table on the Home page first to make sure that this is not a page issue.

Having said that I don’t think this is a good experience on mobile - I would recommend to have a different presentation on mobile using RDL rather than table and expose some filters in a modal dialog as well as rely on search to filter the data

@RichardMarsot

Thanks for the quick response.

Our Application is built on PFS (Field Service) which uses UI-Kit. And our field users uses iPhone. Does this CSS feature works on UI-Kit in iOS Mobile seamlessly?