W3docs

PHP AJAX Poll

Learn how to build an AJAX poll in PHP that records votes and shows live results without reloading the page, using fetch, JSON, and a MySQL table.

What Is an AJAX Poll?

An AJAX poll is a small voting widget that lets a user pick an option, submits that vote to the server in the background, and then shows the updated results — all without reloading the page. AJAX (Asynchronous JavaScript and XML) is the technique of exchanging data with the server using JavaScript after the page has already loaded.

This chapter builds a complete poll: a MySQL table to store the options and their vote counts, a PHP script that records a vote and returns the current tally as JSON, and a small amount of JavaScript that sends the vote and renders the results.

If you want to continuously refresh data on a timer (a live scoreboard, a status dashboard), the same building blocks apply — that pattern is called polling, and we cover it at the end of the chapter.

Why Use AJAX for a Poll?

  • No page reload. The user stays on the page; only the poll area changes. This feels instant and keeps the rest of the page (scroll position, video, form input) intact.
  • Less data transferred. Instead of re-sending the whole HTML document, the server returns only the vote counts as a small JSON payload.
  • Live results. Because the response is data, not markup, you can re-render a results bar immediately and even update it on a timer to reflect votes from other users.

Step 1 — Create the Database Table

Each poll option is one row. We store the option text and a running vote counter.

CREATE TABLE poll_options (
    id INT AUTO_INCREMENT PRIMARY KEY,
    option_text VARCHAR(100) NOT NULL,
    votes INT NOT NULL DEFAULT 0
);

INSERT INTO poll_options (option_text) VALUES
    ('PHP'),
    ('JavaScript'),
    ('Python');

If you are new to creating tables, see Create a MySQL Table and Insert Data.

Step 2 — Build the HTML Form

The form lists the options as radio buttons and reserves a container for the results.

<form id="pollForm">
  <p>What is your favorite language?</p>
  <label><input type="radio" name="option" value="1"> PHP</label>
  <label><input type="radio" name="option" value="2"> JavaScript</label>
  <label><input type="radio" name="option" value="3"> Python</label>
  <button type="submit">Vote</button>
</form>

<div id="pollResults"></div>

The value of each radio button is the option's id from the database — that is the only thing we need to send to the server.

Step 3 — Write the PHP Handler

The PHP script does two jobs: record a vote (when one is submitted) and return the current results as JSON. Always use prepared statements so a value coming from the browser can never be interpreted as SQL.

<?php
header('Content-Type: application/json');

$dsn = "mysql:host=localhost;dbname=your_database;charset=utf8mb4";
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];

try {
    $pdo = new PDO($dsn, 'username', 'password', $options);

    // If a vote was submitted, increment that option's counter.
    if (isset($_POST['option'])) {
        $optionId = (int) $_POST['option'];
        $update = $pdo->prepare(
            "UPDATE poll_options SET votes = votes + 1 WHERE id = :id"
        );
        $update->execute([':id' => $optionId]);
    }

    // Always return the current tally.
    $rows = $pdo->query(
        "SELECT id, option_text, votes FROM poll_options ORDER BY id"
    )->fetchAll();

    $total = array_sum(array_column($rows, 'votes'));

    echo json_encode([
        'status'  => 'success',
        'total'   => $total,
        'options' => $rows,
    ]);
} catch (PDOException $e) {
    http_response_code(500);
    echo json_encode(['status' => 'error', 'message' => 'Database error']);
}

Casting the incoming id with (int) and binding it with :id means the value is treated strictly as a number, not as part of the query. For more on this, read PHP Prepared Statements and json_encode().

Step 4 — Send the Vote with JavaScript

We intercept the form's submit event, send the selected option with fetch(), then draw a results bar from the JSON response.

const form = document.getElementById('pollForm');
const resultsBox = document.getElementById('pollResults');

form.addEventListener('submit', function (event) {
  event.preventDefault(); // Stop the normal page reload.

  const selected = form.querySelector('input[name="option"]:checked');
  if (!selected) return; // Nothing chosen yet.

  const body = new URLSearchParams({ option: selected.value });

  fetch('poll.php', { method: 'POST', body })
    .then(response => response.json())
    .then(showResults)
    .catch(error => console.error('Vote failed:', error));
});

function showResults(data) {
  if (data.status !== 'success') {
    resultsBox.textContent = 'Could not load results.';
    return;
  }

  resultsBox.innerHTML = data.options.map(opt => {
    const percent = data.total
      ? Math.round((opt.votes / data.total) * 100)
      : 0;
    return `<div>${opt.option_text}: ${opt.votes} votes (${percent}%)</div>`;
  }).join('');
}

event.preventDefault() is what stops the browser from doing a full submit, which is the whole point of AJAX. The percentage is computed on the client from the counts the server returned, so the bar always matches the latest data.

Step 5 — Keep Results Live with Polling

So far the results update only when this user votes. To reflect votes from other users, poll the server on a timer — request the latest tally every few seconds and re-render:

function refreshResults() {
  fetch('poll.php') // GET, no vote — just read the tally.
    .then(response => response.json())
    .then(showResults)
    .catch(error => console.error('Refresh failed:', error));
}

setInterval(refreshResults, 5000); // Every 5 seconds.

Because the PHP handler returns the tally on every request (vote or not), a plain GET is enough to fetch fresh numbers. Choose the interval carefully: a short interval feels more live but creates more requests. For data that must be truly real-time, WebSockets or Server-Sent Events are a better fit than fixed-interval polling.

Common Pitfalls

  • Forgetting event.preventDefault(). Without it the form reloads the page and the AJAX call is cancelled mid-flight.
  • Trusting the option id. Always cast/validate it and use a prepared statement; never concatenate $_POST into SQL.
  • Double counting. This basic example lets one browser vote many times. In production, restrict by session, cookie, or IP.
  • Polling too aggressively. A one-second interval on a busy page can overload the server; 3–10 seconds is usually plenty.

Summary

An AJAX poll combines a MySQL table, a PHP endpoint that records a vote and returns the tally as JSON, and JavaScript that submits with fetch() and renders the results — no page reload required. Add a setInterval poll to keep the numbers live for everyone.

graph TD;
    A(User Selects Option and Clicks Vote) --> B(JavaScript Sends POST with fetch);
    B --> C(PHP Increments Vote with Prepared Statement);
    C --> D(PHP Returns Tally as JSON);
    D --> E(JavaScript Renders Results Bar);
    F(Timer Every 5s) --> G(GET poll.php for Latest Tally);
    G --> D;

Practice

Practice
What are the steps involved in creating AJAX Poll in PHP?
What are the steps involved in creating AJAX Poll in PHP?
Was this page helpful?