
本文详解如何用纯前端技术构建 20 题交互式问卷系统,通过 dom 动态渲染问题、收集用户选择,并在完成后生成可下载的个性化建议清单(.txt 文件),全程无需后端或 node.js fs 模块。
本文详解如何用纯前端技术构建 20 题交互式问卷系统,通过 dom 动态渲染问题、收集用户选择,并在完成后生成可下载的个性化建议清单(.txt 文件),全程无需后端或 node.js fs 模块。
在浏览器环境中,JavaScript 无法直接访问本地文件系统(如 fs.writeFile),也不能指定保存路径——这是出于严格的安全沙箱限制。因此,原问题中依赖 Node.js 的 fs 模块、尝试在浏览器里写入磁盘文件的写法必然失败。但好消息是:完全可通过纯前端方案优雅实现目标——即动态呈现问题、构建决策逻辑、汇总答案,并触发用户可控的文件下载。
✅ 正确的技术路径
HTML 结构负责容器与交互入口
使用 <div id="question-container"> 承载当前题目,<button id="next-btn"> 推进流程,<div id="results-section" hidden> 在最后展示汇总与下载按钮。JavaScript 控制状态与流程
定义包含 20 道题及其选项的数组(支持单选/多选),用 currentQuestionIndex 跟踪进度;每答一题,将答案(如 "part-time-job": true)存入一个 responses 对象中。-
决策树逻辑嵌入 JS
所有分支判断(如“是否愿意做兼职?”→ 触发“推荐平台”)均在客户端完成。例如:const recommendations = []; if (responses["willing-part-time"]) recommendations.push("注册Upwork或本地招聘平台"); if (responses["has-items-to-sell"]) recommendations.push("整理闲置物品,策划周末社区跳蚤市场"); if (responses["can-save-monthly"]) recommendations.push("开通指数基金定投(如沪深300ETF)"); -
安全导出为 .txt 文件(关键技巧)
利用 Blob + URL.createObjectURL() 创建内存中文件,再通过 <a download="my-plan.txt"> 触发下载:function downloadResults() { const content = recommendations.join("\n"); const blob = new Blob([content], { type: "text/plain;charset=utf-8" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "my-decision-plan.txt"; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); // 清理内存引用 }
⚠️ 常见误区与修正说明
❌ 错误:<script>src="app.js"</script> —— 缺少 src 属性语法,且未加 defer 可能导致 DOM 尚未加载就执行脚本。
✅ 正确:<script defer src="app.js"></script>(确保脚本在 DOM 解析完毕后执行)。❌ 错误:while (num <= 10) { if (num === 21) break; } —— 条件永远不成立,逻辑冗余且易引发困惑。
✅ 正确:用 for (let i = 0; i < questions.length; i++) 或 questions.forEach(...) 显式遍历题目数组。❌ 错误:浏览器中调用 require('fs') —— require 是 Node.js 环境专属,浏览器无 CommonJS 支持。
✅ 正确:彻底放弃 fs,改用 Blob + a[download] 实现“导出”语义。
? 完整最小可行示例(精简版)
<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>决策行动指南</title>
</head>
<body>
<div id="question-container"></div>
<button id="next-btn">下一步</button>
<div id="results-section" hidden>
<h2>您的个性化建议清单:</h2>
<pre id="results-output"></pre>
<button id="download-btn">? 下载我的计划</button>
</div>
<script>
const questions = [
{ text: "您是否愿意从事兼职工作?", key: "willing-part-time", options: ["是", "否"] },
{ text: "您是否有闲置物品可出售?", key: "has-items-to-sell", options: ["是", "否"] },
{ text: "您能否每月稳定储蓄500元以上?", key: "can-save-monthly", options: ["是", "否"] }
// ……补全至20题(此处仅示意)
];
let currentIdx = 0;
const responses = {};
const $container = document.getElementById("question-container");
const $nextBtn = document.getElementById("next-btn");
const $resultsSection = document.getElementById("results-section");
const $resultsOutput = document.getElementById("results-output");
const $downloadBtn = document.getElementById("download-btn");
function renderQuestion() {
const q = questions[currentIdx];
$container.innerHTML = `
<h3>第${currentIdx + 1}题:${q.text}</h3>
${q.options.map((opt, i) =>
`<label><input type="radio" name="q${currentIdx}" value="${opt}"> ${opt}</label><br>`
).join('')}
`;
}
$nextBtn.addEventListener("click", () => {
const selected = document.querySelector(`input[name="q${currentIdx}"]:checked`);
if (!selected) return;
responses[questions[currentIdx].key] = selected.value === "是";
if (currentIdx < questions.length - 1) {
currentIdx++;
renderQuestion();
} else {
// 所有问题结束,生成建议并显示结果区
const recs = [];
if (responses["willing-part-time"]) recs.push("✅ 注册自由职业平台(如程序员客栈、圆领)");
if (responses["has-items-to-sell"]) recs.push("✅ 整理旧物,发起微信闲置群或闲鱼专场");
if (responses["can-save-monthly"]) recs.push("✅ 开通支付宝「笔笔攒」或银行智能定投");
$resultsOutput.textContent = recs.length
? recs.join("\n")
: "暂无匹配建议,请检查答案或拓展选项范围。";
$resultsSection.hidden = false;
$container.hidden = true;
$nextBtn.hidden = true;
}
});
$downloadBtn.addEventListener("click", () => {
const blob = new Blob([$resultsOutput.textContent], { type: "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob);
const a = Object.assign(document.createElement("a"), { href: url, download: "my-action-plan.txt" });
document.body.append(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
renderQuestion(); // 初始化首题
</script>
</body>
</html>? 总结与进阶提示
- 本方案100% 运行于浏览器端,零服务端依赖,适合快速原型、教育演示或离线工具。
- 若需持久化存储(如跨会话保留答案),可结合 localStorage;若需更复杂逻辑(如权重计算、图谱推理),可引入轻量库如 json-logic-js。
- 后续优化方向:添加进度条、响应式布局、答案回溯、导出为 PDF(借助 jsPDF 库)等。
- 不必转向 Python——本需求恰恰是前端最擅长的交互+轻量数据处理场景;Python 更适合后台批处理、API 服务或桌面应用(需打包为 exe/dmg)。
只要理解浏览器安全模型、善用 DOM API 与现代 Web API(Blob、URL),你就能用 HTML + JavaScript 构建出专业级决策辅助工具。



















