Responsive scrollable tables

Over six years ago I wrote a short post about preventing HTML tables from becoming too wide. The solutions I offered in that post involve using table-layout:fixed to lock down the width of tables. While that may work in some cases, it often will not be very pleasant to use.

Since I wrote that post, many, many small screen devices that you can use to browse the web have been released, which means that the risk of people encountering a data table that is too wide to fit their browser window is greater than ever. There are already several different strategies for dealing with data tables on small screens, involving things like flipping tables, hiding columns, rearranging data cells, and making over-wide tables scrollable. See Responsive Data Table Roundup at CSS-Tricks for some examples.

Every technique for making tables flexible (or “responsive”) that I have seen comes with its own set of drawbacks. That’s expected really—I don’t think this is a problem that can be solved perfectly, so we have to compromise somehow. But I do think that one of the simplest and least inelegant ways to handle data tables is to make them horizontally scrollable when necessary, and so I thought I’d describe how I do that.

In case the following seems familiar, it may be because Russ Weakley describes a pretty similar technique in A simple (and very rough) responsive table solution. The concept is the same, but some details differ.

To see the end result, take a look at the Responsive scrollable tables demo page.

CSS

To be able to scroll the entire table you need to put it inside a container element with overflow scrolling enabled.

The CSS that creates the scrollbar is very simple. If you don’t want or need anything more than the scrollbar as a visual indicator of scrollability, you could get away with this:

.scrollable {
	overflow-x:auto;
}

I personally think doing a bit more than that both makes it easier to realise that the entire table is not visible and makes it look better. There are many ways you can do that, but in the demo I’ve used a rotated version of the pseudo-element technique I describe in Responsive drop shadows. To make this work you need an extra wrapper element and an overflow:hidden declaration on the outer wrapper, making the entire CSS look like this:

.scrollable.has-scroll {
	position:relative;
	overflow:hidden; /* Clips the shadow created with the pseudo-element in the next rule. Not necessary for the actual scrolling. */
}
.scrollable.has-scroll:after {
	position:absolute;
	top:0;
	left:100%;
	width:50px;
	height:100%;
	border-radius:10px 0 0 10px / 50% 0 0 50%;
	box-shadow:-5px 0 10px rgba(0, 0, 0, 0.25);
	content:'';
}
/* This is the element whose content will be scrolled if necessary */
.scrollable.has-scroll > div {
	overflow-x:auto;
}

Feel free to change the shadow as you see fit, or replace it with something completely different.

Since scrollbars are not very visible until you scroll in iOS and Android WebKit browsers (and on OS X depending on user settings), it may be a good idea to make them explicitly visible so users will notice that they can scroll the table. We can do this with some WebKit-only CSS:

.scrollable > div::-webkit-scrollbar {
	height:12px;
}
.scrollable > div::-webkit-scrollbar-track {
	box-shadow:0 0 2px rgba(0,0,0,0.15) inset;
	background:#f0f0f0;
}
.scrollable > div::-webkit-scrollbar-thumb {
	border-radius:6px;
	background:#ccc;
}

JavaScript

While this technique is mostly CSS-based and could be used with CSS alone, I’ve added a bit of JavaScript.

Sometimes you can have full control of the markup used for data tables, but it can be tricky for tables created by a WYSIWYG editor in a CMS, for example. So instead of relying on the wrapping div elements being in the markup I add them with a bit of JavaScript (jQuery-flavoured in this example, but it could be re-written in plain JavaScript, or turned into a jQuery plugin):

// Run on window load instead of on DOM Ready in case images or other scripts affect element widths
$(window).on('load', function() {
	// Check all tables. You may need to be more restrictive.
	$('table').each(function() {
		var element = $(this);
		// Create the wrapper element
		var scrollWrapper = $('<div />', {
			'class': 'scrollable',
			'html': '<div />' // The inner div is needed for styling
		}).insertBefore(element);
		// Store a reference to the wrapper element
		element.data('scrollWrapper', scrollWrapper);
		// Move the scrollable element inside the wrapper element
		element.appendTo(scrollWrapper.find('div'));
		// Check if the element is wider than its parent and thus needs to be scrollable
		if (element.outerWidth() > element.parent().outerWidth()) {
			element.data('scrollWrapper').addClass('has-scroll');
		}
		// When the viewport size is changed, check again if the element needs to be scrollable
		$(window).on('resize orientationchange', function() {
			if (element.outerWidth() > element.parent().outerWidth()) {
				element.data('scrollWrapper').addClass('has-scroll');
			} else {
				element.data('scrollWrapper').removeClass('has-scroll');
			}
		});
	});
});

Besides not having to put the wrapper elements in the markup, doing it this way lets you use different styling when tables actually can be scrolled. The most obvious example in my demo is the shadow that appears on the right hand side of the table when it is scrollable.

Note that this function isn’t limited to tables. You can apply this to any element that potentially will be wider than its parent. A possible use case would be figures with images that you don’t want to scale down.

The downside to using JavaScript for this is of course that if JavaScript is off, tables won’t be scrollable and users will have to scroll the entire page horizontally, just like without this. For tables that you can control the markup of (and around), you can add the wrapper elements to the source and rewrite the script to avoid inserting the wrappers if they already exist, giving non-JS users almost the same result—the difference being that the styling will be the same even if the table doesn’t actually need to be scrolled.

Browser support

This technique works in all desktop browsers I have tested in, down to IE7. Obviously the shadow won’t appear in IE8 and below due to lack of support for box-shadow and border-radius (and in the case of IE7, lack of support for the :before and :after pseudo-elements). There will still be a horizontal scrollbar though, so I think that’s acceptable.

It also works in newer mobile browsers, but in some older mobile browsers (iOS 4 and Android 2 for instance) support for overflow scrolling isn’t quite up to it. If that is a concern you may want to use something like Modernizr to check for support before applying the CSS.

Not just for small screens

While this technique may be especially useful on small screen devices, there is no reason to limit the use to touch devices or devices with screen (or window) sizes below a certain width. Users of desktop browsers benefit from this too—remember that there are plenty of people who do not have a large widescreen monitor and maximise their browser window. And sometimes data tables are just too large to fit inside your layout.

Posted on September 21, 2013 in CSS, JavaScript