What is Regressive Enhancement?
You’re probably already familiar with the concept of progressive enhancement: designing a baseline version of a feature that works in all browsers, then adding features for more capable browsers. The “polyfill” or “regressive enhancement” technique just means that you go ahead and use the new features, then use JavaScript to emulate native behavior in older browsers. So, instead of using a script to give you drop shadows on all browsers, you just write your CSS with thebox-shadow
property, and then include a script that transparently takes that property and uses the values you specify to create a drop shadow in JavaScript.
What is Modernizr?
For those of you who aren’t familiar with it, Modernizr is a small (3.7KB gzipped) JavaScript library that detects the presence of HTML5 and CSS3 features in the browser. Raena made use of it in her recent tutorial on creating a progressively enhanced image gallery, and Kevin interviewed Paul Irish, one of the library’s creators, in a recent episode of the SitePoint pocast. Modernizr is ideally suited to regressive enhancement, because it allows you to know when you can rely on browser functionality, and when you need to fall back on JavaScript or alternate styling. There are two main ways of using Modernizr. The most common way is to rely on the HTML classes it adds to your html tag. When viewing a page with Modernizr in the latest Firefox 4 beta, here’s what I see in the opening <html> tag:<html class=" js flexbox canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop no-websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity no-cssanimations csscolumns cssgradients no-cssreflections csstransforms no-csstransforms3d csstransitions fontface video audio localstorage no-sessionstorage webworkers applicationcache svg inlinesvg smil svgclippaths" lang="en">
All those classes tell me which features are available in the browser. For example, I have @font-face, web workers, and CSS3 box-shadow, text-shadow, and border-image, but I don’t have websockets or 3D CSS transforms. So, in my CSS, I can do something like this:
.borderradius .box {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
}
.no-borderradius .box {
// fallback code providing an alternate styling to browsers that don't support border-radius
}
That’s simple enough. The cool bit is that Modernizr also provides you with a JavaScript API that can tell you whether or not a feature is available, including a few features that don’t show up in the <html>
tag classes. For example, let’s say I have some JavaScript code that provides placeholder values for input elements. I don’t need to run this code if the browser supports the placeholder attribute, so I can use Modernizr to check for that before executing my snippet:
if(!Modernizr.input.placeholder) {
// custom placeholder code
}
This way, my code will only run if there’s no built-in browser support for placeholder text.
The Problem
There’s still a slight problem here, though. If a browser does support the placeholder attribute, I’m still requiring it to download a bunch of code that does nothing but emulate that attribute’s behavior. I’m sure you’ll agree this is a little wasteful! Enter yepnope.js. Yepnope loads scripts if certain conditions are met. The best part is that it integrates beautifully with Modernizr, so everything just snaps into place. The simplest example, from the library’s website, looks like this:yepnope({
test : Modernizr.geolocation,
yep : 'normal.js',
nope : ['polyfill.js', 'wrapper.js']
});
If the browser supports geolocation, that snippet will load the normal.js file from the server; otherwise, it’ll load both polyfill.js and wrapper.js.
A Practical Example
Now that you know how all the parts work, let’s put them together into a real-world example. Let’s say you have a simple signup form, consisting of fields for a username, password, and email address. Here’s the markup:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>yepnope.js test</title>
<style>
div { margin: 10px; }
div p { font-size: 12px; color: #333; }
</style>
</head>
<body>
<form>
<div>
<label for="username">Username</label>
<input type="text" required pattern="[a-z0-9_-]{3,15}" name="username" id="username">
<p>Between 3 and 15 characters. Only letters, numbers, underscores (_) and hyphens (-) are allowed.</p>
</div>
<div>
<label for="email">Email</label>
<input type="email" required placeholder="someone@somewhere.com" name="email" id="email">
</div>
<div>
<label for="password">Password</label>
<input type="password" required name="password" id="password">
</div>
<input type="submit" value="Submit">
</form>
</body>
</html>
The form uses a number of HTML5 features: the required attribute, the placeholder
attribute, and the pattern
attribute. In a supporting browser, such as Firefox 4 beta, this provides placeholder text and some basic client-side validation:
The placeholder attribute provides placeholder text
The email input type provides built-in format validation
The pattern attribute provides regular expression based validation
In a non-supporting browser, however, you get big fat nothing. Let’s fix this using a JavaScript-based polyfill along with Modernizr, and Yepnope to load it only when it’s required.Step 1: Download Modernizr and Yepnope
The new custom Modernizr builder allows you to bundle yepnope right into Modernizr, so let’s do that. Head over to http://modernizr.github.com/Modernizr/2.0-beta/. In this case all we need to detect are the form attributes and input types, so click those two checkboxes, leaving the HTML5 Shim and Modernizr.load (yepnope.js) boxes ticked. Click Generate, and then Download Build to grab your custom Modernizr library.Step 2: Include Modernizr in Your Page
Because Modernizr needs to determine if other scripts should be run, and adds classes that might be required by your CSS, it should go at the top of your HTML, rather than at the bottom as is usually recommended:<script src="modernizr.custom.02401.js"></script>
(Remember to replace the custom build number with your own.)
Step 3: Test for Feature Support
Now we want to test to see if the browser supports the new input types and attributes:yepnope({
test: Modernizr.inputtypes.email && Modernizr.input.required && Modernizr.input.placeholder && Modernizr.input.pattern,
nope: 'h5f.min.js'
});
We’re loading the H5F library by Ryan Seddon, which emulates all the new input types and attributes we’re using in this example. In this case we have one polyfill script that covers a bunch of different features, so we’re checking for all of them at once and loading the polyfill if even one of them is unsupported. This is not always ideal, but we’re keeping things simple for the sake of illustration.
You’ll also notice we’re not using a “yep” in this yepnope call. That’s fine: in the event all the features we’re detecting for are present, Yepnope won’t do anything at all, which is what we want.
Step 4: Execute Callback Code
You might be tempted to just call your newly included library on the next line of code, but this won’t work. Yepnope.js loads scripts asynchronously , so the browser won’t wait for the script to be finished loading before moving on to the next line in your code. That means that if you try to use the features you’ve just told Yepnope to load, you’ll likely get an error. Instead, Yepnope allows you to set a callback function for each script you load, to be run once that script has finished downloading. Here’s how that works:yepnope({
test: Modernizr.inputtypes.email && Modernizr.input.required && Modernizr.input.placeholder && Modernizr.input.pattern,
nope: 'h5f.min.js',
callback: function(url, result, key) {
H5F.setup(document.getElementById("signup"));
}
});
The function you specify as a callback will be called each time a script is loaded. This means that if you’ve specified both a yep and a nope, the callback will be called twice. Fortunately, the callback is passed three useful parameters: url
is the URL of the result that was loaded, result
is a Boolean value representing whether or not your test passed, and key
is a way of referring to specific resources using keys (you don’t need to worry about this for now).
In the above example, I’m only loading a script on nope. As a result, the callback will only be called once anyway, and only if the test fails, so I don’t need to worry about the parameters.
Step 5: You’re Done!
Believe it or not, you’re done. With the above code in place, browsers that support the new form features will use their built-in functionality, while older browsers will load a JavaScript fallback. The JavaScript will only be loaded in those non-supporting browsers, so you’re rewarding modern browsers with snappier load times. Even better, because the polyfill hooks onto the new attributes and doesn’t require any extra classes, the solution is future-proof. Fewer and fewer visitors over time will download the polyfill, until eventually none of them do.What’s Next?
I’ve only covered the simplest use cases of yepnope,js. For such a tiny library, it packs a lot of functionality, so you should definitely have a read through the project’s page to see some more advanced usage examples. Even if you’re not using it for HTML5 or CSS3 polyfills, there are potential performance gains to be had from loading your scripts asynchronously and on demand, so Yepnope is still worth looking into. Now, all you have to do is start putting new HTML5 and CSS3 features to use on your website, safe in the knowledge that you can provide a full-featured fallback to users of older browsers without impacting the experience of your more up-to-date visitors.Frequently Asked Questions (FAQs) about Regressive Enhancement with Modernizr and Yepnope
What is the purpose of using Modernizr and Yepnope in web development?
Modernizr and Yepnope are powerful tools used in web development to ensure that websites function optimally across different browsers. Modernizr is a JavaScript library that detects HTML5 and CSS3 features in the user’s browser. It helps developers to create fallbacks for older browsers that do not support certain features. On the other hand, Yepnope is a conditional resource loader that allows developers to load scripts or styles based on the results of the tests run by Modernizr. This combination allows developers to create websites that are both forward and backward compatible, ensuring a seamless user experience.
How does regressive enhancement differ from progressive enhancement?
Regressive enhancement is a web development strategy that starts with the most advanced features and then provides fallbacks for older browsers. This is the opposite of progressive enhancement, which starts with the most basic functionality and then adds advanced features for modern browsers. The advantage of regressive enhancement is that it allows developers to take full advantage of the latest web technologies, while still ensuring that the website is usable on older browsers.
How do I use Modernizr and Yepnope in my web project?
To use Modernizr and Yepnope in your web project, you first need to download and include the Modernizr script in your HTML file. You can then use Modernizr to test for specific features in the user’s browser. Based on the results of these tests, you can use Yepnope to conditionally load scripts or styles. For example, if Modernizr detects that the browser does not support CSS3 animations, you can use Yepnope to load a fallback script that provides similar functionality using JavaScript.
Can I customize Modernizr to only test for the features I need?
Yes, Modernizr is highly customizable. You can select only the tests you need on the Modernizr download page. This allows you to keep the size of the Modernizr script to a minimum, which can improve the performance of your website.
What are some common use cases for Modernizr and Yepnope?
Modernizr and Yepnope are commonly used to handle differences in browser support for HTML5 and CSS3 features. For example, they can be used to provide fallbacks for CSS3 animations, HTML5 video, and CSS3 gradients. They can also be used to load polyfills for HTML5 elements like canvas and video, which are not supported in older browsers.
Are Modernizr and Yepnope still relevant with the advent of evergreen browsers?
While it’s true that modern, evergreen browsers have excellent support for HTML5 and CSS3, there are still many users who use older browsers. Therefore, tools like Modernizr and Yepnope remain relevant as they ensure that your website provides a consistent user experience across all browsers.
How can I learn more about using Modernizr and Yepnope?
There are many resources available online to learn about Modernizr and Yepnope. The official websites for both tools provide comprehensive documentation and examples. In addition, there are numerous tutorials and blog posts available that provide step-by-step guides on how to use these tools in your web projects.
Are there any alternatives to Modernizr and Yepnope?
Yes, there are several alternatives to Modernizr and Yepnope. For feature detection, you can use tools like Feature.js or Can I Use. For conditional resource loading, you can use tools like RequireJS or LoadJS. However, Modernizr and Yepnope remain popular due to their simplicity and flexibility.
Can I use Modernizr and Yepnope with other JavaScript libraries like jQuery?
Yes, Modernizr and Yepnope can be used alongside other JavaScript libraries like jQuery. In fact, they can enhance the functionality of these libraries by providing feature detection and conditional resource loading.
What are the performance implications of using Modernizr and Yepnope?
If used correctly, Modernizr and Yepnope can actually improve the performance of your website. By only loading the scripts and styles that are necessary for the user’s browser, you can reduce the amount of data that needs to be downloaded. However, it’s important to keep the size of the Modernizr script to a minimum by only selecting the tests you need.
Louis joined SitePoint in 2009 as a technical editor, and has since moved over into a web developer role at Flippa. He enjoys hip-hop, spicy food, and all things geeky.