Sending Data Without a Page Reload: jQuery and AJAX
The 2014 standard for decoupling form submissions from full page loads: practical usage of jQuery's $.ajax method.
Until a few months ago, having the page reload every time a user submitted a form was perfectly normal to me. The user clicks the button, the page flickers, and the same page comes back with a different message. It worked — but it felt rough.
AJAX (Asynchronous JavaScript and XML) is a technique that lets the browser send a request to the server and receive a response without reloading the page. JSON has largely replaced XML in practice, but the name stuck. jQuery’s $.ajax method was the most widely used way to do this in 2014.
Why jQuery?
JavaScript’s built-in XMLHttpRequest object can handle AJAX, but you end up dealing with browser inconsistencies by hand. jQuery abstracts all of that complexity and behaves the same way across every browser. Every project I looked at back then had jQuery; there was no reason to learn anything else in that environment.
Basic $.ajax usage
$.ajax({
url: '/api/yorum-ekle',
method: 'POST',
data: {
yorum: 'Bu içerik çok faydalıydı.',
makale_id: 42
},
success: function(yanit) {
console.log('Başarılı:', yanit);
},
error: function(xhr, durum, hata) {
console.log('Hata:', hata);
}
});
url is the destination address, method is the HTTP verb, and data holds the payload. The success and error callbacks fire based on the server’s response.
Wiring a form submission to AJAX
The scenario I used most often in practice: the user fills out a form and submits it, the operation completes without a page reload, and a message is displayed to the user.
HTML side:
<form id="yorumFormu">
<textarea name="yorum" id="yorumAlani"></textarea>
<button type="submit">Gönder</button>
</form>
<div id="mesaj"></div>
JavaScript side:
$('#yorumFormu').on('submit', function(e) {
e.preventDefault(); // Prevent the page from reloading
var yorum = $('#yorumAlani').val();
$.ajax({
url: '/yorum-ekle',
method: 'POST',
data: { yorum: yorum },
dataType: 'json',
success: function(yanit) {
if (yanit.basarili) {
$('#mesaj').text('Yorumunuz eklendi.').show();
$('#yorumAlani').val('');
}
},
error: function() {
$('#mesaj').text('Bir hata oluştu, tekrar deneyin.').show();
}
});
});
The e.preventDefault() line is critical — without it, the browser submits the form normally and the page reloads.
Server side
On the PHP side, receiving the request and returning JSON is all you need:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$yorum = trim($_POST['yorum'] ?? '');
if (!empty($yorum)) {
// Save to database...
echo json_encode(['basarili' => true]);
} else {
echo json_encode(['basarili' => false, 'mesaj' => 'Yorum boş olamaz.']);
}
exit;
}
Setting the Content-Type header to application/json is good practice:
header('Content-Type: application/json');
Forgetting this header won’t cause an outright failure, but browsers and client libraries rely on it to process the response correctly. In particular, jQuery’s dataType: 'json' option should be consistent with this header — mismatches can be confusing to debug.
$.get and $.post shortcuts
There are shortcuts for the most common HTTP verbs:
// GET request
$.get('/api/makaleler', function(yanit) {
console.log(yanit);
});
// POST request
$.post('/api/yorum-ekle', { yorum: 'Merhaba' }, function(yanit) {
console.log(yanit);
});
These are shorthands for $.ajax. They are convenient for simple cases, but when you need error handling, $.ajax gives you more flexibility.
Loading indicator
It is good practice to let the user know that a request is in progress:
$.ajax({
url: '/api/veri',
beforeSend: function() {
$('#yukleniyor').show();
},
complete: function() {
$('#yukleniyor').hide();
},
success: function(yanit) {
// handle response
}
});
beforeSend fires before the request starts; complete fires when the request finishes, whether it succeeded or failed.
One trap to avoid: hiding the indicator inside success instead of complete. If the server returns an error, success never runs and the spinner stays on screen forever. Because complete always fires, that is the right place to put the cleanup.
Security: CSRF protection
A topic that often gets skipped with AJAX requests: CSRF (Cross-Site Request Forgery) protection. If you are using Laravel or another framework on the PHP side, POST requests typically require a token to be validated.
You need to send the token from the meta tag in your HTML along with every AJAX request:
<meta name="csrf-token" content="<?php echo csrf_token(); ?>">
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
Once you define this with $.ajaxSetup, every subsequent $.ajax call automatically includes the header. A POST request sent without the token will get a 419 (Page Expired) from Laravel — figuring out why can take a while the first time.
What I’ve learned so far
Building an interface that works without page reloads made a noticeable difference to the user experience. When I first implemented it, I had that “this is it” moment; the page no longer flickering seems like a small detail, but the improvement is significant.
jQuery made this easy, but understanding what is happening underneath matters too. To really appreciate the abstraction sitting on top of XMLHttpRequest, it is worth trying to write one by hand. Once you have done that, it becomes much clearer why jQuery became so widespread.
The hardest part of learning AJAX for me was shifting my mental model — treating the response as an event rather than a value. Instead of “fetch this data, then do that,” you have to think “when this request completes, run this function.” For someone coming from PHP, that mindset shift takes time: PHP executes line by line, each statement waiting for the previous one to finish. JavaScript, by contrast, says “call me when it’s ready” and moves on. Once you internalize that difference, the reason success callbacks are structured the way they are becomes much clearer.
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.