It's happened to all of us. After much toil, we've finally finished a website - no, a work of art. Everything runs perfectly - animations are smooth, the layout is robust, and everything looks polished, perfect... but then you open it in Edge.

Your stomach drops - It looks awful. This isn't the site you worked tirelessly to build. Everything's broken! The animations are wrong, the layout is all over the place, and everything looks terrible.

You've fallen victim to cross-browser compatibility.

Although it might seem you need to go back tear everything down and prepare for a massive rewrite, it turns out that debugging cross-browser bugs is easier than you might think.

Cross-browser bugs aren't particularly special, and manifest as any other bug would, resulting in thrown errors, or unexpected behaviour. Since they occur between browsers though, they are always caused by one of two things:

  1. You're using functionality that doesn't exist in the browser.
  2. You're using functionality that is different in the browser.

Discover the bug

Despite the fanciful introduction where all your bugs jump out at you, it's likely that many of your browser bugs aren't immediately obvious upon looking at your website - so you need to do some testing.

The best way to discover bugs in specific browsers is to use those browsers. You should poke around your website on each browser you intend to support, and make sure that everything works correctly. If you find a bug, you should either make a note of it to come back to later, or you can attempt to delve into it right away.

Ideally, you'd perform all your testing on real devices, but it can be unrealistic to acquire all the versions of devices you might need. Instead, you should test on all the devices you have, and then use an emulator or a service like BrowserStack to check the rest.

Which browsers should you test?

Simply put, test what you're going to support. If you don't have any other restriction (like a client who insists on using IE11), a good baseline is the 3 latest versions of major browsers. This will give you a nice tradeoff between compatibility and all the latest browser technology.

Isolate the bug

Great! You've found a bug! The next step is to find out where it is, and what's going wrong.

Any cross-browser issues can usually be traced to a single incompatibility - you'll have to rely on standard debugging techniques here: debugger statements, or working with the browser's Developer Tools (they all have them - even Internet Explorer has decent debugging tools).

How to fix it

In every case, cross-browser bugs require a bit of research and experimentation. You should check resources like the MDN and caniuse.com to check for compatibility, and known cross-browser issues.

The functionality doesn't exist

Unfortunately, not all features are supported in all browsers. If you're using a piece of functionality that doesn't exist in the browser you're testing, there are still things you can do.

If the problem is in JavaScript, your best bet is to try and find a polyfill - this is a piece of code that will replace the functionality that doesn't exist. This isn't flawless (you can't polyfill keywords), but it will let you modify API functions seamlessly.

If you can't polyfill, the next approach is to use a different, 'good enough' replacement for the unsupported functionality.

In CSS, this is done by providing two values. When CSS encounters a value that is doesn't recognise, it ignores it – or if it encounters the same value twice, it will use the second. This means we can provide fall-back values:

.grid {
	/* In browsers that support display: grid, this is overridden */
	display: block;

	/* In browsers that don't support display: grid, this is ignored */
	display: grid;
}

In JavaScript, the best approach to create backup functionality is to use feature-sniffing. This works by testing that a feature exists before attempting to use it. It's possible to perform these tests yourself, but it's better to use a library like Modernizr.

if (Modernizr.webgl) {
	// this browser supports WebGL
	let ctx = canvas.getContext('webgl');
	draw3d(ctx);
} else {
	// we don't have WebGL support, so we perform a 2d drawing
	let ctx = canvas.getContext('2d');
	draw2d(ctx);
}

The functionality is inconsistent

In my mind, what's more annoying than functionality not existing is functionality that doesn't work the same. Unfortunately, browsers are seperate projects developed by seperate teams, so inconsistencies in certain APIs are inevitable.

Inconsistent behaviour tends to stem from some browsers being more flexible about what they'll accept (Firefox and Chrome are generally very flexible), and some browser being more restrictive (Safari and Internet Explorer come to mind).

Thankfully, because of flexible browsers, your fix is usually to change your approach to match the restrictive browser's version of the behaviour - Flexible browsers like Chrome and Firefox will usually handle this version too.

One that I've dealt with in the past is that Internet Explorer doesn't acknowledge 0 in it's flex-basis property without a unit.

.flex-container {
	flex-basis: 0; /* Breaks in IE */
	flex-basis: 0px; /* Works in all browsers */
}

Wrap Up

Cross-browser issues aren't much different than normal bugs - in fact, they're often bugs that come with a pre-existing guide on how to fix them!

The important piece of advice is to be sure to look for these cross-browser issues - they are entirely capable of turning your project from a finely tuned machine into a smouldering pile of mess - and you'll have no idea!