拖拽匹配题应采用data-id/data-target关联的结构化dom:题干用drag-item[data-id],选项用draggable[data-value],目标区用drop-zone[data-target];php校验需预处理json、验证键合法性、白名单比对值并防xss;禁用session存草稿,改用前端维护状态;答案存储推荐关系表而非json字段。

拖拽匹配题的 DOM 结构怎么组织才方便 JS 操作?
拖拽匹配题本质是「源容器 → 目标容器」的元素移动,但 PHP 不直接参与拖拽交互——它只负责生成初始 HTML 和校验答案。关键在于让 PHP 输出的结构能被前端 JS 无歧义识别。常见错误是把所有 draggable 元素平铺、不标记题干/选项/答案区关联关系,导致 JS 匹配逻辑混乱。
- 每个题干项用
<div class="drag-item" data-id="q1">,<code>data-id唯一标识题干(如“鲁迅的代表作”) - 每个可拖拽选项用
<span draggable="true" data-value="狂人日记"></span>,data-value存标准答案值 - 每个目标区域用
<div class="drop-zone" data-target="q1">,<code>data-target与题干data-id对应 - PHP 渲染时确保同一题的所有关联元素
data-id/data-target严格一致,避免大小写或空格差异 - 用
filter_input(INPUT_POST, 'answers', FILTER_SANITIZE_SPECIAL_CHARS)预处理原始字符串,再json_decode(..., true) - 校验键是否全在预设题号数组中:
array_keys($answers) === array_keys($expected_questions) - 对每个值做白名单检查:
in_array($answers['q1'], $valid_options['q1'], true),不能只用==(防字符串隐式转换) - 答案字段必须用
htmlspecialchars()再存入数据库,防止 XSS 注入到后续管理后台展示页 - 拖拽是瞬时 UI 行为,PHP 无法感知 drag/drop 过程,session 只能在完整表单提交时更新
- 若强行用 AJAX 实时同步,会因网络延迟导致多次拖拽覆盖彼此(例如拖 A→B,还没响应又拖 C→B,后端收到两个请求,后者覆盖前者)
- 正确做法是:前端用 JS 维护当前拖拽状态(如
localStorage),PHP 只处理最终提交;若需防丢失,用beforeunload提示保存草稿,而非依赖 session - 推荐结构:
answer_records表含字段user_id、question_id、selected_value、created_at -
selected_value存标准化选项值(如“狂人日记”),而非 ID——因为选项文本可能随题目版本变更,但历史答案必须保留原始语义 - 给
(question_id, selected_value)加联合索引,支撑高频聚合查询
这样 JS 只需监听 dragstart 记录 data-value,在 drop 时比对 data-target,就能知道“谁拖给了谁”。
PHP 如何安全接收并校验拖拽提交的答案?
用户提交的是前端拼装的 JSON,比如 {"q1":"狂人日记","q2":"背影"},PHP 必须防篡改、防缺失、防类型错乱。常见错误是直接 json_decode($_POST['answers']) 后就比对,忽略键合法性与值白名单。
别信前端传来的任何 data-value——它可能被开发者工具直接改掉。PHP 的校验不是“辅助”,而是唯一可信防线。
为什么拖拽题不能靠 session 存答案草稿?
有些开发者想用 PHP session 记录用户每拖一次的状态,实现“断点续答”。这在并发请求下极易出错:用户多窗口操作、网络重试、浏览器刷新都会让 session 中的中间态与实际 DOM 脱节。
session 是服务端状态,拖拽是客户端交互节奏——两者时间尺度根本不匹配,硬绑只会埋雷。
MySQL 存拖拽题答案用 JSON 字段还是关系表?
JSON 类型看着省事,但拖拽题答案常需统计(如“多少人选了《阿Q正传》匹配‘鲁迅’”),JSON 查询性能差且难索引。关系表虽多一张表,但扩展性强。
用 JSON 字段等于把校验、查询、迁移的麻烦全推给后期。初期多建两列,远比半年后写一堆正则解析 JSON 快。











