AJAX Poll
PHP Example - AJAX Poll
Section titled “PHP Example - AJAX Poll”AJAX Polling Example
Section titled “AJAX Polling Example”This example demonstrates creating a simple poll where user votes are submitted and results are displayed asynchronously using AJAX (specifically, the modern fetch API) without requiring a full page reload.
Conceptual Poll Interface:
<div id="poll-container"> <h3>Do you enjoy learning modern PHP and AJAX?</h3> <form id="poll-form"> <label> <input type="radio" name="vote" value="yes"> Yes </label> <br> <label> <input type="radio" name="vote" value="no"> No </label> <br><br> <button type="button" id="submit-vote">Submit Vote</button> </form> <div id="poll-results" style="margin-top: 15px;"></div></div>Example Explained - The HTML & JavaScript (using fetch)
Section titled “Example Explained - The HTML & JavaScript (using fetch)”When the user selects an option and clicks the button, JavaScript captures the vote and sends it to a PHP backend script using the fetch API. The response (containing the updated poll results) is then used to update the content of the <div id="poll-results">.
<!DOCTYPE html><html><head><title>AJAX Poll</title><style> #poll-results table { border-collapse: collapse; margin-top: 10px; } #poll-results td { padding: 5px; border: 1px solid #ccc; } .poll-bar { background-color: lightblue; height: 20px; display: inline-block; vertical-align: middle; } .error { color: red; margin-top: 10px; }</style></head><body>
<div id="poll-container"> <h3>Do you enjoy learning modern PHP and AJAX?</h3> <form id="poll-form"> <label> <input type="radio" name="vote" value="yes" required> Yes </label> <br> <label> <input type="radio" name="vote" value="no"> No </label> <br><br> <button type="button" id="submit-vote">Submit Vote</button> </form> <div id="poll-results" style="margin-top: 15px;"></div> <div id="poll-error" class="error"></div></div>
<script>document.getElementById('submit-vote').addEventListener('click', function() { const form = document.getElementById('poll-form'); const resultsDiv = document.getElementById('poll-results'); const errorDiv = document.getElementById('poll-error'); const selectedOption = form.querySelector('input[name="vote"]:checked');
errorDiv.textContent = ''; // Clear previous errors resultsDiv.innerHTML = 'Voting...'; // Provide feedback
if (!selectedOption) { errorDiv.textContent = 'Please select an option.'; resultsDiv.innerHTML = ''; return; }
const voteValue = selectedOption.value; const formData = new FormData(); formData.append('vote', voteValue);
// Send the vote to the PHP backend using fetch (POST method recommended for state changes) fetch('poll_vote.php', { method: 'POST', body: formData }) .then(response => { if (!response.ok) { // Handle HTTP errors (like 404, 500) throw new Error(`HTTP error! status: ${response.status}`); } return response.text(); // Get the response body as text (HTML) }) .then(html => { // Update the results area with the HTML received from PHP resultsDiv.innerHTML = html; }) .catch(error => { // Handle fetch errors (network issues, HTTP errors) console.error('Fetch Error:', error); errorDiv.textContent = 'Error submitting vote. Please try again. (' + error.message + ')'; resultsDiv.innerHTML = ''; // Clear 'Voting...' message });});</script>
</body></html>JavaScript Breakdown:
- An event listener waits for the button click.
- It finds the selected radio button’s value.
- A
FormDataobject is created to hold thevotevalue. fetch()sends aPOSTrequest topoll_vote.phpwith the form data..then()handles the successful response: checks if the HTTP status is OK, then reads the response body as text (expecting HTML from the PHP script).- The received HTML is inserted into the
#poll-resultsdiv. .catch()handles errors during the fetch process (e.g., network down, server error).- Basic validation and user feedback are included.
The PHP Backend (poll_vote.php)
Section titled “The PHP Backend (poll_vote.php)”This PHP script receives the vote, updates the stored results (using a simple text file in this example), and then generates the HTML to display the current results.
Note on File Storage: Using a plain text file for storing results is simple but not suitable for high-traffic sites due to potential race conditions (multiple users writing simultaneously). A database (like MySQL or PostgreSQL) is the standard, more robust solution. This example uses file locking (LOCK_EX) as a basic measure against race conditions.
<?phpdeclare(strict_types=1);
$filename = "poll_result.txt";$yes = 0;$no = 0;
// --- Step 1: Read current results safely ---$fileHandle = fopen($filename, 'c+'); // Open for read/write, create if not existsif (!$fileHandle) { http_response_code(500); echo "Error: Cannot open data file."; exit;}
// Attempt to get an exclusive lock (prevents simultaneous writes)if (flock($fileHandle, LOCK_EX)) { $content = fread($fileHandle, (filesize($filename) > 0) ? filesize($filename) : 1); // Read existing content if ($content) { $parts = explode("||", $content); if (count($parts) === 2 && is_numeric($parts[0]) && is_numeric($parts[1])) { $yes = (int)$parts[0]; $no = (int)$parts[1]; } } // --- Step 2: Process the vote (if any) --- if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['vote'])) { $vote = $_POST['vote']; // Basic: consider more validation
if ($vote === 'yes') { $yes++; } elseif ($vote === 'no') { $no++; }
// --- Step 3: Write updated results back --- ftruncate($fileHandle, 0); // Clear the file content rewind($fileHandle); // Move pointer to the beginning fwrite($fileHandle, $yes . "||" . $no); }
flock($fileHandle, LOCK_UN); // Release the lock} else { fclose($fileHandle); http_response_code(503); // Service Unavailable (couldn't get lock) echo "Error: Could not lock data file. Please try again."; exit;}
fclose($fileHandle);
// --- Step 4: Calculate percentages and output HTML results ---$total = $yes + $no;$yesPercent = ($total > 0) ? round(($yes / $total) * 100) : 0;$noPercent = ($total > 0) ? round(($no / $total) * 100) : 0;
// Ensure percentages add up correctly after rounding (adjust largest if needed)if ($total > 0 && ($yesPercent + $noPercent) !== 100) { if ($yesPercent > $noPercent) $yesPercent = 100 - $noPercent; else $noPercent = 100 - $yesPercent;}
?><h2>Result:</h2><table> <tr> <td>Yes:</td> <td> <span class="poll-bar" style="width:<?php echo $yesPercent * 2; // Scale width for display ?>px;"></span> <?php echo $yesPercent; ?>% (<?php echo $yes; ?> votes) </td> </tr> <tr> <td>No:</td> <td> <span class="poll-bar" style="width:<?php echo $noPercent * 2; ?>px;"></span> <?php echo $noPercent; ?>% (<?php echo $no; ?> votes) </td> </tr></table>PHP Script Logic:
- Open/Lock File: Opens
poll_result.txtfor reading and writing (c+), creating it if necessary. Attempts to acquire an exclusive lock (flock) to prevent race conditions. - Read Data: Reads the current counts from the file (e.g.,
10||5). Handles empty or invalid file content. - Process Vote: Checks if the request is
POSTand if avotevalue (‘yes’ or ‘no’) is present. Increments the corresponding counter. - Write Data: Truncates the file, rewinds the pointer, and writes the updated counts back (e.g.,
11||5). - Unlock/Close File: Releases the lock (
flock) and closes the file handle. - Calculate & Output: Calculates percentages and generates the HTML table containing the results, including simple visual bars. This HTML is sent back as the response to the
fetchrequest.
The Data File (poll_result.txt)
Section titled “The Data File (poll_result.txt)”This simple text file stores the vote counts, separated by ||.
Example content:
25||12This represents 25 ‘Yes’ votes and 12 ‘No’ votes.
Important: Ensure the web server process (e.g., www-data, apache) has write permissions on this poll_result.txt file and the directory it resides in. Do not grant overly broad permissions (like 777).