- Understanding Shims
- Why Shims Matter
- Implementing Shims
- Testing and Debugging Shims
- Advanced Shim Implementation Techniques
- Ensuring Compatibility with CSS
- Testing and Debugging Shims
- Handling Cross-Browser Compatibility for Modern JavaScript Features
- Ensuring Cross-Browser Compatibility for APIs
- Ensuring Compatibility with CSS
- Ensuring Compatibility with Media Queries
- Conclusion
Ensuring that a website works smoothly across all major browsers can be a challenging task. Different browsers interpret HTML, CSS, and JavaScript in slightly different ways, leading to inconsistencies in functionality and appearance. Shims are one of the tools that developers can use to bridge the gap between modern web standards and older browsers that may not support these standards. This article will explore how to effectively use shims to achieve cross-browser compatibility, providing you with actionable steps to ensure your website performs consistently for all users.
Understanding Shims

Shims are scripts that provide fallback functionality for older browsers that do not support certain JavaScript features. Unlike polyfills, which add support for newer features, shims emulate functionality, making them a useful tool for dealing with legacy browsers.
By implementing shims, you can ensure that users on older browsers still experience the essential features of your website without significant degradation.
Why Shims Matter
In the modern web development landscape, ensuring that your website works across all browsers is crucial for providing a good user experience. Users may access your site from various browsers and devices, each with its own set of capabilities.
Shims help bridge the gap between these differences, ensuring that your website remains functional and accessible to a broader audience.
Implementing Shims
To effectively use shims, you need to identify the features that are not supported by older browsers and implement the appropriate shims to provide the necessary functionality. Here are some common scenarios where shims can be useful:
HTML5 Elements
HTML5 introduced several new elements, such as <section>
, <article>
, <nav>
, and <header>
. Older versions of Internet Explorer, however, do not recognize these elements, which can lead to styling and scripting issues. You can use a shim to make these elements recognizable in older browsers.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HTML5 Shim Example</title>
<!-- HTML5 Shiv Shim for IE -->
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
<![endif]-->
</head>
<body>
<header>
<h1>Welcome to My Website</h1>
</header>
<section>
<article>
<p>This is an article.</p>
</article>
</section>
</body>
</html>
In this example, the HTML5 Shiv is used to make HTML5 elements recognizable in older versions of Internet Explorer.
JavaScript Methods
Newer JavaScript methods, such as Array.prototype.forEach
, Array.prototype.map
, and Function.prototype.bind
, are not supported in older browsers like Internet Explorer 8. You can use shims to provide these methods in browsers that do not support them.
// Shim for Array.prototype.forEach
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback, thisArg) {
if (this == null) {
throw new TypeError('Array.prototype.forEach called on null or undefined');
}
var T, k;
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
if (arguments.length > 1) {
T = thisArg;
}
k = 0;
while (k < len) {
var kValue;
if (k in O) {
kValue = O[k];
callback.call(T, kValue, k, O);
}
k++;
}
};
}
This shim adds the forEach
method to the Array
prototype if it does not already exist, ensuring that the method is available in all browsers.
CSS Properties
Some CSS properties and values introduced in recent specifications are not supported by older browsers. While vendor prefixes can handle many of these issues, shims can also be used to provide fallback functionality.
For example, CSS Grid Layout is not supported in older versions of Internet Explorer. A shim can help provide basic grid functionality in these browsers.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS Grid Shim Example</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="grid-container">
<div class="grid-item">Item 1</div>
<div class="grid-item">Item 2</div>
<div class="grid-item">Item 3</div>
</div>
<script src="grid-shim.js"></script>
</body>
</html>
In this example, the grid-shim.js
script would contain JavaScript code to emulate basic grid functionality for older browsers that do not support CSS Grid.
Testing and Debugging Shims

