How to implement $(document).ready equivalent without jQuery?
I have a script that uses $(document).ready, but it doesn’t use anything else from jQuery. I’d like to lighten it up by removing the jQuery dependency.
How can I implement my own $(document).ready functionality without using jQuery? I know that using window.onload will not be the same, as window.onload fires after all images, frames, etc. have been loaded.
The most common way to implement $(document).ready equivalent without jQuery is using the DOMContentLoaded event listener, which fires when the DOM is fully loaded but doesn’t wait for stylesheets, images, and subframes to finish loading. Another approach is using the document.readyState property to check if the document is already loaded when the script runs. Modern JavaScript also offers the await import() method with dynamic imports for more sophisticated scenarios.
Contents
- DOM Content Loaded Event
- Document Ready State Check
- Modern JavaScript Approaches
- Compatibility Considerations
- Complete Implementation Examples
- Performance Best Practices
DOM Content Loaded Event
The simplest and most reliable way to replicate jQuery’s $(document).ready() functionality is by using the DOMContentLoaded event. This event fires when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.
document.addEventListener('DOMContentLoaded', function() {
// Your code here
console.log('DOM is ready');
});
This approach is widely supported across all modern browsers and provides the exact same timing as jQuery’s ready() method. The main advantage is that it doesn’t require any external libraries and is natively supported by the browser.
Important: Unlike window.onload, DOMContentLoaded doesn’t wait for external resources like CSS files, images, or iframes to load, which is exactly what you need when replacing $(document).ready().
Document Ready State Check
For scripts that might execute before the DOM is fully loaded, you can check the document.readyState property. This approach is particularly useful when you can’t control where the script is placed in the document.
// Check if the document is already loaded
if (document.readyState === 'loading') {
// Still loading, wait for DOMContentLoaded
document.addEventListener('DOMContentLoaded', function() {
// Your code here
});
} else {
// DOMContentLoaded already fired
// Your code here
}
The readyState property can have three values:
- ‘loading’: The document is still loading
- ‘interactive’: The document has been fully loaded and parsed, but subresources like scripts, images, stylesheets and frames are still loading
- ‘complete’: The document and all subresources have finished loading
This method ensures your code runs immediately if the DOM is already ready, or waits for it if it’s still loading.
Modern JavaScript Approaches
Modern JavaScript offers several elegant ways to handle DOM ready functionality:
Using IIFE (Immediately Invoked Function Expression)
(function() {
'use strict';
if (document.readyState === 'complete' ||
document.readyState === 'interactive') {
// Document is already ready
initializeApp();
} else {
// Wait for DOMContentLoaded
document.addEventListener('DOMContentLoaded', initializeApp);
}
function initializeApp() {
// Your initialization code here
console.log('App initialized');
}
})();
Using Promise-based approach
function documentReady() {
return new Promise(resolve => {
if (document.readyState !== 'loading') {
return resolve();
}
document.addEventListener('DOMContentLoaded', () => resolve());
});
}
// Usage
documentReady().then(() => {
// Your code here
console.log('DOM is ready');
});
Async/await syntax (ES2017+)
async function waitForDOMReady() {
if (document.readyState === 'loading') {
await new Promise(resolve => {
document.addEventListener('DOMContentLoaded', resolve);
});
}
}
// Usage
waitForDOMReady().then(() => {
// Your code here
console.log('DOM is ready');
});
Compatibility Considerations
While DOMContentLoaded is well-supported in modern browsers, you might need fallbacks for very old browsers:
Cross-browser compatibility solution
function ready(callback) {
// For modern browsers
if (document.readyState !== 'loading') {
callback();
} else if (document.addEventListener) {
// Standard browsers
document.addEventListener('DOMContentLoaded', callback);
} else {
// Older IE browsers
document.attachEvent('onreadystatechange', function() {
if (document.readyState === 'complete') {
callback();
}
});
}
}
// Usage
ready(function() {
// Your code here
console.log('DOM is ready');
});
This solution handles:
- Modern browsers using DOMContentLoaded
- Internet Explorer using onreadystatechange
- Cases where the script runs after DOM is ready
Complete Implementation Examples
Simple jQuery Ready Replacement
// Create a simple jQuery-like ready function
function ready(fn) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', fn);
} else {
fn();
}
}
// Usage
ready(function() {
console.log('Document is ready');
// Now you can safely manipulate the DOM
const elements = document.querySelectorAll('.my-class');
elements.forEach(el => {
el.style.color = 'blue';
});
});
Advanced jQuery-like Ready Function
// More comprehensive jQuery-like ready function
const $ = {
ready: function(callback) {
// Handle cases where DOM is already loaded
if (document.readyState === 'interactive' ||
document.readyState === 'complete') {
// Use setTimeout to ensure callback is async
return setTimeout(callback, 0);
}
// Use DOMContentLoaded for modern browsers
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', callback);
} else {
// Fallback for older browsers
document.attachEvent('onreadystatechange', function() {
if (document.readyState === 'complete') {
callback();
}
});
}
}
};
// Usage
$.ready(function() {
console.log('Document is ready using jQuery-like syntax');
// Your DOM manipulation code here
document.getElementById('my-element').textContent = 'Hello World';
});
Multiple Ready Handlers
// Multiple ready handlers support
const readyHandlers = [];
let isReady = false;
function onDocumentReady(handler) {
if (isReady) {
handler();
} else {
readyHandlers.push(handler);
}
}
// Set up the DOMContentLoaded listener
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', function() {
isReady = true;
readyHandlers.forEach(handler => handler());
});
} else {
document.attachEvent('onreadystatechange', function() {
if (document.readyState === 'complete') {
isReady = true;
readyHandlers.forEach(handler => handler());
}
});
}
// Usage
onDocumentReady(function() {
console.log('First handler executed');
});
onDocumentReady(function() {
console.log('Second handler executed');
});
Performance Best Practices
When implementing your own $(document).ready equivalent, consider these performance optimizations:
1. Use event delegation for dynamic content
ready(function() {
// Use event delegation instead of attaching events to individual elements
document.addEventListener('click', function(e) {
if (e.target.matches('.dynamic-button')) {
// Handle dynamic button clicks
console.log('Dynamic button clicked');
}
});
});
2. Minimize DOM operations
ready(function() {
// Cache DOM elements to avoid repeated queries
const elements = {
container: document.getElementById('main-container'),
buttons: document.querySelectorAll('.action-button'),
input: document.getElementById('user-input')
};
// Use cached elements
elements.container.appendChild(document.createElement('div'));
});
3. Use requestAnimationFrame for visual updates
ready(function() {
function updateUI() {
// Perform visual updates
requestAnimationFrame(updateUI);
}
requestAnimationFrame(updateUI);
});
4. Lazy load non-critical resources
ready(function() {
// Load non-critical scripts after DOM is ready
const script = document.createElement('script');
script.src = 'non-critical-script.js';
script.defer = true;
document.head.appendChild(script);
});
Sources
- MDN Web Docs - DOMContentLoaded Event
- MDN Web Docs - Document.readyState
- Stack Overflow - Document ready without jQuery
- JavaScript.info - DOMContentLoaded, load, beforeunload
- Can I Use - DOMContentLoaded Support
Conclusion
Implementing $(document).ready without jQuery is straightforward using native JavaScript events and properties. The DOMContentLoaded event provides the most reliable and widely supported solution that matches jQuery’s timing exactly. For modern JavaScript development, consider using Promise-based approaches or async/await syntax for cleaner code. Always consider browser compatibility when working with older browsers that may not support modern events. Remember that these native approaches are more performant than jQuery’s implementation since they don’t have the overhead of the jQuery library itself.