Prefetch Page Accelerator

Modern web performance is often a game of milliseconds. While we spend hours compressing images and stripping out redundant code to shave down load times, there is an overlooked psychological trick that can make your site feel near-instantaneous: predictive prefetching.
  • Instead of waiting for a reader to actually click a link before asking the server to load the next page, you can use JavaScript to anticipate their intent.
By placing a lightweight script into your site's template, the browser gets a vital head start on downloading the next destination file before the user's finger or mouse button even goes down. Rather than blindly prefetching every single link on a page - which wastes bandwidth and throttles mobile data plans - the smart approach is to trigger the download only upon a user’s hover or touch event.

On a desktop, there is an average human delay of 200 to 500 milliseconds between a mouse cursor parking over a link and the finger completing a click.

On a mobile device, a similar, albeit shorter, latency exists between the initial touch on the glass and the browser executing the command. This script silently captures that exact window of intent. It waits a tiny fraction of a second to ensure the movement isn't just a casual swipe, and then quietly injects a low-priority background request for the upcoming page.

The result is a browsing experience that feels incredibly fluid and snappy, mimicking the behavior of an expensive single-page web application on a standard blog platform. Because the script is entirely asynchronous and uses passive event listeners, it runs smoothly in the background without causing a single stutter to page scrolling or layout rendering.

It automatically filters out external websites or simple anchor tags, focusing solely on preloading your internal articles. For the person reading your blog, the transition between pages becomes practically seamless, pulling the next article instantly from the local browser cache the exact moment they decide to click.


Try it: Click any link on this page.

Prefetch Page Accelerator
<script>
(function() {
    'use strict';

    // 1. Respect user data-saver settings and slow mobile connections
    const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
    if (connection && (connection.saveData || /2g|3g/.test(connection.effectiveType || ''))) {
        return;
    }

    const prefetchedUrls = new Set();
    let hoverTimer = null;

    // RegEx for media/binary downloads, system URLs, and dynamic queries
    const ignoredExtensions = /\.(zip|rar|tar|gz|pdf|doc|docx|xls|xlsx|mp3|mp4|avi|wav|jpg|jpeg|png|gif|svg|webp|epub)$/i;
    const ignoredPaths = /\/(feeds|b|logout|comment-editor\.do|search)/i;

    // Check for modern Speculation Rules API support
    const supportsSpeculationRules = HTMLScriptElement.supports && HTMLScriptElement.supports('speculationrules');

    // Helper to strip hashes and standardise internal URLs
    const getCleanUrl = (href) => {
        try {
            const parsed = new URL(href, window.location.href);
            parsed.hash = ''; // Remove fragment identifiers
            return parsed.href;
        } catch (e) {
            return null;
        }
    };

    // Validate link suitability for prefetching
    const isValidUrl = (url, target) => {
        if (!url) return false;

        const currentCleanUrl = window.location.href.split('#')[0];
        if (url === currentCleanUrl) return false;

        try {
            const parsed = new URL(url);
            if (parsed.origin !== window.location.origin) return false;
            if (ignoredExtensions.test(parsed.pathname)) return false;
            if (ignoredPaths.test(parsed.pathname)) return false;
        } catch (e) {
            return false;
        }

        if (target) {
            const rel = target.getAttribute('rel');
            if (rel && rel.includes('nofollow')) return false;
            if (target.hasAttribute('data-no-prefetch')) return false;
        }

        return true;
    };

    // Inject prefetch instruction via Speculation Rules or standard link tag
    const prefetchUrl = (url) => {
        if (prefetchedUrls.has(url)) return;
        prefetchedUrls.add(url);

        if (supportsSpeculationRules) {
            const specScript = document.createElement('script');
            specScript.type = 'speculationrules';
            specScript.textContent = JSON.stringify({
                prefetch: [{ source: 'list', urls: [url] }]
            });
            document.head.appendChild(specScript);
        } else {
            const link = document.createElement('link');
            link.rel = 'prefetch';
            link.href = url;
            link.as = 'document';
            document.head.appendChild(link);
        }
    };

    // 2. Pre-load home page if currently reading an internal post or page
    const homeUrl = window.location.origin + '/';
    const cleanCurrent = window.location.href.split('#')[0];
    if (cleanCurrent !== homeUrl && cleanCurrent !== window.location.origin) {
        prefetchUrl(homeUrl);
    }

    // 3. Non-blocking setup for Viewport Intersection Observer
    const runWhenIdle = window.requestIdleCallback || function(cb) { setTimeout(cb, 800); };

    runWhenIdle(() => {
        if (!('IntersectionObserver' in window)) return;

        const observerTimers = new Map();

        const observer = new IntersectionObserver((entries, obs) => {
            entries.forEach(entry => {
                const target = entry.target;
                const cleanUrl = getCleanUrl(target.href);

                if (entry.isIntersecting) {
                    // Dwell timer ensuring user is actually reading in this area
                    const timer = setTimeout(() => {
                        if (isValidUrl(cleanUrl, target) && !prefetchedUrls.has(cleanUrl)) {
                            prefetchUrl(cleanUrl);
                        }
                        obs.unobserve(target);
                        observerTimers.delete(target);
                    }, 400);
                    observerTimers.set(target, timer);
                } else {
                    if (observerTimers.has(target)) {
                        clearTimeout(observerTimers.get(target));
                        observerTimers.delete(target);
                    }
                }
            });
        }, { rootMargin: '50px', threshold: 0.1 });

        // Observe valid links on the page
        document.querySelectorAll('a').forEach(link => {
            const cleanUrl = getCleanUrl(link.href);
            if (cleanUrl && isValidUrl(cleanUrl, link) && !prefetchedUrls.has(cleanUrl)) {
                observer.observe(link);
            }
        });
    });

    // 4. Intent detection via hover, touch, and mousedown
    const triggerPrefetch = (event) => {
        const target = event.target.closest('a');
        if (!target || !target.href) return;

        const cleanUrl = getCleanUrl(target.href);
        if (!isValidUrl(cleanUrl, target) || prefetchedUrls.has(cleanUrl)) return;

        if (event.type === 'touchstart' || event.type === 'mousedown') {
            prefetchUrl(cleanUrl);
        } else if (event.type === 'pointerover' || event.type === 'mouseover') {
            clearTimeout(hoverTimer);
            hoverTimer = setTimeout(() => {
                prefetchUrl(cleanUrl);
            }, 30);
        }
    };

    const cancelPrefetch = () => {
        clearTimeout(hoverTimer);
    };

    // Attach passive event listeners
    document.addEventListener('pointerover', triggerPrefetch, { passive: true });
    document.addEventListener('mouseout', cancelPrefetch, { passive: true });
    document.addEventListener('touchstart', triggerPrefetch, { passive: true });
    document.addEventListener('mousedown', triggerPrefetch, { passive: true });
})();
</script>

    

Post a Comment

0 Comments

Notice:
Comments are moderated and may not appear immediately. Please keep your comments respectful, and relevant to the post. My site. My rules.

Post a Comment (0)