To ensure that your shims are working correctly, it’s essential to test your website across different browsers. Here are some steps to help you test and debug your shims effectively:
Use Cross-Browser Testing Tools
Cross-browser testing tools, such as BrowserStack, Sauce Labs, and CrossBrowserTesting, allow you to test your website on various browsers and devices. These tools can help you identify issues with your shims and ensure that your website functions correctly across all supported browsers.
Browser Developer Tools
Most modern browsers come with built-in developer tools that can help you debug your website. Use these tools to inspect the DOM, debug JavaScript, and analyze network requests. This can help you identify issues with your shims and fix them promptly.
Automated Testing
Automated testing can help you catch issues early in the development process. Use tools like Selenium, Cypress, and Puppeteer to create automated tests that verify the functionality of your shims across different browsers.
Advanced Shim Implementation Techniques
Creating Custom Shims
Sometimes, you may need to create custom shims to support features specific to your application. Creating custom shims involves writing JavaScript that mimics the behavior of modern features. This can be particularly useful when dealing with bespoke functionality not covered by existing shims.
For instance, suppose your application relies on the Element.closest()
method, which is not supported in older browsers like Internet Explorer. You can create a custom shim to provide this method:
// Shim for Element.closest
if (!Element.prototype.closest) {
Element.prototype.closest = function(selector) {
var element = this;
while (element && element.nodeType === 1) {
if (element.matches(selector)) return element;
element = element.parentElement || element.parentNode;
}
return null;
};
}
This shim checks if the Element.closest()
method exists and defines it if it doesn’t, ensuring compatibility with older browsers.
Using Libraries for Shimming
There are several libraries available that provide a comprehensive set of shims for various features. Using these libraries can save time and ensure that your shims are well-tested and maintained. Some popular shim libraries include:
ES5-Shim
ES5-Shim provides compatibility shims for ECMAScript 5 methods that are missing in older JavaScript engines. It helps bridge the gap between modern JavaScript and older browsers.
<!-- Include ES5-Shim for older browsers -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.10/es5-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.10/es5-sham.min.js"></script>
By including ES5-Shim, you ensure that methods like Array.prototype.map
and Function.prototype.bind
are available in all browsers.
HTML5 Shiv
HTML5 Shiv (or HTML5Shim) is a popular script for enabling HTML5 elements in older versions of Internet Explorer. It allows these browsers to recognize and style new HTML5 elements.
<!-- Include HTML5 Shiv for IE -->
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
<![endif]-->
This script ensures that HTML5 elements are styled and scripted correctly in older browsers.
Polyfill.io
Polyfill.io is a service that detects the features a user’s browser needs and dynamically loads the appropriate polyfills. It provides a wide range of polyfills and shims for modern web features.
<!-- Include Polyfill.io -->
<script src="https://cdn.polyfill.io/v3/polyfill.min.js"></script>
Polyfill.io automatically serves the necessary polyfills based on the user’s browser, simplifying the process of ensuring cross-browser compatibility.
Ensuring Compatibility with CSS
Handling CSS Grid and Flexbox

CSS Grid and Flexbox are powerful layout systems that may not be fully supported in older browsers. Using shims or fallback techniques can help ensure that your layouts remain functional.
Flexibility (Flexbox Polyfill)
Flexibility is a polyfill that provides support for the Flexbox layout in older browsers like Internet Explorer 9. It uses JavaScript to emulate the behavior of Flexbox, ensuring compatibility.
<!-- Include Flexibility for Flexbox support -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/flexibility/2.0.1/flexibility.js"></script>
<script>
// Apply Flexibility to the entire document
flexibility(document.documentElement);
</script>
This polyfill ensures that your Flexbox layouts work consistently across all browsers.
CSS Grid Fallbacks
For CSS Grid, providing fallbacks can be more complex. One approach is to use feature queries to apply different styles based on support for CSS Grid.
/* Grid Layout */
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 20px;
}
/* Flexbox Fallback */
@supports not (display: grid) {
.container {
display: flex;
flex-wrap: wrap;
}
.container > div {
flex: 1 1 30%;
margin: 10px;
}
}
By using feature queries, you can provide a Flexbox fallback for browsers that do not support CSS Grid, ensuring that your layout works in older browsers.
Testing and Debugging Shims
Cross-Browser Testing Tools
Using cross-browser testing tools can help you identify and fix compatibility issues early in the development process. These tools allow you to test your website on a wide range of browsers and devices.
BrowserStack
BrowserStack provides real-time access to various browsers and devices, enabling you to test your shims and polyfills across different environments. You can see how your website behaves on older browsers and identify any issues.
Sauce Labs
Sauce Labs offers automated testing on a cloud-based platform, allowing you to run tests on multiple browsers simultaneously. This can help you catch compatibility issues and ensure that your shims work as expected.
Browser Developer Tools
Most modern browsers come with built-in developer tools that allow you to debug and inspect your code. These tools can help you understand how your shims are being applied and identify any issues.
Inspecting the DOM
Use the Elements panel in browser developer tools to inspect the DOM and see how HTML5 elements and CSS properties are being rendered. This can help you identify any issues with your shims and fix them promptly.
Debugging JavaScript
Use the Console and Sources panels to debug JavaScript and see how your shims are being executed. You can set breakpoints, step through code, and analyze network requests to identify and fix issues.
Automated Testing
Automated testing can help you catch compatibility issues early and ensure that your shims are working correctly. Use tools like Selenium, Cypress, and Puppeteer to create automated tests that verify the functionality of your shims across different browsers.
Selenium
Selenium is a popular tool for automating web browsers. It allows you to create tests that interact with your website and verify that it behaves as expected.
const {Builder, By, Key, until} = require('selenium-webdriver');
(async function example() {
let driver = await new Builder().forBrowser('firefox').build();
try {
await driver.get('http://www.example.com');
await driver.findElement(By.name('q')).sendKeys('shim example', Key.RETURN);
await driver.wait(until.titleIs('shim example - Google Search'), 1000);
} finally {
await driver.quit();
}
})();
Cypress
Cypress is a modern end-to-end testing framework that provides a fast and reliable way to test your website. It allows you to write tests in JavaScript and run them in a real browser environment.
describe('My First Test', () => {
it('Visits the Kitchen Sink', () => {
cy.visit('https://example.cypress.io')
cy.contains('type').click()
cy.url().should('include', '/commands/actions')
cy.get('.action-email').type('fake@email.com').should('have.value', 'fake@email.com')
})
})
Handling Cross-Browser Compatibility for Modern JavaScript Features

