Skip to content

AngularJS: Fixed-header scrollable table

2013 December 19
by Alec

For the last couple projects I’ve worked on, there has been a need for a Bootstrap-based scrollable table with a fixed header. A pretty common requirement, I’d say. Common enough to inspire quite a few solutions in my current favorite client-side framework, AngularJS.

At the time, there weren’t many options, so I wrote my own directive with a few requirements in mind:

  • Static table height. This is to allow room for more content on the page regardless of how many rows the table has.
  • Pretty column headers. Often, there isn’t enough screen real estate to display the full text for each column header, so when one is too long, truncate it and add ellipsis. None of the existing Angular implementations handle this well, and it turned out to not be an easy feature to implement.
  • Sortable headers. Specify an attribute to sort on and an optional comparator function.
  • Scroll-to-row. Specify an identifier for each row, and the directive will respond to “rowSelected” events, scrolling to display the row.

Everything else was left alone, so that you still have full control over the (transcluded) HTML and can style it or add event handlers wherever you please.

The CSS was inspired by:

Here is the directive in action, complete with sortable headers:

As with any software, some tradeoffs were made to meet my requirements. Here are some of them:

  • Header height is fixed. Overly-long headers are truncated. I typically add a title=”” attribute so users can still see the full text.
  • Headers are no longer included in the calculation of column width. The best workaround here is to add min-widths to table cells.
  • Dynamically adding row content may shift columns so that the header is no longer accurate. There is a watch attribute required by the directive that must be updated in order for the column headers to re-render.

Edit Jan 2, 2014: The code is now on GitHub, for your forking pleasure (ahem).

