Lesson 10 of 12

jQuery AJAX

AJAX means talking to a server in the background, without reloading the page. A search box that shows suggestions as you type, a “Load more” button, a form that says “Thanks!” without leaving the page – all of these use AJAX. The jQuery AJAX methods were a big reason for its popularity, and you will meet them in a lot of existing code. The modern built-in alternative is fetch(), covered in Fetch and Async JavaScript.

How a jQuery AJAX request works

  1. Your script sends an HTTP request to a URL, for example /api/products.
  2. The page stays usable while the request travels – it is asynchronous.
  3. The server replies, usually with JSON: text that looks like JavaScript objects and arrays.
  4. jQuery parses the JSON and calls your success function with the data, or your error function if something went wrong.
  5. Your code updates the page with the new data.

The examples on this page run in a sandbox with no server of its own, so they load sample data from JSONPlaceholder, a free public test API that allows requests from any website. Its data is placeholder text, not real people.

The quick way: $.getJSON()

$.getJSON(url) sends a GET request and parses the JSON reply. It returns a jqXHR object, which works like a promise: .done() runs on success, .fail() on error, and .always() in both cases – the right place to hide a loading message.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Loading users with $.getJSON()</title>
  <style>
    body { font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; }
    li { margin: 4px 0; }
    .error { color: #b91c1c; }
  </style>
</head>
<body>
  <button id="load" type="button">Load users</button>
  <p id="status" role="status">Click the button to load data.</p>
  <ul id="users"></ul>

  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <script>
const $status = $('#status');
const $list = $('#users');
const $btn = $('#load');

$btn.on('click', () => {
  $btn.prop('disabled', true);
  $status.removeClass('error').text('Loading…');
  $list.empty();

  $.getJSON('https://jsonplaceholder.typicode.com/users')
    .done((users) => {
      const items = users.map((user) =>
        $('<li>').text(`${user.name} – ${user.address.city}`)   // .text(): safe for any data
      );
      $list.append(items);
      $status.text(`Loaded ${users.length} users.`);
    })
    .fail((jqXHR, textStatus) => {
      $status.addClass('error').text(`Could not load users (${textStatus}). Please try again.`);
    })
    .always(() => {
      $btn.prop('disabled', false);
    });
});
  </script>
</body>
</html>

Notice the three states every AJAX feature needs: loading (a message and a disabled button so people do not click five times), success and error. Networks fail, especially on mobile data, so never skip the error message.

Full control with $.ajax()

$.getJSON(), $.get() and $.post() are jQuery AJAX shortcuts. Underneath they all call $.ajax(), which takes one settings object:

SettingMeaning
urlWhere to send the request
method'GET' (default), 'POST', 'PUT', 'DELETE'… (older code uses type)
dataData to send – an object becomes name=value&… form data
contentTypeThe format of the data you send, e.g. 'application/json'
dataTypeThe format you expect back: 'json', 'text', 'html'…
timeoutGive up after this many milliseconds
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Sending data with a POST request</title>
  <style>
    body { font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; }
    label { display: block; margin-top: 8px; }
    pre { background: #f3f4f6; padding: 8px; white-space: pre-wrap; }
  </style>
</head>
<body>
  <form id="review">
    <label for="title">Review title</label>
    <input id="title" name="title" type="text" value="Great filter coffee">
    <label for="body">Your review</label>
    <textarea id="body" name="body" rows="2">Strong, fresh and served hot.</textarea>
    <p><button type="submit">Post review</button></p>
  </form>
  <p id="status" role="status"></p>
  <pre id="reply"></pre>

  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <script>
$('#review').on('submit', (event) => {
  event.preventDefault();
  $('#status').text('Sending…');

  $.ajax({
    url: 'https://jsonplaceholder.typicode.com/posts',
    method: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({
      title: $('#title').val().trim(),
      body: $('#body').val().trim(),
      userId: 1
    }),
    dataType: 'json',
    timeout: 8000
  })
    .done((saved) => {
      $('#status').text(`Saved with id ${saved.id}.`);
      $('#reply').text(JSON.stringify(saved, null, 2));
    })
    .fail((jqXHR, textStatus) => {
      $('#status').text(`Sending failed: ${textStatus}`);
    });
});
  </script>
</body>
</html>

Tip: JSONPlaceholder only pretends to save: it replies as a real API would, with a new id, but nothing is stored. With your own server, the same code would insert a row into the database.

Using async and await

Because the jqXHR object is “thenable”, you can await it inside an async function, just like fetch(). If the request fails, await throws – and the thrown value is the jqXHR object, so you can read err.status.

async function loadPosts() {
  try {
    const posts = await $.getJSON('https://jsonplaceholder.typicode.com/posts?userId=1');
    $('#status').text(`${posts.length} posts`);
  } catch (err) {
    $('#status').text(`Error ${err.status}`);
  }
}

// The same with fetch(): note that fetch does NOT reject on 404 or 500
async function loadPostsWithFetch() {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts?userId=1');
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  const posts = await response.json();
  document.getElementById('status').textContent = `${posts.length} posts`;
}

Practising without any API

If you are offline, or want to test your rendering code with exact data, you can request a data: URL. The JSON is written inside the URL itself, so no network is needed, yet jQuery handles it like a normal response. This is only a learning trick – real data always comes from a server.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Loading JSON from a data: URL</title>
  <style>
    body { font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; }
  </style>
</head>
<body>
  <p id="status" role="status">Loading menu…</p>
  <ul id="menu"></ul>

  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <script>
const menu = [
  { dish: 'Ghee roast dosa', price: 120 },
  { dish: 'Kothu parotta', price: 150 },
  { dish: 'Rose milk', price: 60 }
];
const url = 'data:application/json,' + encodeURIComponent(JSON.stringify(menu));

$.getJSON(url)
  .done((items) => {
    $('#menu').append(items.map((item) => $('<li>').text(`${item.dish} – ₹${item.price}`)));
    $('#status').text(`${items.length} dishes on today's menu.`);
  })
  .fail(() => $('#status').text('Could not read the menu.'));
  </script>
</body>
</html>

With a real server

On a real site you usually call your own back end, such as a PHP file that reads the database with PDO and prints JSON. Requests to the same domain just work. Requests to another domain only work if that server sends CORS headers allowing your site; otherwise the browser blocks the response and .fail() runs.

  • Always validate and sanitise data on the server too – anyone can send requests without using your page.
  • Show server data with .text(), not .html(), unless you fully trust and control that HTML.
  • jQuery 4 no longer turns a JSON request into JSONP automatically, for security reasons. Use CORS; ask for JSONP only on purpose with dataType: 'jsonp'.
  • $('#box').load('part.html') loads an HTML fragment into an element. It needs a server and is handy in older sites.

Further reading

Key takeaways

  • AJAX loads or sends data in the background without a page reload.
  • $.getJSON() is the shortcut for GET + JSON; $.ajax() takes a settings object for full control.
  • Handle loading, success and error with .done(), .fail() and .always() – or await with try/catch.
  • Send JSON with method: 'POST', contentType: 'application/json' and JSON.stringify().
  • Real servers need CORS for cross-domain calls; jQuery 4 no longer auto-promotes to JSONP.
  • Always insert server data with .text() and validate on the server.

Quick quiz

Check what you learned – pick an answer.

1. Which callback is the best place to re-enable a "Load" button whether the request worked or not?

2. You want to send an object as JSON in a POST. Which pair of settings is right?

3. In jQuery 4, a cross-domain request with dataType: 'json' to a server without CORS headers…

Practice exercise

When the button is clicked, load https://jsonplaceholder.typicode.com/posts?userId=1 and show the title of each post in the ordered list. Show "Loading…" while waiting, the number of posts when done, and an error message if it fails.

Show a hint

Use $.getJSON(url).done(…).fail(…), create each list item with .text(post.title) so the titles are inserted safely, and append them all at once.

Show the solution
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Exercise: AJAX</title>
  <style>
    body { font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; }
  </style>
</head>
<body>
  <button id="load" type="button">Load posts by user 1</button>
  <p id="status" role="status"></p>
  <ol id="posts"></ol>

  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <script>
$('#load').on('click', async () => {
  $('#status').text('Loading…');
  $('#posts').empty();

  try {
    const posts = await $.getJSON('https://jsonplaceholder.typicode.com/posts?userId=1');
    $('#posts').append(posts.map((post) => $('<li>').text(post.title)));
    $('#status').text(`${posts.length} posts loaded.`);
  } catch (err) {
    $('#status').text('Sorry, the posts could not be loaded.');
  }
});
  </script>
</body>
</html>

Progress is saved in this browser only.