A Look Into: HTML5 Shiv and Polyfills
Updates: Loading Polyfills is no longer a common practice to provide compatibility. You will find that a few of these repository linked on this article are no longer actively maintained. You can head over to the last section, on Browserlist and Babel, on how web developers handle browser compatibility these days.
In our previous post we have discussed a few new HTML5 elements that are already widely used, namely header
, nav
, footer
and article
. Furthermore, if we give styles to these elements then view them in a modern browser, we should see the result of the styles.
However, this is not the case when we view them in Internet Explorer 8 and earlier (which we refer to as ‘old browsers’), somehow the styles do not seem to apply.
HTML5 Basic Elements: Header, Nav, and Footer
HTML5 is still evolving, yet some of its new elements are ready to use today and are supported... Read more
Let’s see the example below:
This thing happens because old browsers were not built for HTML5 and therefore they don’t recognize the elements, hence the styles won’t be applied as well.
How to Solve This Problem
We can use this little script document.createElement(elementName)
which was initially mentioned by Sjoerd Visscher in his comment. Now, let’s say we want the old browsers to recognize the header
element, we can write:
document.createElement('header')
Then, when we view it in Internet Explorer
The header
element now has the styles applied, whilst the other elements remain unstyled. This script will also apply to a non-sense element, for example;
document.createElement('foo');
The script above will make Internet Explorer recognize the foo
element even when there is no such element in HTML5. As for the other elements in our page, we can copy and paste the script and specify the elements we need to patch, but of course doing so is unnecessary as it has already been done with HTML5shiv.
HTML5 Shiv
The script above was actually the inspiration behind the HTML5shiv development.
HTML5shiv is a library to enable all new HTML5 elements and sectioning in the old browsers. We only need to link this library to our document and put it inside Internet Explorer conditional comments, as follows;
<!--[if lt IE 9]> <script type="text/javascript" src="html5shiv.js"></script> <![endif]-->
That way, the library will only be loaded in Internet Explorer 8 and below, as the other browsers won’t need it. Now, when we view our webpage in IE, all the elements should have their styles.
Polyfills
We have covered about Polyfills several times through our HTML5 tutorials. This term was initially coined by Remy Sharp, he described it a: “A piece of code (or plugin) that provides the technology that you, the developer, expect the browser to provide natively.”
As we can see from his description, Polyfills basically have the same purpose as HTML5shiv, but in this case, Polyfills will also mimic the functionality of the elements.
There are already a bunch of Polyfills for every new browser feature, here is the complete list. And, the best way to use Polyfills is to load it along with Modernizr, let’s see the following example;
Modernizr.load({ test: Modernizr.placeholder, nope: 'placeme.js' });
Modernizr, in this example, tests the placeholder
feature and will deliver the Polyfills, only if it turns out the browser does not support the specified feature.
Recommended Reading: How to use HTML5 <details> and <summary> element
Using Browserlist and Babel
Most of Internet users may now be using an evergreen modern browser that adding polyfills to shim some of the HTML5 or CSS3 feature is rarely needed these days. Still we sometimes need to support some older version of the browsers. But rather than creating and loading multiple polyfill scripts, we are now able to convert new our code to be compatible with some of the older browser using Babel and Browserlist.
The first thing you need to have installed on your computer are Node.js and NPM, and intalling a few NPM modules namely @babel/cli
, @babel/core
, @babel/plugin-transform-runtime
, @babel/preset-env
, and @babel/runtime
.
Then, add the browserlist
configuration to the package.json
file. Below we expect to support browser with at least 2% usage, Internet Explorer 11, and Safari 9 or the latest.
{ "name": "bable-example", "version": "0.0.1", "devDependencies": { "@babel/cli": "^7.8.3", "@babel/core": "^7.8.3", "@babel/plugin-transform-runtime": "^7.8.3", "@babel/preset-env": "^7.8.3" }, "browserslist": "> 2%, ie 11, safari >= 9", "dependencies": { "@babel/runtime": "^7.8.3" } }
Now assuming we have written a modern syntax such as the const
keyword, the arrow function, which will only work in modern browsers.
const foo = [ 'bar', 'baz' ]; foo.forEach((val) => console.log(val));
We can run this command below:
npx babel script.js --out-file script-compiled.js
The command will create a new file of our script.js
to script-compiled.js
and convert the script to:
var foo = ['bar', 'baz']; foo.forEach(function (val) { return console.log(val); });
Let’s get a more complex example with defining an async function in JavaScript, for example:
async function getData() { console.log('1'); await fetch('http://localhost:3000/foo') .then(resp => resp.json()) .then(data => console.log(data)); console.log('2'); }
Babel will compile this code into so it could work in several other browsers that will work in some older browsers:
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); function getData() { return _getData.apply(this, arguments); } function _getData() { _getData = (0, _asyncToGenerator2["default"])( /*#__PURE__*/ _regenerator["default"].mark(function _callee() { return _regenerator["default"].wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: console.log('1'); _context.next = 3; return fetch('http://localhost:3000/foo').then(function (resp) { return resp.json(); }).then(function (data) { return console.log(data); }); case 3: console.log('2'); case 4: case "end": return _context.stop(); } } }, _callee); })); return _getData.apply(this, arguments); }
Final Thoughts
Web development has so much changed since I got started about 10 years ago. As we built dynamic apps not just plain static pages, the way we need to craft the Web has also evolved to follow the needs.
Here we’ve seen how Babel could translate of a modern code to be compatible with an old browser. In the real project, you will usually integrate Babel to a build tool like Webpack, Parcel, Rollup, and you can check out the following reference for more on using Babel.