Flexible HTML Table with Fixed Header and Footer around a Scrollable Body



I'd be happy to try adapt the css to your demo table when I find the time. :slight_smile:

I think I found the old example of a horizontal scroll version that has the flexibility you want, though it isn't vertical. :grin:

The demo example for a horizontal table scroll using the data attribute with some explaining of how it works:

Table-Horizontal-Scroll.html (6.0 KB)

I uploaded a version to JsFiddle to play with: https://jsfiddle.net/Erik_J/hzy53qc8/


Hi Erik,

Nice demo:)

I notice there's a bug/behaviour in Chrome where your text-transform capitalize is causing a problem. Some of the left column cells are overlapping the next row because you are losing the capital letter of the first word in the th content. The data-th is being capitalized ok but the original content is losing the capital on the first word thus making the content shorter and when some words wrap they cross over into the cell below.

You can solve the bug by including a space after the content in the th.

<th scope="row" data-th="row header longer longer longer longer longer longer longer ">

Note the space at the end of the line.

I'm assuming that when you use:before then Chrome assumes that the item inserted is the first letter and you lose the capital on the first word of your normal data.

I still don't think this addresses the fluid nature of the cells as you have simply moved the fixed height restrictions of the vertical demo into a fixed width restriction in the horizontal version. :slight_smile:

My original js demo is fully fluid in width and height for header, footer and content (although it does have problems in other areas as already mentioned) :slight_smile:


I re-jigged my JS demo to use vanilla js instead of jquery and I fixed the scroll bar differences between browsers by using a holding div that was set overflow scroll so that the cloned tables are placed inside that div and thus match exactly the original content. (I'm sure the JS could be tidied up a lot but its only half a dozen lines anyway).

It doesn't matter what size the browsers scrollbars are (or whether they are overlaid when scrolling as on mac systems) as the content will always match up exactly

The fix comes at the expense of an empty div but the table remains untouched and doesn't need any special formatting. It should work in all modern browsers (ie11+ - maybe older but haven't tested).

The table headers and footers are fully fluid both in width and height and have no restrictions upon them.

As mentioned earlier there may be overheads in cloning large tables so use at your own risk or test with care. I'm not sure of the accessibility implications either so probably need to mark up with aria:hidden rules.


I've started so I'll finish :slight_smile:

Here's an improvement that only needs to clone the table once and is just a few lines of vanilla js.

As before this is fully fluid in width and height.

So far looks promising and seems to work in IE11+ and other modern browsers.

Usual caveats apply.


Thanks for debugging my poor example. :slight_smile:

I think I'll just add a trailing space to the data attribute:

Impressive solution you recently posted (#16), especially the natural scrollbar! :slight_smile:

I'll try improve my noscript version next free moment. :sweat_smile:


:ballot_box_with_check: Standard HTML table & its constituent semantic elements.
:ballot_box_with_check: Fixed thead & tfoot at the top & bottom of the table, respectively.
:ballot_box_with_check: All column widths resize automatically depending on cell contents.
☐ Vertical scrollbar within tbody.

@PaulOB This is greatness so far. Even ignoring the last requirement, this is an amazing feat as it is! I have some follow-up questions for when you can spare some time:

  1. Looking at the JS, is there a difference between the 2 functions that's called? I think one of them is meant to be removed/commented out since they both appear to be identical.

  2. Forgetting the 4th requirement for the moment & focusing on your execution of it, have you taken into account when the table isn't populated enough to necessitate a scrollbar? When that's the case, the hidden scrollbar hangs off the end of the table & pushes each column further out of alignment consecutively. When the scrollbar is an overlay, it doesn't look so iffy but the misalignment is still present.

  3. … and returning to the "vertical scrollbar within tbody" request, I wonder if a slight modification could be made to it in your execution: can the scrollbar have margins at its top & bottom that corresponds to the height of thead & tfoot cut away from it? If that's possible, the CSS can be adapted so that it's not obvious whether the scrollbar is overlaid within the table or beside it. Removing borders & merging together background-colors could make it look just as if it's inside the table, without actually adding any of the headache associated with actually doing that.

I can't thank everyone enough for their input & insight into this. Really much appreciated :slight_smile:



You just caught it when I was playing around and only the first one is needed. The Js could be condensed into one line of js but becomes less readable so I reverted it back to original. If you refresh you should see latest version.

That can be overcome by simply setting a height on the table that matches the other heights we set for the wrap (all 300px). In that way if there is only one row then the whole row stretches from header to footer. If there are two rows they take up 50% and so on until there are more rows than the height allows and then the height expands to allow the scroll automatically.

You could use js detect if the table was taller than the 300px height (or whatever height you have set) and then only add the fixed header and footer but that would complicate it a little I feel and I like the way it works at present and remains fairly simple.

The code is updated so just copy and remove rows etc to see how it behaves.

The problem with moving the thead and tfoot above and below the scrollbar is that you can never know the height of the thead/tfoot unless you constantly monitor them as they will change when the window is resized and content wraps. This would mean tying into the resize event in JS and then every time the screen size is changed you would need to update the thead and tfoot positions accordingly. This then becomes an intensive task for the browser (even with throttling) and leads to the 'janky' operation of the whole window when resized and something that will annoy most users. It's not impossible but something you would want to avoid most of the time unless its something that you can't do without.:slight_smile:


If you don't mind cloning the table twice then it can be done.

I've only just knocked this up so haven't had time to test and I am away for the evening now.

Most of the rules are cosmetic in hiding and showing borders etc.


@PaulOB Glad to hear one of those functions were redundant! I'm looking into that table more closely now & was unsure about a few details.

Unfortunately for my use case, I won't know how much screen estate is available to display the table (it's handled automatically by a flex container that grows to "absorb" all space available to it) so I can't possibly assign a fixed height to it. Which brings me to a couple of questions about this:

  1. Can the fixed thead & tfoot automatically assume their positions at the top & bottom, respectively, of their container? i.e., the div container within which the table is placed.

  2. The stretchy rows are kinda unexpected. I get why they do that but, for the purposes of displaying this table, I think each row should only be as tall as they need to be, as in the rest of your demos. If there isn't enough rows to warrant a scrollbar, then the table should either:

    • Show the table background in the empty space under the rows. The thead & tfoot should remain fixed at the top & bottom of their container despite the lack of rows.

    • Show the table background in the empty space under tfoot. In essence, the tfoot "shifts" upwards to stick under the bottom of the last row.

In both cases, the scrollbar should be hidden away from sight as it has no purpose. Personally, I'd prefer the first option because it doesn't make much sense, from a user experience perspective, to have tfoot just a couple of rows away from thead. Having it at the bottom of the container/page might not be of much help either but, at least, it maintains consistency whether the table is fully populated or not. Familiarly adds great value to any UI. Speaking of which, on a completely unrelated note, I find SitePoint UI/UX to be incredibly intuitive with the user's needs at the forefront above all else. I started looking into both StackOverflow & SitePoint at the same time and it's ridiculous how well SitePoint handles certain features/implementations compared to the alternative.

You probably understand what I mean but, just in case I haven't made myself clear enough, here's an image of my suggestion: on the left is the current implementation while the one on the right is my suggestion of a scrollbar with margins the same size as thead & tfoot. The idea being that both are positioned outside the table but the one on the right looks more closely integrated than it really is.

I think you're likely to be absolutely correct in that it would require some zany JS-magic to pull off. Although, for the sake of comparison, if we were to clone the entire table twice, perhaps it's not as process-intensive as it might seem at first to run a JS script which adjusts for resizing dynamically. But that's just me thinking aloud.

Coincidentally, I came across a jQuery plugin on GitHub which attempts to address this very issue and does commendable job of it as seen in this live demo. It even offers an optional parameter to only display a certain number of rows. However, it is of course not without its own issues (limited to one table per page is a significant one) and I think it might be verging dangerously close to the land of JS voo-doo so I wonder what's everyone here take on it.


You didn't look at my last post which has an example of scrollbars on just the body. It does need 2 cloned tables to achieve though as there is no other way to do this smoothly.

I will look at your other requirements tomorrow but it's a bit like trying to hit a moving target :slight_smile:


Fair point, sorry I came across that way! I looked through your last table as well but wasn't certain whether that was the final version since you mentioned it you didn't have time to test it out yet. For all intents & purposes, it is the closest I've seen to a perfect solution for all my requests. It's almost unnerving the way the table adapts across different browsers & their various scrollbars.

The only notable drawback—aside from cloning the table twice—I've noticed the scrollbar continues to remain visible in a disabled state when there are not enough rows for the table to scroll. This request has existed since the original post because a disabled scrollbar doesn't have much purpose & detracts visually from an otherwise normal table. To be honest, I'm on the verge of considering to use one of these jQuery scrollbars purely because of the functional value you've added to the table already, despite the visible scrollbar.

My last post was concerning your previous table (using 1 clone) and about going down a somewhat different route for it. I love the simplicity of its execution was wondering if it could be worth modifying one of my original requests for the sake of easier code readability & easier maintenance in the long-run. I thought having the scrollbar outside the table (contrary to my request) would make it substantially easier to code so I was willing to run with it along with a few more tweaks that would make it seem like the scrollbar was within the table itself. In addition, I threw in a link to another demo which does a nifty job of tackling this but with its own drawbacks, perhaps it could more useful to you for comparison.

I really do appreciate the time you and others have put into this thread. I've tried to keep my requests broadly the same from the original post with other tweaks/suggestions to make things simpler when possible. If something isn't feasible, please let me know & I'll try to adjust accordingly. Thank you :smiley:

Easily Read Table

As I mentioned at the start all these other methods remove the thead and tfoot content from the table and then continually monitor the resize and scroll events to resize them to match the content and even with throttling this produces an effect that in not entirely smooth unlike my demos.

You can see this when you resize and the headers and footers get left behind.

All these js plugins suffer from the same problems but of course without cloning the whole table as in my example it becomes impossible to do othewise. It's a trade-off that has to be made.

I believe I mentioned in one of the other threads also that it would be wise to check the height of the table before adding the fixed headers. It shouldn't be too hard to test if the table has more then n number of rows and if so just exit and leave the table alone.

This is a question that leads to many more questions and a container has no height unless you have set it somewhere so it seems you are moving the requirement of height on the table to a height on the container that holds the table. If you are talking about flex containers then their height is based on their content and that means elements would not scroll unless you added a height somewhere. So in effect you are just moving height around from one place to another.

If you are trying to match a height of a column with another column then yes flex will do this but it will not contain the content so that a scrollbar would appear unless you limited its height once again which brings us back to setting a height on the table to start with.

You would need to show me the complete construct of where you want this table placed so that all the dynamics can be taken into account. In the end though an element cannot scroll unless its height is limited in some way and that can only be done by setting a height or in the case of absolutely placed elements supplying both a top and a bottom.

I will look at detecting the height of the table before applying the fixed header and footers which will solve the stretch rows issue and the scrollbar appearance. However, the fact remains that for the body to have the scrollbar only can only be done as in my last example with 2 clones which does exactly what was asked of it.:slight_smile:


I've added a check to make sure the table is at least 300px tall before the fixed headers appear.

I've applied this fix to the single clone version only.

The scrollbar was set to auto so will disappear also when not needed.


Very good point, I should have added a link to it much earlier:

EDIT: How do I embed/preview the CodePen directly in my post?


Pasting the URL should do it.


Thanks! Although, it's a bit weird that the dedicated URL for embedding CodePen (in HTML or iFrame) didn't do the trick, meaning I can't adjust the size of the preview… Oh well, it'll do!


As I suspected you've merely moved the height restriction higher up the dom so you can still get the table to scroll and stretch using the 100% of available space instead of a fixed 300px.


There are some things that are not possible in this scenario as you need to keep the scrollbar visible or the tables won't match up when there's not enough rows.

Better results could probably be obtained with the 2 clone version but I think the same problem may occur where the header and footer won't match the table if the scrollbar is set to auto rather than always visible.

You have to remember that CSS is basically static not dynamic and if you want different things to happen when certain requirements are met then you have to use JS to provide that functionality. The problem with using JS in a scenario like this is that you have to continually monitor the table in order to make changes to the other elements when required.

In the end everything is a trade-off. :slight_smile: You either sacrifice performance for a better design or you go with a simpler design and better performance.


Very many thanks, I'll have to commit some time to implementing this solution in my flex structure to see it all in action in situ (and finally stop bumping this thread!). Just right off the bat, would it be possible to ensure the table rows don't stretch beyond their regular height when there's not enough rows to fill the table space, as you did before?

The display of the disabled scrollbar is a bit of a thorn among the roses but I can try looking into dynamic JS solutions to address this somehow. Thanks for all your time & help :thumbsup:


I've updated the example to not stretch the rows (although I preferred the stretchy rows).

As I said that would require js listening to the resize event. I've put together an example here:

These are all proof of concepts and you would need to test carefully and generally for modern browsers only. At present the JS is relying on ids so if you wanted this approach for multiple tables the js would need to be refactored to take that into account (possible a job for the JS forum as my js is basic at best).


This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.