AboutBlogContact
Backend EngineeringSeptember 22, 2004 5 min read 272Updated: June 22, 2026

The AJAX Revolution (2004): How XMLHttpRequest Changed the Web Before Anyone Called It AJAX

AunimedaAunimeda
📋 Table of Contents

Jesse James Garrett coined the term "Ajax" in February 2005. But the technology had been in production at Microsoft since 1999 (XMLHTTP ActiveX component in IE5), implemented in Mozilla in 2002, and used quietly by Google in Gmail and Google Maps throughout 2004.

Before Ajax, the web worked like this: user clicks, form submits, entire page reloads, new HTML renders. Every interaction - searching, sorting, paginating, filtering - required a full round trip to the server and a complete page redraw.

We were building web applications in 2004. The moment we saw how Google Maps handled map panning - moving the map without a page reload, loading new tiles silently in the background - we understood that something fundamental had shifted.


What XMLHttpRequest actually was

XMLHttpRequest was invented by a Microsoft developer (Alex Hopmann) for Outlook Web Access in Exchange 2000. The idea: let JavaScript make HTTP requests without navigating the browser. Microsoft shipped it as an ActiveX object in IE5 (1999). The W3C didn't standardize it until 2006.

The raw API in 2004, before jQuery abstracted it:

// Cross-browser XMLHttpRequest - the boilerplate everyone copy-pasted
function createXHR() {
    if (typeof XMLHttpRequest !== 'undefined') {
        // Firefox 1.0, Safari 1.2, Opera 7.6
        return new XMLHttpRequest();
    }
    // IE 6, 5.5
    var versions = [
        'MSXML2.XmlHttp.6.0',
        'MSXML2.XmlHttp.3.0',
        'Microsoft.XmlHttp'
    ];
    for (var i = 0; i < versions.length; i++) {
        try { return new ActiveXObject(versions[i]); } catch(e) {}
    }
    throw new Error('XMLHttpRequest not supported');
}

function fetchData(url, callback) {
    var xhr = createXHR();
    
    xhr.onreadystatechange = function() {
        // readyState 4 = complete, status 200 = OK
        if (xhr.readyState === 4 && xhr.status === 200) {
            callback(xhr.responseText);
        }
    };
    
    xhr.open('GET', url, true); // true = asynchronous
    xhr.send(null);
}

// Usage
fetchData('/api/search?q=javascript', function(responseText) {
    document.getElementById('results').innerHTML = responseText;
});

Note: the server returned HTML, not JSON. JSON wasn't the default response format yet - that came later. The server rendered an HTML fragment, JavaScript dropped it into a div. Crude, but it worked.


What we were building in 2004

We were building an internal project management tool for a small company. The original version: every action (add task, change status, add comment) submitted a form and reloaded the entire page. With 50 tasks on a project page, each reload took 2-3 seconds on the office's ADSL connection.

After seeing Gmail's compose window update without a page reload, we rebuilt the task status change:

function updateTaskStatus(taskId, newStatus) {
    var xhr = createXHR();
    
    xhr.onreadystatechange = function() {
        if (xhr.readyState !== 4) return;
        
        if (xhr.status === 200) {
            // Server returned new HTML for this task row
            var row = document.getElementById('task-' + taskId);
            row.innerHTML = xhr.responseText;
        } else {
            alert('Failed to update task. Please try again.');
        }
    };
    
    xhr.open('POST', '/tasks/update-status', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send('task_id=' + taskId + '&status=' + newStatus);
}
// Server: tasks/update-status - returns HTML fragment, not full page
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $taskId    = (int)$_POST['task_id'];
    $newStatus = mysql_real_escape_string($_POST['status']);
    
    mysql_query("UPDATE tasks SET status='$newStatus' WHERE id=$taskId");
    $task = mysql_fetch_assoc(mysql_query("SELECT * FROM tasks WHERE id=$taskId"));
    
    // Output just the table row, not the full page
    include 'partials/task-row.php';
    exit;
}

The first time a user changed a task status and the page didn't reload - just that one row updated in place - the reaction was genuine surprise. It felt like the page was alive.


The problems nobody warned us about

No error handling standard. If the server returned a 500, xhr.status was 500, but browsers didn't show any error to the user. You had to handle every failure case in JavaScript. The original web had the browser's error page as a fallback. Ajax requests had nothing.

Browser back button broke. Clicking "back" after several Ajax interactions took you to whatever the page looked like on first load, not to the previous Ajax state. This was a fundamental UX problem that took years to solve (the HTML5 History API in 2009).

No loading indicators by convention. With full page loads, the browser's loading spinner showed users something was happening. With Ajax, nothing indicated progress unless you built it. We forgot this repeatedly.

// The solution we added after user confusion
function fetchWithLoadingState(url, targetElement, callback) {
    targetElement.innerHTML = '<img src="/images/spinner.gif" alt="Loading..."/>';
    
    fetchData(url, function(html) {
        targetElement.innerHTML = html;
        if (callback) callback();
    });
}

IE caching GET requests. Internet Explorer cached XMLHttpRequest GET requests aggressively. The same URL would return the same response for the duration of the session. Fix: append a timestamp to every request URL.

// IE cache-busting - everyone did this in 2004-2007
var url = '/api/tasks?ts=' + new Date().getTime();
fetchData(url, callback);

What this era established

The 2004-2006 Ajax era established patterns that are still the foundation of modern web development:

Separation of data and presentation. Once you started returning data (then HTML fragments, then JSON) and handling presentation in JavaScript, the server became an API and the browser became a rendering engine. This separation is now fundamental - REST APIs, GraphQL, React - all descend from this conceptual split.

Asynchronous by default. The browser's event model was always asynchronous. Ajax made JavaScript developers confront asynchronous programming directly: callbacks, then Promises, then async/await. The mental model shift from synchronous to asynchronous thinking started here.

User experience expectations changed permanently. After Gmail and Google Maps, users expected web applications to behave like desktop applications - responsive, immediate, not requiring full page reloads. That expectation has only intensified. It drove the entire SPA era.

The technology was rudimentary. The boilerplate was tedious. The cross-browser compatibility was painful. But the idea - that a web page could update itself without reloading - opened a door that hasn't closed.


Aunimeda builds production-grade backend systems - APIs, microservices, real-time applications, and system integrations.

Contact us for backend engineering services. See also: Custom Software Development, Web Development

Read Also

Why We Chose Node.js in 2011aunimeda
Backend Engineering

Why We Chose Node.js in 2011

In 2011 Node.js was 2 years old, had no LTS, and most PHP developers thought it was a toy. We moved a real-time dashboard to it anyway. Here's the exact reasoning, the risks we accepted, and what happened.

The Node.js Revolution (2011): How JavaScript Conquered the Serveraunimeda
Backend Engineering

The Node.js Revolution (2011): How JavaScript Conquered the Server

Ryan Dahl showed Node.js at JSConf 2009. By 2011 we were running it in production. Non-blocking I/O, npm, and the realization that one language could run everywhere changed how we hired, how we built, and how we thought.

Node.js: Ryan Dahl's 45-Minute Talk That Rewrote Backend Development (2009)aunimeda
Backend Engineering

Node.js: Ryan Dahl's 45-Minute Talk That Rewrote Backend Development (2009)

On November 8, 2009, Ryan Dahl presented Node.js at JSConf EU in Berlin. He showed JavaScript running on the server with non-blocking I/O. The audience sat in silence, then gave a standing ovation. Within three years, Node.js had more packages than any other programming language ecosystem. This is what he built and why it worked.

Need IT development for your business?

We build websites, mobile apps and AI solutions. Free consultation.

Get Consultation All articles