Fixed Header Shiny DataTables

3,021 views
Skip to first unread message

Aaron Ouyang

unread,
Feb 21, 2014, 1:13:44 AM2/21/14
to shiny-...@googlegroups.com

Hi, after google and stackoverflow, I still did not find a good answer to set fixed or freezed header in shiny datatables.

The key not clear is that how to put in ui.R or server.R the js mentioned on datatables site as below and make it work on the table output?

/*
 * Example initialisation
 */
$(document).ready( function () {
    var table = $('#example').dataTable();
    new $.fn.dataTable.FixedHeader( table );
} );



Does RStudio consider include the "Fixed Header" as an option for rednerDataTable()? This will help many people.

Thanks.

Dmitri

unread,
Feb 21, 2014, 10:03:37 AM2/21/14
to shiny-...@googlegroups.com
In the next version of shiny passing callback functions to DataTables will hopefully be allowed. It should be easier to implement then. For now... Every time renderDataTable() is re-executed a new DataTables object is created with a new id that you need to create a FixedHeader object for in addition to removing FixedHeader object of the previous DataTable. You need to catch the event of creating this new DataTable (try setting some onclick/onchange event handlers or MutationObserver to catch DOM changes), but you also need to catch the event of moving between tabs, when DataTable objects get hidden and FixedHeader objects must get hidden as well. So, it's not as easy as adding couple of lines of code to ui.R. I agree that it would be great to get it built-in in the next versions of shiny.

Aaron Ouyang

unread,
Feb 24, 2014, 5:14:01 AM2/24/14
to shiny-...@googlegroups.com
Thanks. Then look forward to the next version Shiny!

Yihui Xie

unread,
Feb 25, 2014, 4:49:22 PM2/25/14
to Aaron Ouyang, shiny-discuss
Hi,

I just added experimental support for this in the development version:
https://github.com/rstudio/shiny/

You need to download the FixedHeader plugin from
http://datatables.net/extras/ and put dataTables.fixedHeader.js under
www/ (if you put it in other directories, you need to adjust the path
in tags$script() below accordingly). Then this should work:

# ui.R
shinyUI(fluidPage(
dataTableOutput('tab1'),
tags$head(
tags$style(type='text/css', '.fixedHeader table.table thead
.sorting { background-color: #eee; }'),
tags$script(src='dataTables.fixedHeader.js')
)
))

# server.R
shinyServer(function(input, output) {
output$tab1 = renderDataTable(iris, callback='function(oTable) {
new FixedHeader(oTable); }')
})

Please test it if you can. Thanks!

Regards,
Yihui

Yihui Xie

unread,
Feb 25, 2014, 4:53:11 PM2/25/14
to Dmitri, shiny-discuss
I think that is a good idea and I just added a 'callback' argument in
renderDataTable() so that users can easily use the DataTable instance
to do whatever they want. An example is given in my previous reply.
Thanks!

BTW, note I did not add FixedHeader plug-in directly to shiny, since
it will be a burden for us to maintain all these plug-ins in shiny.

Regards,
Yihui

Aaron Ouyang

unread,
Feb 26, 2014, 4:01:28 AM2/26/14
to shiny-...@googlegroups.com, Dmitri
Hi Yihui,

I tested your solution above and it works under shiny server!
Many thanks!

Aaron

Dmitri

unread,
Feb 26, 2014, 6:24:17 AM2/26/14
to shiny-...@googlegroups.com, Aaron Ouyang
Check this example:

# ui.R 
shinyUI(fluidPage(
  selectInput('spec',"Species",levels(iris$Species)),
  tabsetPanel(
    tabPanel("Tab 1",dataTableOutput('tab1')),
    tabPanel("Tab 2", NULL)
  ),
  tags$head( 
    tags$style(type='text/css', '.fixedHeader table.table thead .sorting { background-color: #eee; }'), 
    tags$script(src='dataTables.fixedHeader.min.js'),
  ) 
))

# server.R 
shinyServer(function(input, output) { 
  output$tab1 = renderDataTable(iris[iris$Species==input$spec,], callback='
function(oTable) { 
  new FixedHeader(oTable);
  oTable.on("remove", function () {
    $("table:not([id])[aria-describedby^=\'"+oTable.attr("id")+"\']").parent().remove()
  })
}') 
})

Endless amount of header objects is created every time you change input$spec, I added a fix for that. However, I can't come up with an elegant fix for header objects always staying visible when switching tabs.

Aaron OUYANG

unread,
Feb 26, 2014, 9:35:11 PM2/26/14
to Dmitri, shiny-...@googlegroups.com

Hi,

 

I encountered another issue here probably due to CSS. When the content of one column is too long, the fixed header may seem not aligned with the original table header. And the alignment varies in different browsers.

 

Although the codes Yihui provided did work in building the fixed headers, I feel sorry to have to remove the fixed header because of the uncomfortable alignment.

 

Thanks again.

 

Aaron

Dmitri

unread,
Feb 27, 2014, 9:23:33 AM2/27/14
to shiny-...@googlegroups.com, Dmitri
It still might be useful to have some custom event triggered when switching tabs, as I'm not aware of any easy way to capture this event (apart from using mutations, which I don't consider proper either). It should be easy to add and will solve many potential problems like the one I described above. BTW, what's the "official" way to assign a callback function now: options = list(fnInitComplete=I('function here')) or callback = 'function here' (I mean the approach that will go to the next release)? I personally like the first approach more, as it's much more flexible and allows to use other callbacks. E.g. to add conditional formatting that has to be re-applied after every sorting needs fnDrawCallback.

Yihui Xie

unread,
Feb 27, 2014, 11:52:00 AM2/27/14
to Dmitri, shiny-discuss
The new 'callback' argument in renderDataTable() is not the same thing
as DataTables callback functions. Instead, it is only a function with
one argument 'oTable' that is the DataTable instance created for the
table. You can manipulate this instance using the 'callback' function.
This is what it basically does:

oTable = $('#foo').dataTable({ options })
callback(oTable)

It is irrelevant to DataTables callbacks such as fnInitComplete
(http://datatables.net/usage/callbacks), which are part of the
initialization options and must be passed in using the 'options'
argument in renderDataTable().

Regards,
Yihui

Dmitri

unread,
Feb 27, 2014, 12:11:52 PM2/27/14
to shiny-...@googlegroups.com, Dmitri
Now I got it, thanks!

Jie Song

unread,
Dec 16, 2014, 7:51:37 AM12/16/14
to shiny-...@googlegroups.com, aaron....@glamour-sales.com.cn
Hi Yihui
I tried the example you gave in my own application.
Since I have several main and sub tabs, I found that the head of the table appears on all the tabs, which is quite annoying.
Is there any way to deal with it?

Thanks very much.
Jie

在 2014年2月25日星期二UTC+1下午10时49分22秒,Yihui Xie写道:

aakash solanki

unread,
Jan 30, 2015, 5:11:50 PM1/30/15
to shiny-...@googlegroups.com, aaron....@glamour-sales.com.cn
There must be a way to remove the FixedHeader of the oTable in a tab and then create a new one when user clicks on the other Tab. 

aakash solanki

unread,
Jan 30, 2015, 5:45:59 PM1/30/15
to shiny-...@googlegroups.com, aaron....@glamour-sales.com.cn
http://www.datatables.net/forums/discussion/4313/destroy-fixedheader/p1

There is help here at the above link. But I have not been able to implement it in my shiny app. 

Any help would be appreciated.

Yihui Xie

unread,
Mar 10, 2015, 5:31:53 PM3/10/15
to aakash solanki, Jie Song, Ouyang Aaron, Dmitri, shiny-discuss
The usage has been significantly simplified in the DT pacakge now.
Please see http://rstudio.github.io/DT/extensions.html for how to
enable DataTables extensions, and
http://rstudio.github.io/DT/shiny.html for how to use DataTables in
shiny.

To Jie: I believe that was an issue with the z-index
(http://datatables.net/release-datatables/extensions/FixedHeader/examples/zIndexes.html),
and you can use a smaller z-index, e.g.

library(DT)
datatable(
iris, options = list(pageLength = 50), extensions = list(FixedHeader
= list(zTop = 1))
)

If this does not work, it is probably a bug of the FixedHeader extension...

Regards,
Yihui
> --
> You received this message because you are subscribed to the Google Groups
> "Shiny - Web Framework for R" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to shiny-discus...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/shiny-discuss/22e328fd-d08a-42d4-8566-ff3ebd3baffa%40googlegroups.com.
>
> For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages