Ajax в jQuery: $.ajax, $.get, $.post и отправка форм без перезагрузки
Опубликовано: 26.09.2026 · Обновлено: 26.09.2026 · Актуально для jQuery 3.7.1 и 4.0.0
В основе всех Ajax-запросов jQuery лежит один метод — $.ajax(). $.get(), $.post(), $.getJSON() и .load() — это его короткие обёртки для частых случаев, под капотом они вызывают тот же $.ajax() с готовым набором настроек. Результат любого из них — объект jqXHR: он реализует интерфейс Promise/A+, поэтому обрабатывать ответ можно через .done()/.fail()/.always() (или совместимый с ними .then()), либо через async/await, если код построен на промисах.
Короткие методы и $.ajax()
Все Ajax-функции jQuery сводятся к одному универсальному методу — $.ajax(). Остальные — сокращения для типовых случаев: меньше кода, но и меньше гибкости, часть настроек в них зашита заранее.
| Метод | Что делает | Эквивалент через $.ajax() |
$.get(url, data, success, dataType) | GET-запрос, самый частый случай — получить данные | $.ajax({ url, method: "GET", data, success, dataType }) |
$.post(url, data, success, dataType) | POST-запрос — отправить данные на сервер | $.ajax({ url, method: "POST", data, success, dataType }) |
$.getJSON(url, data, success) | GET-запрос с заранее заданным dataType: "json" | $.ajax({ url, dataType: "json", data, success }) |
$.getScript(url, success) | Загружает JS-файл и выполняет его | $.ajax({ url, dataType: "script", success }) |
.load(url, data, complete) | Загружает HTML с сервера и вставляет в выбранные элементы | GET/POST через $.ajax() + .html() над результатом; поддерживает селектор внутри строки: .load("/page.html #block") |
С версий 1.12/2.2 у $.get() и $.post() есть ещё один вариант вызова — единственный объект настроек, как в $.ajax(): $.get({ url: "/api/users/42", dataType: "json", headers: {...} }). Обратите внимание: url здесь внутри объекта. Если написать $.get(url, { dataType: "json" }), второй аргумент будет считаться данными запроса и уйдёт в строку запроса как ?dataType=json. Нужна настройка вне короткой сигнатуры (заголовки, timeout) — берите объектный вариант или сразу $.ajax().
// Три варианта одного и того же GET-запроса
$.get("/api/users/42", function (user) {
console.log(user.name);
});
$.get({ url: "/api/users/42", dataType: "json" })
.done(function (user) {
console.log(user.name);
});
$.ajax({
url: "/api/users/42",
method: "GET",
dataType: "json"
}).done(function (user) {
console.log(user.name);
});
.load() стоит особняком: это единственный метод из таблицы, который вызывается не на $/jQuery, а на выборке элементов — $("#content").load("/page.html"). Он всегда работает с HTML-ответом и не принимает dataType.
Настройки $.ajax(), которые нужны чаще всего
$.ajax() принимает один объект настроек. Параметров у него около тридцати, но в повседневной работе почти всегда нужны только эти:
| Опция | Тип / по умолчанию | Зачем нужна |
url | строка | Адрес запроса. Единственный обязательный параметр, если не передан первым аргументом. |
method | строка, по умолчанию "GET" | HTTP-метод: "GET", "POST", "PUT", "DELETE". Добавлен в 1.9 как более понятное имя для старой type — обе работают в 3.7 и 4.0, но пишите method. |
data | объект / строка / массив | Данные запроса. Для GET уходят в строку запроса, для остальных методов — в тело (формат зависит от contentType). |
dataType | строка, по умолчанию — «умное угадывание» по заголовку ответа | Ожидаемый формат ответа: "json", "html", "script", "xml", "text". Указывайте явно — угадывание не всегда надёжно. |
contentType | строка, по умолчанию "application/x-www-form-urlencoded; charset=UTF-8" | Формат тела запроса. Для JSON меняйте на "application/json" вручную — сам jQuery это не делает. |
processData | булево, по умолчанию true | Автоматически превращать data в строку. Для FormData нужно false, иначе jQuery попытается сериализовать её как обычный объект. |
headers | объект, по умолчанию {} | Дополнительные заголовки — токены авторизации, CSRF-токен. |
timeout | число миллисекунд | Через сколько считать запрос неудавшимся. 0 (по умолчанию) — без ограничения. |
cache | булево, по умолчанию true, false для "script" и "jsonp" | Разрешить браузеру кешировать GET-запрос. Для всегда свежих данных ставьте false. |
xhrFields.withCredentials | булево, по умолчанию false | Отправлять cookies и заголовки авторизации в кросс-доменных запросах. |
$.ajax({
url: "/api/orders",
method: "POST",
data: JSON.stringify({ productId: 17, qty: 2 }),
contentType: "application/json",
dataType: "json",
headers: { "X-Requested-With": "XMLHttpRequest" },
timeout: 8000,
xhrFields: { withCredentials: true }
})
.done(function (order) {
console.log("заказ создан", order.id);
})
.fail(function (jqXHR, textStatus) {
console.error("не удалось создать заказ:", textStatus);
});
Полный список опций — на странице $.ajax(). Задать значения по умолчанию сразу для всех запросов можно через $.ajaxSetup() — но осторожно, это глобальная настройка, она повлияет и на сторонние плагины.
Обработка ответа: .done/.fail/.always и .then
$.ajax() и все его обёртки возвращают объект jqXHR — надстройку над XMLHttpRequest, совместимую с интерфейсом Promises/A+. Есть два способа подписаться на результат.
Через колбэки в настройках — success, error, complete. Они по-прежнему работают в jQuery 3.7 и 4.0, никто их не убирал:
$.ajax({
url: "/api/profile",
success: function (data, textStatus, jqXHR) {
console.log("получено", data);
},
error: function (jqXHR, textStatus, errorThrown) {
console.error("ошибка", textStatus);
},
complete: function (jqXHR, textStatus) {
console.log("запрос завершён в любом случае");
}
});
Через методы jqXHR — .done(), .fail(), .always() — те же три случая, но можно вызывать цепочкой уже после того, как получили результат запроса, и вешать несколько обработчиков на один и тот же jqXHR:
$.ajax("/api/profile")
.done(function (data, textStatus, jqXHR) {
console.log("получено", data);
})
.fail(function (jqXHR, textStatus, errorThrown) {
console.error("ошибка:", textStatus, errorThrown);
})
.always(function () {
console.log("запрос завершён в любом случае");
});
Не путайте колбэк success в настройках и метод jqXHR.success(). Колбэк в объекте настроек работает и сейчас. А вот одноимённые методы самого jqXHR (jqXHR.success(fn), jqXHR.error(fn), jqXHR.complete(fn)) — это специальный случай, удалённый в jQuery 3.0 как источник путаницы «это же не настоящий Deferred». Старый код вида $.ajax(...).success(fn) на 3.0+ упадёт с TypeError — замените на .done(fn).
Аргументы у .done() и .fail() расположены зеркально не случайно: .done(data, textStatus, jqXHR) — данные первыми, потому что это главное, что нужно при успехе; .fail(jqXHR, textStatus, errorThrown) — сам jqXHR первым, потому что при ошибке распарсенных данных обычно нет, а нужен доступ к статусу ответа (jqXHR.status) и телу ошибки (jqXHR.responseText или jqXHR.responseJSON, если сервер вернул JSON с описанием ошибки).
Поскольку jqXHR — thenable-объект, с ним работает и .then(), и, что удобнее в современном коде, async/await:
async function loadProfile() {
try {
const data = await $.ajax({ url: "/api/profile", dataType: "json" });
console.log("получено", data);
} catch (jqXHR) {
// При отклонении промиса await получает jqXHR, а не объект Error
console.error("ошибка:", jqXHR.status, jqXHR.statusText);
}
}
jqXHR.status === 0 не значит «сервер ответил нулём» — это значит, что ответ вообще не получен: .abort(), обрыв сети, истёкший timeout или блокировка CORS ещё до ответа. Различить причины помогает textStatus ("timeout", "abort", "error") — подробности при CORS смотрите в консоли браузера, а не в аргументах колбэка.
JSON: получить и отправить
Получение и отправка JSON — разные по механике вещи, их часто путают.
Получить JSON — указать dataType: "json" в $.ajax() или использовать $.getJSON(). jQuery сам распарсит текст ответа и отдаст в колбэк готовый объект:
$.getJSON("/api/products", { category: "shoes" })
.done(function (products) {
console.log(products.length, "товаров");
})
.fail(function (jqXHR, textStatus) {
// Частая причина parsererror здесь — пустое тело ответа
console.error(textStatus);
});
Отправить JSON — самостоятельно сериализовать объект и указать тип содержимого. По умолчанию $.ajax() отправляет data как application/x-www-form-urlencoded — объект { name: "Иван", age: 30 } без настроек уйдёт строкой name=%D0%98%D0%B2%D0%B0%D0%BD&age=30, а не JSON:
// НЕПРАВИЛЬНО: сервер, ожидающий JSON, получит form-urlencoded тело
$.ajax({
url: "/api/products",
method: "POST",
data: { name: "Кроссовки", price: 4990 }
});
// ПРАВИЛЬНО: явная сериализация и content-type
$.ajax({
url: "/api/products",
method: "POST",
data: JSON.stringify({ name: "Кроссовки", price: 4990 }),
contentType: "application/json",
dataType: "json" // формат ответа, а не запроса
});
Это самая частая причина «сервер получает пустой объект» в связках jQuery + JSON API: разработчик меняет dataType, думая, что это управляет и форматом отправки, но dataType — только про формат ответа. За формат тела запроса отвечают contentType и ручной JSON.stringify().
Базовый сценарий — отправить форму без перезагрузки страницы. Сначала — обработчик submit с preventDefault(), чтобы браузер не сделал стандартную отправку и переход:
$("#feedback-form").on("submit", function (event) {
event.preventDefault();
var $form = $(this);
var $btn = $form.find("button[type=submit]");
$btn.prop("disabled", true);
$.ajax({
url: $form.attr("action"),
method: "POST",
data: $form.serialize(), // "name=..&email=..&message=.."
dataType: "json"
})
.done(function (response) {
$form[0].reset();
})
.fail(function (jqXHR) {
var errors = jqXHR.responseJSON && jqXHR.responseJSON.errors;
showFormErrors($form, errors || { _general: "Не удалось отправить форму" });
})
.always(function () {
$btn.prop("disabled", false);
});
});
.serialize() собирает поля формы в строку вида a=1&b=2. Если нужен не строковый, а объектный результат — есть .serializeArray(), возвращающая массив { name, value } по каждому полю. Оба метода учитывают только поля с атрибутом name и пропускают отключённые и невыбранные radio/checkbox.
Блокировка кнопки на время запроса — обязательная деталь: без неё двойной клик или медленное соединение отправляют форму дважды. Снимать блокировку нужно в .always(), а не в .done(), иначе при ошибке кнопка останется заблокированной навсегда.
Ошибки валидации с сервера обычно приходят с кодом 4xx — это тоже .fail(), а не .done(). Распарсенный JSON из тела ошибки jQuery кладёт в jqXHR.responseJSON, либо доступен как текст в jqXHR.responseText.
Отправка файлов требует другого набора настроек: содержимое формы собирается не в строку, а в FormData — объект, умеющий нести бинарные данные:
$("#upload-form").on("submit", function (event) {
event.preventDefault();
var formData = new FormData(this); // включая поля <input type="file">
$.ajax({
url: "/api/upload",
method: "POST",
data: formData,
processData: false, // не превращать FormData в строку
contentType: false, // не задавать Content-Type самим — это сделает браузер
dataType: "json"
}).done(function (response) {
console.log("файл загружен:", response.url);
});
});
processData: false и contentType: false обязательны вместе: иначе jQuery подставит application/x-www-form-urlencoded вместо нужного multipart/form-data; boundary=..., и сервер не разберёт форму.
Таймауты, повтор и отмена
Опция timeout обрывает запрос, если ответ не пришёл вовремя, и вызывает .fail() с textStatus === "timeout":
$.ajax({ url: "/api/slow-report", timeout: 5000 })
.fail(function (jqXHR, textStatus) {
if (textStatus === "timeout") {
console.warn("сервер не ответил за 5 секунд");
}
});
jqXHR также реализует метод .abort() — им можно отменить запрос вручную в любой момент до завершения:
var request = $.ajax({ url: "/api/report" });
$("#cancel-btn").on("click", function () {
request.abort();
});
Практическая задача, где отмена нужна почти всегда, — живой поиск по мере ввода: без защиты от гонки более медленный старый ответ может прийти позже нового и затереть актуальный результат:
var currentSearch = null;
$("#search").on("input", function () {
var query = this.value;
if (currentSearch) {
currentSearch.abort(); // отменяем предыдущий незавершённый запрос
}
currentSearch = $.ajax({
url: "/api/search",
data: { q: query },
dataType: "json"
}).done(function (results) {
renderResults(results);
});
});
Отменённый через .abort() запрос тоже вызовет .fail() с textStatus === "abort" — это ожидаемо, и такую ошибку в интерфейсе показывать не нужно, достаточно её игнорировать в обработчике.
CORS, cookies и CSRF
CORS (Cross-Origin Resource Sharing) настраивается не в jQuery. Разрешение или запрет запроса с другого домена определяет сервер: он должен вернуть заголовки Access-Control-Allow-Origin (и при необходимости Access-Control-Allow-Credentials, Access-Control-Allow-Headers). Без них браузер заблокирует ответ независимо от кода на jQuery — в консоли ошибка CORS, а в .fail() — статус 0 без подробностей.
На стороне jQuery настраивается только одно — отправлять ли учётные данные (cookies, заголовки авторизации) вместе с кросс-доменным запросом:
$.ajax({
url: "https://api.example.com/orders",
xhrFields: { withCredentials: true }
});
Ловушка: при withCredentials: true сервер обязан вернуть Access-Control-Allow-Origin с конкретным доменом, а не * — со звёздочкой браузер откажется принимать ответ. Это ограничение спецификации Fetch/CORS, а не jQuery.
CSRF-токен. Для запросов, которые меняют данные (POST/PUT/DELETE), большинство серверных фреймворков требует передавать защитный токен в заголовке. Общий принцип: токен кладут в <meta>-тег при рендере страницы, а на клиенте читают его и добавляют в заголовки всех запросов через $.ajaxSetup():
var csrfToken = $('meta[name="csrf-token"]').attr("content");
$.ajaxSetup({
headers: { "X-CSRF-Token": csrfToken }
});
// Теперь любой $.ajax()/$.post() на странице отправит этот заголовок автоматически
Имя заголовка и meta-тега зависит от бэкенда — принцип «токен из meta в заголовок каждого запроса» общий. Если глобальный $.ajaxSetup() нежелателен (часть запросов идёт на сторонние домены), заголовок можно добавлять точечно через beforeSend у конкретного запроса.
Глобальные события Ajax
Кроме колбэков конкретного запроса, jQuery рассылает глобальные события на все Ajax-запросы страницы (отключаются опцией global: false). Подписываются на них через $(document).on("ajaxStart", …) и т. п. Одноимённые методы-шорткаты (.ajaxStart(), .ajaxStop(), .ajaxError() и др.) с jQuery 3.5 считаются устаревшими — работают, но в новом коде используйте .on():
| Событие | Когда срабатывает | Типичное применение |
ajaxStart | Начался первый из одновременных Ajax-запросов | Показать глобальный индикатор загрузки |
ajaxStop | Завершились все текущие запросы | Скрыть индикатор загрузки |
ajaxError | Любой запрос завершился с ошибкой | Централизованное логирование/уведомление об ошибках |
ajaxComplete | Любой запрос завершился (успех или ошибка) | Общая статистика, аналитика запросов |
ajaxSuccess | Любой запрос завершился успешно | Обновление кеша/меток «последняя синхронизация» |
ajaxSend | Перед отправкой любого запроса | Общая точка для логирования исходящих запросов |
$(document)
.on("ajaxStart", function () {
$("#global-loader").show();
})
.on("ajaxStop", function () {
$("#global-loader").hide();
})
.on("ajaxError", function (event, jqXHR, settings, error) {
console.error("Ajax-запрос упал:", settings.url, error);
});
Полный список Ajax-методов, включая низкоуровневые $.ajaxPrefilter() и $.ajaxTransport(), — на странице все Ajax-методы. Глобальными событиями удобно закрывать сквозную логику, но не стоит злоупотреблять: несколько независимых виджетов со своим ajaxError легко превращаются в дублирующиеся уведомления об одной и той же ошибке.
Что изменилось в jQuery 4.0 для Ajax
Релиз jQuery 4.0.0 (январь 2026 года) сделал несколько неявных прежде вещей явными — по историческим причинам безопасности:
- JSON больше не превращается в JSONP автоматически. Раньше
dataType: "json" с параметром обратного вызова в URL мог незаметно обернуться в JSONP-запрос. В 4.0 нужен явный dataType: "jsonp" — неявное превращение убрано как риск выполнения чужого кода без ведома разработчика. Для новых интеграций вместо JSONP лучше обычный CORS-запрос.
- Скрипты не выполняются без явного
dataType: "script". 3.0 уже запрещал это для кросс-доменных запросов; 4.0 распространил ограничение и на запросы в пределах одного домена. Нужен $.getScript() или dataType: "script".
- Транспорт скриптов теперь всегда использует тег
<script>, а не только для кросс-доменных запросов, как раньше. При строгом CSP стоит перепроверить, что политика разрешает нужные источники скриптов.
- Поддержка
FormData и бинарных данных в data встроена в ядро (по руководству по обновлению это может изменить порядок вызова префильтров). Если код должен работать и на 3.x, продолжайте явно указывать processData: false, contentType: false — на 4.0 это не мешает.
Подробный разбор остальных изменений библиотеки, не касающихся Ajax, — в статье что изменилось в jQuery 4.0.
Частые ошибки
| Симптом | Причина | Решение |
parsererror при статусе 200 | Указан dataType: "json", а тело ответа пустое или не является валидным JSON | Проверить фактическое тело ответа; возвращать с сервера {} вместо пустой строки, либо не задавать dataType для пустых ответов |
jqXHR.status === 0 без деталей | Запрос не дошёл до сервера: обрыв сети, CORS-блокировка, .abort() | Смотреть вкладку «Сеть» в devtools — там видна реальная причина |
| Ошибка CORS в консоли | Сервер не отправил Access-Control-Allow-Origin | Настраивается на сервере, а не в jQuery |
| Форма перезагружает страницу | В обработчике submit не вызван event.preventDefault() | Добавить event.preventDefault() первой строкой обработчика |
| Форма отправляется дважды | Кнопка не блокируется на время запроса | Блокировать кнопку в начале обработчика, снимать в .always() |
Объект в data уходит как form-urlencoded вместо JSON | Не вызван JSON.stringify(), не изменён contentType | data: JSON.stringify(obj), contentType: "application/json" |
| GET-запрос возвращает старые данные | Браузер отдаёт закешированный ответ | Указать cache: false в настройках запроса |
То же на fetch
Нативный fetch() решает ту же задачу иначе: он ближе к «сырому» HTTP и не угадывает формат данных за вас.
| Что нужно | jQuery | fetch |
| GET-запрос с JSON-ответом | $.getJSON(url) | fetch(url).then(r => r.json()) |
| Реакция на HTTP-ошибку (4xx/5xx) | Автоматически идёт в .fail() | fetch отклоняет промис только при сетевой ошибке; статус 4xx/5xx — это «успешный» resolve, ошибку нужно проверять вручную по response.ok |
| Отправка JSON | data: JSON.stringify(obj), contentType: "application/json" | body: JSON.stringify(obj), headers: { "Content-Type": "application/json" } |
| Отмена запроса | jqXHR.abort() | AbortController + signal в опциях |
| Отправка формы с файлами | data: new FormData(form), processData: false, contentType: false | body: new FormData(form) — fetch сам не выставляет Content-Type для FormData |
Переход на fetch оправдан там, где jQuery остаётся только ради Ajax-слоя. Подробнее о соответствиях jQuery и нативного JS — в статье jQuery и чистый JavaScript; о событии submit и отправке форм — в статьях события в jQuery: .on() и .off() и формы в jQuery.
Чек-лист
- Для типовых запросов — короткие методы (
$.get, $.post, $.getJSON), для нестандартных настроек (заголовки, таймаут, отмена, JSON-тело) — сразу $.ajax().
- Обрабатывать результат через
.done()/.fail()/.always() (или async/await), а не через методы jqXHR.success()/.error() — их убрали в jQuery 3.0.
- Для JSON-тела запроса — обязательно
JSON.stringify() и contentType: "application/json"; один dataType: "json" отвечает только за формат ответа.
- Файлы — через
FormData с processData: false, contentType: false вместе.
- Кнопку отправки формы — блокировать на время запроса, снимать блокировку в
.always().
- Живой поиск и другие запросы, которые могут перекрываться, — хранить jqXHR и вызывать
.abort() у предыдущего перед новым запросом.
- CORS настраивается на сервере; на клиенте — только
xhrFields.withCredentials, если нужны cookies кросс-доменно.
- В jQuery 4.0 JSONP и выполнение скриптов через Ajax требуют явного
dataType — неявных автопревращений больше нет.
Частые вопросы
Чем $.post() отличается от $.ajax()?
Ничем принципиальным — $.post(url, data, success, dataType) это сокращённая запись $.ajax({ url: url, method: "POST", data: data, success: success, dataType: dataType }). Внутри $.post() вызывает тот же $.ajax(), просто с меньшим числом аргументов и заранее заданным методом. Если нужны настройки, которых нет в сокращённой сигнатуре (заголовки, timeout, contentType), используйте $.ajax() напрямую или объектный синтаксис $.post({ url: url, data: data, timeout: 5000 }) — единственный аргумент-объект настроек, доступный с jQuery 1.12/2.2.
Почему срабатывает .fail(), хотя сервер вернул статус 200?
Чаще всего дело в dataType. Если явно или неявно (через «intelligent guess» по заголовку ответа) ожидается json, а тело ответа пустое или не является валидным JSON, jQuery не может его распарсить и вызывает .fail() с textStatus === "parsererror", хотя HTTP-статус успешный. Проверьте фактическое тело ответа во вкладке «Сеть» и либо возвращайте с сервера корректный JSON (например, {} вместо пустой строки), либо не указывайте dataType: "json" для ответов без тела.
Как отправить файл через Ajax в jQuery?
Собрать FormData и отключить автоматическую обработку данных: $.ajax({ url, method: "POST", data: new FormData(form), processData: false, contentType: false }). processData: false запрещает jQuery сериализовать FormData в строку, а contentType: false — не выставлять свой заголовок, чтобы браузер сам подставил multipart/form-data с правильным boundary. Если задать contentType вручную, boundary потеряется и сервер не разберёт форму.
Как отправить JSON, а не form-urlencoded?
Данные нужно сериализовать самостоятельно и указать тип содержимого: $.ajax({ url, method: "POST", data: JSON.stringify(obj), contentType: "application/json", dataType: "json" }). Если передать обычный объект в data без JSON.stringify(), jQuery по умолчанию превратит его в строку вида key=value&key2=value2 с contentType: "application/x-www-form-urlencoded" — это самая частая причина, почему сервер, ожидающий JSON, получает пустое тело или падает с ошибкой парсинга.
success или done — что использовать?
Колбэк success в настройках $.ajax() работает и в jQuery 4.0, он никуда не делся. Но начиная с 3.0 у самого jqXHR методы .success()/.error()/.complete() удалены — это разные вещи, и путаница между ними встречается часто. В новом коде удобнее методы Deferred — .done()/.fail()/.always(): их можно вызывать цепочкой уже после получения jqXHR, не запихивая всю логику в объект настроек, и они одинаково работают у $.ajax(), $.get(), $.post() и $.getJSON().
Как показать индикатор загрузки на время запроса?
Два варианта. Точечный — показать индикатор перед конкретным запросом и скрыть в колбэке .always(), который срабатывает и при успехе, и при ошибке. Глобальный — подписаться на события ajaxStart (показ индикатора) и ajaxStop (скрытие) через $(document).on(…): первый сработает, когда начнётся первый из одновременных запросов, второй — когда завершится последний, так что при нескольких параллельных запросах индикатор не мигнёт раньше времени.
[ наверх ]