Using Modernizr for Feature Detection in jQuery
Feature detection is an essential practice in modern web development that ensures web applications degrade gracefully across different browsers without relying on fragile user-agent sniffing. This article explains how to implement feature detection using Modernizr in conjunction with modern jQuery, demonstrating how to conditionally run jQuery methods, target browser capabilities through CSS classes, and dynamically handle fallbacks.
Setting Up Modernizr and Modern jQuery
To use both libraries effectively, Modernizr should generally load in
the <head> of your document to prevent a Flash of
Unstyled Content (FOUC), as it immediately appends feature-support
classes to the <html> element. Modern jQuery should
be included either deferred in the <head> or just
before the closing </body> tag.
<!DOCTYPE html>
<html lang="en" class="no-js">
<head>
<meta charset="UTF-8">
<title>Feature Detection Example</title>
<script src="modernizr-custom.js"></script>
</head>
<body>
<!-- Page Content -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="main.js"></script>
</body>
</html>Note: Replacing class="no-js" with
class="js" happens automatically when Modernizr
executes.
Method 1: JavaScript Conditional Branching
Modernizr exposes a global Modernizr object that
contains boolean properties representing whether the user's browser
supports specific HTML5, CSS3, or JavaScript features. You can query
these properties directly inside your jQuery code to conditionally
execute DOM manipulations or attach event listeners.
$(function() {
// Check for Local Storage support
if (Modernizr.localstorage) {
$('#save-btn').on('click', function() {
localStorage.setItem('userDraft', $('#editor').val());
});
} else {
// Fallback behavior using server-side session or cookies via jQuery
$('#save-btn').on('click', function() {
$.post('/api/save-draft', { draft: $('#editor').val() });
});
}
// Check for Geolocation support
if (Modernizr.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
$('#location-display').text(
`Latitude: ${position.coords.latitude}, Longitude: ${position.coords.longitude}`
);
});
} else {
$('#location-display').text('Geolocation is not supported by your browser.');
}
});Method 2: Selecting Elements Based on Modernizr HTML Classes
Modernizr adds descriptive classes directly to the root
<html> element, such as .flexbox or
.no-flexbox, and .webp or
.no-webp. You can leverage standard jQuery selectors to
target elements based on these classes.
$(function() {
// Check root class via jQuery
if ($('html').hasClass('no-touchevents')) {
// Enable hover-based tooltips only on non-touch devices
$('.tooltip').on('mouseenter mouseleave', function() {
$(this).find('.tooltip-text').toggle();
});
} else {
// Enable tap/click toggles for touch devices
$('.tooltip').on('click', function(e) {
e.stopPropagation();
$(this).find('.tooltip-text').fadeToggle(200);
});
}
});Method 3: Conditional Polyfilling Before Running jQuery Code
When a critical browser feature is missing, Modernizr allows you to conditionally load a polyfill script before running your primary jQuery logic.
$(function() {
// Conditionally load polyfill if IntersectionObserver is absent
if (!Modernizr.intersectionobserver) {
$.getScript('https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver')
.done(initializeLazyLoading)
.fail(function() {
console.error('Failed to load polyfill.');
});
} else {
initializeLazyLoading();
}
function initializeLazyLoading() {
const observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
const $img = $(entry.target);
$img.attr('src', $img.data('src')).removeClass('lazy');
observer.unobserve(entry.target);
}
});
});
$('img.lazy').each(function() {
observer.observe(this);
});
}
});Best Practices
- Use Custom Builds: Only build and download Modernizr with tests for the features your application actually relies on. Including unnecessary tests increases bundle size.
- Combine with Progressive Enhancement: Ensure the page remains fundamentally functional even if JavaScript or specific modern APIs fail to load entirely.
- Prefer Feature Detection over Browser Version
Checks: Never use jQuery's deprecated
$.browseror parsenavigator.userAgent. Modernizr checks whether an API actually works, which remains resilient across platform and version changes.