NeuroAgent

Implement $(document).ready Without jQuery: Complete Guide

Learn how to implement jQuery's $(document).ready using native JavaScript DOMContentLoaded event. Complete guide with code examples and browser compatibility solutions.

Question

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.

NeuroAgent

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

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.

javascript
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.

javascript
// 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)

javascript
(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

javascript
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+)

javascript
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

javascript
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

javascript
// 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

javascript
// 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

javascript
// 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

javascript
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

javascript
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

javascript
ready(function() {
    function updateUI() {
        // Perform visual updates
        requestAnimationFrame(updateUI);
    }
    
    requestAnimationFrame(updateUI);
});

4. Lazy load non-critical resources

javascript
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

  1. MDN Web Docs - DOMContentLoaded Event
  2. MDN Web Docs - Document.readyState
  3. Stack Overflow - Document ready without jQuery
  4. JavaScript.info - DOMContentLoaded, load, beforeunload
  5. 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.