如何使用 AJAX 实现单页逐题问答系统(True/False 问卷)

老瑶姑娘_6615

老瑶姑娘_6615

2026-09-08

432人浏览

原创

如何使用 AJAX 实现单页逐题问答系统(True/False 问卷)

本文介绍如何通过原生 javascript fetch api 与 php 后端协同,构建一个无需页面刷新、逐题呈现 400+ 道 true/false 问题的单页问卷系统,并将用户答案(1 或 0)按序持久化至文本文件。

本文介绍如何通过原生 javascript fetch api 与 php 后端协同,构建一个无需页面刷新、逐题呈现 400+ 道 true/false 问题的单页问卷系统,并将用户答案(1 或 0)按序持久化至文本文件。

在传统表单提交中,每次点击“True”或“False”都会触发整页刷新,导致体验割裂、状态丢失且无法平滑过渡——尤其面对 400+ 题量时,性能与可用性均受挑战。理想的解决方案是采用 AJAX(推荐现代 fetch)实现前后端异步通信,使前端仅更新问题区域与按钮状态,后端专注处理逻辑与数据落盘。

以下是一个完整、可直接运行的单页应用(SPA)实现,包含清晰的职责划分:

AJAX技术—无刷新获取数据相关课件
AJAX技术—无刷新获取数据相关课件

AJAX技术—无刷新获取数据相关课件

下载

✅ 核心设计要点

  • 无刷新交互:所有问题切换均由 fetch() 完成,不依赖 form.submit();
  • 状态驱动流程:前端通过 data-id 跟踪当前题号,后端依据 action=start|question 分阶段响应;
  • 结构化日志:答案文件(data.cpx)以 q:2,a:0 格式记录,含题号与答案,便于后续解析;
  • 边界安全处理:自动识别最后一题并禁用按钮,防止越界提交;
  • 轻量可靠:不依赖 jQuery,纯原生 JS + 基础 PHP,降低维护成本。

? 后端 PHP 处理逻辑(嵌入同一文件,如 CPI_Test.php)

<?php if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
    ob_clean();

    $questionsFile = 'CPXQ.dat';
    $answersFile   = 'data.cpx';

    // 读取全部题目(自动去除换行符)
    $lines = file($questionsFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    if (!$lines) die('Failed to load questions.');

    switch ($_POST['action']) {
        case 'start':
            header('Content-Type: text/plain');
            echo $lines[0] ?? 'No questions available.';
            exit;
            break;

        case 'question':
            $id = (int)$_POST['id'];
            $answer = (int)$_POST['answer']; // 1 for True, 0 for False

            // 保存当前答案(含题号)
            $logEntry = sprintf("q:%d,a:%d", $id + 1, $answer) . PHP_EOL;
            file_put_contents($answersFile, $logEntry, FILE_APPEND | LOCK_EX);

            // 计算下一题索引
            $nextId = $id + 1;

            if ($nextId < count($lines)) {
                // 返回下一题数据(JSON)
                header('Content-Type: application/json');
                echo json_encode([
                    'id'       => $nextId,
                    'question' => $lines[$nextId]
                ]);
            } else {
                // 最终题已答完
                header('Content-Type: application/json');
                echo json_encode([
                    'id'       => 0,
                    'question' => 'End of questionnaire. Thank you!'
                ]);
            }
            exit;
            break;
    }
}
?>

