Using jQuery to Observe Resource Loading Events
What Resource Loading Events Actually Are
When a browser loads a page, it fetches tons of stuff. Scripts, stylesheets, images, fonts, iframes — all of these are resources. Resource loading events let you tap into the lifecycle of these fetches. You can detect when something starts loading, finishes, fails, or times out.
jQuery doesn't have native resource observers like the PerformanceObserver API. But you can still use jQuery to observe and react to resource loading events with a bit of DOM manipulation and event handling.
Why You'd Want to Do This
Common reasons:
- Track how long external scripts take to load
- Detect when dynamically injected assets finish rendering
- Show loading spinners until specific resources complete
- Debug missing or failed resources
- Lazy-load content based on when assets become available
jQuery Doesn't Watch Resources Directly
Here's the bitter truth: jQuery is primarily a DOM manipulation library. It wasn't built to observe network requests or resource loading. The $.ajax() and $.get() methods let you track your own AJAX requests, but that's different from observing arbitrary resource loading.
For actual resource observation, you'll need to combine jQuery with native browser APIs. This isn't a limitation of jQuery — it's just not what it's designed for.
The Methods That Actually Work
1. Using $.ajax() for Your Own Requests
If you're loading resources through AJAX, jQuery makes this straightforward:
$.ajax({
url: '/api/data.json',
success: function(data) {
console.log('Resource loaded:', data);
},
error: function(xhr, status, error) {
console.error('Failed to load resource:', error);
},
complete: function(xhr, status) {
console.log('Request finished, regardless of success');
}
});
This works for any resource you fetch via XHR. But it won't catch things like <script src> tags or <img> elements.
2. Observing DOM Changes with MutationObserver
You can use a MutationObserver with jQuery to watch when new elements (like scripts or images) get added to the DOM:
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
$.each(mutation.addedNodes, function(i, node) {
if (node.nodeType === 1) {
// A new element was added
if ($(node).is('script')) {
console.log('Script tag detected:', node.src);
}
if ($(node).is('img')) {
$(node).on('load error', function() {
console.log('Image event:', this.src, event.type);
});
}
}
});
});
});
Start observing like this:
observer.observe(document.body, {
childList: true,
subtree: true
});
3. Tracking Images with jQuery Event Binding
For images already in the DOM, bind load and error events directly:
$('img').on('load', function() {
console.log('Image loaded:', this.src);
}).on('error', function() {
console.error('Image failed:', this.src);
});
This catches images that exist when the script runs. For dynamically created images, bind the event before setting the source:
var img = $('<img>').on('load', function() {
console.log('Dynamic image loaded');
}).on('error', function() {
console.error('Dynamic image failed');
}).attr('src', '/path/to/image.jpg');
4. Script Loading with $.getScript()
jQuery's $.getScript() loads scripts and executes them. It returns a promise you can chain:
$.getScript('/path/to/script.js')
.done(function() {
console.log('Script loaded and executed');
})
.fail(function(jqxhr, settings, exception) {
console.error('Script failed:', exception);
});
Comparison of Methods
| Method | Use Case | Tracks Load | Tracks Error | Works for Dynamic Content |
|---|---|---|---|---|
$.ajax() |
XHR/AJAX requests | Yes | Yes | Yes |
$.getScript() |
Loading JS files | Yes | Yes | Yes |
MutationObserver |
DOM changes | No (just detects addition) | No | Yes |
.on('load/error') |
Images, iframes | Yes | Yes | Manual binding needed |
Native PerformanceObserver |
All resources | Yes | Limited | Yes |
Using PerformanceObserver (The Native Way)
If you need comprehensive resource tracking, use the native PerformanceObserver API. jQuery can coexist with this:
var resourceObserver = new PerformanceObserver(function(list) {
list.getEntries().forEach(function(entry) {
console.log('Resource:', entry.name);
console.log('Type:', entry.initiatorType);
console.log('Duration:', entry.duration + 'ms');
});
});
Start observing:
resourceObserver.observe({ entryTypes: ['resource'] });
This catches everything — scripts, stylesheets, images, XHR, fetch, beacons. jQuery is irrelevant here, but you can still use jQuery for DOM manipulation while this runs in the background.
Getting Started: Practical Example
Here's a complete example that tracks resource loading for a specific container:
<div id="resource-tracker">
<img src="/image1.jpg" data-resource="hero-image">
<img src="/image2.jpg" data-resource="thumbnail">
<script src="/analytics.js" data-resource="analytics"></script>
</div>
$(function() {
// Track images
$('#resource-tracker img').each(function() {
var $img = $(this);
var resourceName = $img.data('resource');
$img.on('load', function() {
console.log(resourceName + ' loaded successfully');
$img.addClass('loaded');
}).on('error', function() {
console.error(resourceName + ' failed to load');
$img.addClass('error');
});
});
// Track scripts
$('#resource-tracker script').each(function() {
var script = this;
var resourceName = $(this).data('resource');
// Scripts already executed if they have src
// For tracking, use getScript if reloading
console.log(resourceName + ' script tag present');
});
});
Common Gotchas
- Cached images: The
loadevent may not fire for cached images. Add a cache-busting query string or use thecompleteproperty check. - Scripts without
onloadsupport: Older browsers don't fire load events on scripts. Use$.getScript()instead. - MutationObserver performance: Watching the entire
document.bodywithsubtree: truecan hurt performance. Scope it to specific containers. - Timing: Bind events before the resource starts loading. For images, set
srcafter binding the event handler. - Cross-origin resources: Error events on cross-origin resources may be silently swallowed due to security restrictions.
When to Skip jQuery Altogether
If resource observation is your main goal, drop jQuery. The native APIs are faster, smaller, and more capable:
PerformanceObserverfor resource timingIntersectionObserverfor lazy loading imagesDOMContentLoadedandloadevents for page-level timing- Native
fetchwithsignalfor abortable requests
jQuery makes sense when you're already using it for DOM manipulation and need a quick resource tracking solution. But if observation is the primary feature, native APIs win every time.