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