? 前端 HTML + JavaScript(同文件内)



  <meta charset="UTF-8"><title>CPI 测验系统</title><style>
    body { font-family: "Segoe UI", sans-serif; max-width: 720px; margin: 2rem auto; padding: 0 1rem; }
    fieldset { background: #f9f9f9; padding: 1.5rem; border-radius: 6px; margin-bottom: 1.5rem; }
    button { padding: 0.75rem 2rem; font-size: 1.1rem; margin: 0.5rem; cursor: pointer; }
    button:disabled { opacity: 0.6; cursor: not-allowed; }
  </style><h1>CPI TEST</h1>
  
<script> const fieldset = document.querySelector('fieldset'); const form = document.forms.questions; const fd = new FormData(); // 加载首题 fd.set('action', 'start'); fetch(location.href, { method: 'POST', body: fd }) .then(r => r.text()) .then(text => fieldset.innerHTML = text.trim()); // 按钮点击处理(事件委托) form.addEventListener('click', e => { if (e.target.tagName !== 'BUTTON') return; const value = e.target.dataset.value; const id = e.target.dataset.id; fd.set('action', 'question'); fd.set('id', id); fd.set('answer', value); fetch(location.href, { method: 'POST', body: fd }) .then(r => r.json()) .then(data => { fieldset.innerHTML = data.question; // 更新所有按钮的 data-id;到达终点则禁用 if (data.id === 0) { form.querySelectorAll('button').forEach(btn => btn.disabled = true); } else { form.querySelectorAll('button').forEach(btn => btn.dataset.id = data.id); } }) .catch(err => console.error('Submission failed:', err)); }); </script>

⚠️ 注意事项与进阶建议

  • 并发安全:当前方案使用共享文本文件,不适用于多用户并发场景。生产环境强烈建议改用数据库(如 MySQL),为每位用户分配唯一 session_id 或 user_id,并将答案存为 (user_id, question_id, answer, timestamp) 结构化记录。
  • 防回退机制:如需禁止返回上一题,可在 JS 中移除历史记录(history.replaceState()),或服务端校验 id 严格递增(拒绝 id ≤ 已存最大题号 的请求)。
  • 错误防护:实际部署前应增加 PHP 错误日志、file_put_contents 权限检查、$lines 数组长度校验等健壮性措施。
  • 用户体验优化:可添加加载状态(如按钮置灰+文字变为“Submitting…”)、答题计时、进度条(<progress value="5" max="400"></progress>)等增强交互反馈。

该方案以最小技术栈达成核心目标:流畅、可靠、可扩展。掌握其设计思想后,即可轻松适配更复杂的问卷逻辑(如跳题规则、多选、评分计算等)。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

ajax

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
php文件怎么打开
php文件怎么打开

打开php文件步骤:1、选择文本编辑器;2、在选择的文本编辑器中,创建一个新的文件,并将其保存为.php文件;3、在创建的PHP文件中,编写PHP代码;4、要在本地计算机上运行PHP文件,需要设置一个服务器环境;5、安装服务器环境后,需要将PHP文件放入服务器目录中;6、一旦将PHP文件放入服务器目录中,就可以通过浏览器来运行它。

2023.09.01

9804

6

php怎么取出数组的前几个元素
php怎么取出数组的前几个元素

取出php数组的前几个元素的方法有使用array_slice()函数、使用array_splice()函数、使用循环遍历、使用array_slice()函数和array_values()函数等。本专题为大家提供php数组相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.11

5861

5

php反序列化失败怎么办
php反序列化失败怎么办

php反序列化失败的解决办法检查序列化数据。检查类定义、检查错误日志、更新PHP版本和应用安全措施等。本专题为大家提供php反序列化相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.11

2075

5

php怎么连接mssql数据库
php怎么连接mssql数据库

连接方法:1、通过mssql_系列函数;2、通过sqlsrv_系列函数;3、通过odbc方式连接;4、通过PDO方式;5、通过COM方式连接。想了解php怎么连接mssql数据库的详细内容,可以访问下面的文章。

2023.10.23

3648

4

php连接mssql数据库的方法
php连接mssql数据库的方法

php连接mssql数据库的方法有使用PHP的MSSQL扩展、使用PDO等。想了解更多php连接mssql数据库相关内容,可以阅读本专题下面的文章。

2023.10.23

4354

6

html怎么上传
html怎么上传

html通过使用HTML表单、JavaScript和PHP上传。更多关于html的问题详细请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.03

3411

9

PHP出现乱码怎么解决
PHP出现乱码怎么解决

PHP出现乱码可以通过修改PHP文件头部的字符编码设置、检查PHP文件的编码格式、检查数据库连接设置和检查HTML页面的字符编码设置来解决。更多关于php乱码的问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.09

4857

8

php文件怎么在手机上打开
php文件怎么在手机上打开

php文件在手机上打开需要在手机上搭建一个能够运行php的服务器环境,并将php文件上传到服务器上。再在手机上的浏览器中输入服务器的IP地址或域名,加上php文件的路径,即可打开php文件并查看其内容。更多关于php相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.13

3802

8

sprintf函数用法详解
sprintf函数用法详解

sprintf函数的用法:1、格式化字符串;2、指定输出宽度和精度;3、返回值。更多关于sprintf函数用法详解的内容,大家可以阅读下面的文章。

2023.11.27

11782

4

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
jQuery官方API文档
jQuery官方API文档

共0课时 | 0人学习

Symfony教程(入门篇+基础篇)
Symfony教程(入门篇+基础篇)

共18课时 | 1.8万人学习