40 Responses
  1. dbaumann permalink
    February 7, 2014

    This is awesome work. I thought I had this problem solved, but this shows me that I was just scratching the surface. I’m also learning how to write better directives from reading your code.

    • Alec permalink*
      February 7, 2014

      Thanks! It has evolved dramatically over the months. The biggest improvement (IMO) was adding inter-directive communication between the main table and headers. Also, I’d be keen to find a better method for discovering when the table has rendered.

  2. Robert K permalink
    February 28, 2014

    Very nice plugin, unfortunately it didn’t work for me. Have to spend some more time figuring out what is going on. I think the “watch” issue had some issues.

    • Alec permalink*
      March 1, 2014

      Hi Robert, if you put your code into a jsfiddle or plunkr I could take a look.

  3. March 3, 2014

    Hi. Thanks for this blog. Excelent!

    Now, I have some issues. First. What if I don´t want a header (), and none “headerSpacer”?

    Is there some kind of key word to disable it?

    Thanks again.

    • Alec permalink*
      March 4, 2014

      Well, if you don’t want a header, I suggest another plugin! Or just use a plain ‘ol <table>. Alternatively, the template is just HTML, so you can remove the <div class=”headerSpacer”> if you wish.

  4. Peter van der Leek permalink
    April 4, 2014

    Great work!
    Unfortunately your directive fails if the header contains more than 1 row, see

    I will have a look at the code now to see if I can fix the problem myself, but your help is of course appreciated 🙂

    • Alec permalink*
      April 4, 2014

      Hi Peter,

      Yes, I personally never needed more than one row, but I don’t think it would be too difficult to modify the CSS/JS to support more than one. Let me know if you figure out a good solution.

      • Peter van der Leek permalink
        April 16, 2014

        Unfortunately not so easy as it might seem. Some static stuff in the CSS will have to become dynamic. I couldn’t get it working 🙁

  5. Michael permalink
    June 27, 2014

    I am trying to use your module. Do you have a usage document on what I need to do to install and use your module? I am new to angular btw.

  6. Alex permalink
    July 31, 2014


    Is there also a version for Bootstrap 3.x?
    because i think it does not work correct there.

    br alex

    • Alec permalink*
      September 8, 2014

      Yes, the latest version is 3.x compatible. See the GitHub project page.

  7. Evahn permalink
    September 17, 2014

    Thanks for this table – it seems to work really well.

    Is there an easy way to set the initial sort column and direction as the records appear unsorted until you click on a direction arrow in the column header to sort by that column.

    • Alec permalink*
      October 5, 2014

      Hi Evahn, I don’t believe the directive, when loaded, indicates an initially sorted column. You could of course provide sorted data to the ‘rows’ attribute. Please file an issue on Github if you would like to see this functionality added.

  8. Sanjay permalink
    September 23, 2014

    Hi Alec,

    Good Job!!
    If the table head is fixed its working excellent!! but if i have two static column and 5 dynamic column which i am looping for the header title then its not coming perfect as i am expecting.
    Could you please help me for the same?


    • Alec permalink*
      October 5, 2014

      Hi Sanjay,

      If you put your code online with Plunkr or JSFiddle I could take a look more easily. Or, if you are sure it is a bug, please create an issue on Github. Thanks

  9. October 28, 2014

    Hi Alec, nice job..thanks!!

    We like the resizing of the colums and truncate feature of the header, but can it also be disabled? We need the header ‘names’ to be fully visible all the time, but scrollable of course 🙂

    Any help would be appreciated, thanks!

    • Alec permalink*
      October 28, 2014

      Hi Willem,

      The display of the headers is just simple CSS, so just adjust or override as necessary. See .scrollArea table .th-inner.

  10. raven permalink
    October 31, 2014

    hi Alec ,
    I want to fixed the header and first column,is this possible?

    • Alec permalink*
      October 31, 2014

      Fixed header: yes; first column: no, that’s out of scope for us.

  11. Louis permalink
    December 23, 2014

    Hello Alec,

    Thanks for your work !
    Just to precise that it correctly merges with ng-table (I had a doubt with watch=”$data” ^^) except regarding the pagination which is included in the scrollable container.

  12. Sergey permalink
    February 14, 2015

    Hi Alec,

    Great work!
    I was unable to use ‘resizing’ directive.
    How can use the resizing of the colums?

    Best regards,

    • Alec permalink*
      May 16, 2015

      Hi Sergey, simply add the ‘resizable’ attribute to the scrollable-table element to enable resizing.

  13. Abhishek Pandey permalink
    June 17, 2015

    Awesome tutorial!
    How can I add a fixed footer into it?

  14. Guib permalink
    June 18, 2015

    Hi Alec,

    Thanks for the great job,

    I used your module and il works perfectly with Chrome and IE but nothing is displayed in Firefox.

    Do you have tested it on different browsers?


  15. Harish permalink
    July 7, 2015

    Hi Alec,
    Great work! It helped me a lot.
    How do I wrap the text in headers? I could not find a way to wrap the text for header, all I can get to work is ellipsis. Is there a way that I can wrap the text into a separate line for header?

  16. sundar permalink
    November 9, 2015

    I am not able increase the height of the scrollable table. It s always taking around 310 px height. I want the grid should expand the containers height and show the scroll after that.Can you tell me if it can be achieved through any workaround ?

    • Alec permalink*
      November 9, 2015

      Hi Sundar,
      The height and overflow:scroll are set in the CSS.

  17. Naveen Nagendran permalink
    December 13, 2015

    Hey Alee,

    I have 2 issues.
    a. I have a dropdown on table header. On click the dropdown shows up however it hides behind table body. Tried with overflows and positions, no use though
    b. I need to override title attribute on table header.

    Any suggestion will be helpful

  18. Ali permalink
    March 3, 2016


    How to control the direction right to left (rtl) for header, it is applied for body but header not.

    Thanking You,

  19. Ali permalink
    March 5, 2016


    the direction working fine when it is not resizable option, and the problem only when resizable

    Thanking you,

  20. tarun prasad permalink
    March 31, 2016

    Hi Alec,

    Great work,

    I used your module and it works perfectly with Chrome and IE but not working perfectly in Firefox.

    Do you have any solution ?


    • Alec permalink*
      June 13, 2016

      This should be fixed in the latest version. See the GitHub repo.

  21. Paul permalink
    April 28, 2016

    I love the module, but I’m having some problems getting it to work, even though I seem to have followed the code pretty precisely.

    As you can see in that image, the header row columns are showing in the data area, and not in their (visual) header row – but the sort arrow IS in that row, which is kind of funny.

  22. srikanth permalink
    April 30, 2016

    Aloha Alec,

    I am using your module. I am running into a situation, where u might give me some quick inputs.
    Situation here is I need server side sorting on my table. When I click on the table header, it should make service request to get sorted data.

    The moment I use the column resizer to resize column width, it fires the “th”s data-ng-click method.


    vm.tableHeaderClick(column) method is defined in my controller. This gets called as and when I do a column resize using column resize handler.

    How can I stop this event propagation from column resizing to this click event ?


  23. May 3, 2016

    Hi Alec,

    Great work. I’m using your table in my current project and I’m very satisfied.

    Thank you!

    Do you have any suggestions what would be the best way to make height of the table become dynamic ?

    Best wishes from Bosnia…

    • May 3, 2016

      This solution works fine for me



  24. Martin Friesen permalink
    June 10, 2016

    Scrollable Tables doesn’t work with modals.
    How can I fix this?

    • Alec permalink*
      June 13, 2016

      Please create a jsfiddle displaying the issue.

Comments are closed.