ES6 and Beyond
The introduction of ES6 (ECMAScript 2015) and subsequent versions brought a plethora of new features and syntax to JavaScript, enhancing the language’s capabilities. However, not all browsers support these modern features natively. Shims and polyfills play a crucial role in bridging this gap.
Promises
Promises are a key feature of modern JavaScript, used to handle asynchronous operations. Older browsers like Internet Explorer do not support promises natively. You can use a shim to add this functionality.
// Promise shim
if (typeof Promise !== "function") {
window.Promise = function(executor) {
var callbacks = [];
var isResolved = false;
var isRejected = false;
var value;
this.then = function(onResolved, onRejected) {
return new Promise(function(resolve, reject) {
handle({
onResolved: onResolved || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
};
this.catch = function(onRejected) {
return this.then(null, onRejected);
};
function handle(callback) {
if (isResolved) {
callback.onResolved(value);
} else if (isRejected) {
callback.onRejected(value);
} else {
callbacks.push(callback);
}
}
function resolve(newValue) {
isResolved = true;
value = newValue;
callbacks.forEach(function(callback) {
callback.onResolved(value);
});
}
function reject(newValue) {
isRejected = true;
value = newValue;
callbacks.forEach(function(callback) {
callback.onRejected(value);
});
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
};
}
This shim provides basic promise functionality, allowing you to use promises in browsers that do not support them natively.
Fetch API
The Fetch API is a modern replacement for XMLHttpRequest, providing a more powerful and flexible way to make HTTP requests. If you need to support older browsers, you can use a polyfill for the Fetch API.
// Include the Fetch polyfill
if (!window.fetch) {
document.write('<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.4/fetch.min.js"><\/script>');
}
// Example usage of Fetch
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Including this polyfill ensures that your application can make HTTP requests in browsers that do not support the Fetch API.
Using Babel for Modern JavaScript
Babel is a popular JavaScript compiler that allows you to write modern JavaScript code while ensuring compatibility with older browsers. It transpiles your ES6+ code into ES5, which is widely supported.
Setting Up Babel
To set up Babel in your project, you can install it via npm and configure it with a .babelrc
file.
# Install Babel and necessary presets
npm install --save-dev @babel/core @babel/preset-env babel-loader
Create a .babelrc
file to configure Babel:
{
"presets": ["@babel/preset-env"]
}
Configure Webpack to use Babel:
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
}
};
With this setup, Babel will automatically transpile your modern JavaScript code into a format compatible with older browsers.
Ensuring Cross-Browser Compatibility for APIs

Geolocation API
The Geolocation API allows you to access the geographical location of a user’s device. While modern browsers support this API, older browsers may not. You can use a shim to provide fallback functionality.
// Geolocation shim
if (!navigator.geolocation) {
navigator.geolocation = {
getCurrentPosition: function(success, error) {
error(new Error("Geolocation not supported"));
},
watchPosition: function(success, error) {
error(new Error("Geolocation not supported"));
}
};
}
// Example usage of Geolocation API
navigator.geolocation.getCurrentPosition(
function(position) {
console.log('Latitude:', position.coords.latitude);
console.log('Longitude:', position.coords.longitude);
},
function(error) {
console.error('Error:', error.message);
}
);
This shim provides basic fallback functionality for the Geolocation API, ensuring that your application can handle cases where the API is not supported.
Local Storage
Local storage allows you to store data on the client’s browser. While widely supported, some older browsers may not fully implement the local storage API. A shim can help ensure compatibility.
// Local storage shim
if (!window.localStorage) {
Object.defineProperty(window, "localStorage", new (function () {
var aKeys = [], oStorage = {};
Object.defineProperty(oStorage, "getItem", {
value: function (sKey) { return sKey ? this[sKey] : null; },
writable: false,
configurable: false,
enumerable: false
});
Object.defineProperty(oStorage, "key", {
value: function (nKeyId) { return aKeys[nKeyId]; },
writable: false,
configurable: false,
enumerable: false
});
Object.defineProperty(oStorage, "setItem", {
value: function (sKey, sValue) {
if(!sKey) { return; }
document.cookie = escape(sKey) + "=" + escape(sValue) + "; path=/";
},
writable: false,
configurable: false,
enumerable: false
});
Object.defineProperty(oStorage, "length", {
get: function () { return aKeys.length; },
configurable: false,
enumerable: false
});
Object.defineProperty(oStorage, "removeItem", {
value: function (sKey) {
if(!sKey) { return; }
document.cookie = escape(sKey) + "=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT";
},
writable: false,
configurable: false,
enumerable: false
});
Object.defineProperty(oStorage, "clear", {
value: function () {
if (!aKeys.length) { return; }
for (var sKey in aKeys) {
document.cookie = escape(aKeys[sKey]) + "=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT";
}
aKeys = [];
},
writable: false,
configurable: false,
enumerable: false
});
this.get = function () {
var iThisIndex;
for (var sKey in document.cookie.split(/\s*;\s*/)) {
iThisIndex = sKey.indexOf("=");
aKeys.push(unescape(sKey.substring(0, iThisIndex)));
oStorage[unescape(sKey.substring(0, iThisIndex))] = unescape(sKey.substring(iThisIndex + 1));
}
return oStorage;
};
this.configurable = false;
this.enumerable = true;
})());
}
// Example usage of local storage
localStorage.setItem('key', 'value');
console.log(localStorage.getItem('key'));
This shim uses cookies to emulate local storage functionality, ensuring that your application can store data on older browsers.
Ensuring Compatibility with CSS
CSS Variables
CSS variables, also known as custom properties, allow you to define variables in CSS and reuse them throughout your stylesheet. However, older browsers do not support CSS variables. A shim can help provide fallback functionality.
:root {
--main-color: #3498db;
}
.element {
color: var(--main-color);
color: #3498db; /* Fallback for older browsers */
}
By including a fallback value, you ensure that older browsers can still render the correct styles.
CSS Grid
CSS Grid Layout is a powerful tool for creating complex layouts. However, it is not supported in older browsers like Internet Explorer 10 and 11. You can use a polyfill to provide basic grid functionality.
Grid Polyfill
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS Grid Polyfill</title>
<link rel="stylesheet" href="styles.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/css-polyfills/1.0.2/grid-polyfill.min.js"></script>
</head>
<body>
<div class="grid-container">
<div class="grid-item">Item 1</div>
<div class="grid-item">Item 2</div>
<div class="grid-item">Item 3</div>
</div>
</body>
</html>
This polyfill ensures that your grid layouts work consistently across all browsers.
Ensuring Compatibility with Media Queries
Responsive Design
Responsive design is essential for creating websites that work well on different devices. Media queries allow you to apply CSS rules based on the characteristics of the device, such as its width and orientation.
Media Query Shim
Older browsers may not support media queries. A shim can help provide fallback functionality for these browsers.
<!-- Include the Respond.js library for media query support in IE8 and below -->
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
Respond.js is a popular library that provides support for CSS3 media queries in Internet Explorer 8 and below.
Testing Media Queries
Testing your responsive design across different devices and screen sizes is crucial for ensuring compatibility. Use tools like BrowserStack and CrossBrowserTesting to see how your site looks on various devices.
Additionally, perform manual testing on real devices to catch any issues that automated tests might miss.
Conclusion
Using shims for cross-browser compatibility is essential for ensuring that your website works consistently across all major browsers. By understanding how to implement and test shims effectively, you can bridge the gap between modern web standards and older browsers, providing a seamless experience for all users. This involves creating custom shims, using libraries, handling CSS compatibility, and leveraging automated testing tools. With these strategies, you can confidently develop websites that are accessible, functional, and performant for everyone.
Read Next: