使用JavaScript动态构建交互式问卷表单
在Web开发中,动态表单能够显著提升用户体验,尤其在问卷、调查或配置页面中。本文将通过JavaScript一步步构建一个交互式问卷表单,支持动态添加问题、选项、实时验证以及数据提交。
一、基础HTML结构
首先创建一个静态的容器页面,用于承载动态生成的内容。表单的骨架由JavaScript操作,因此只需要一个空的<div>作为挂载点。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动态问卷表单</title>
<style>
/* 基础样式,无内联样式干扰 */
body { font-family: Arial, sans-serif; margin: 20px; }
.question { margin-bottom: 15px; padding: 10px; border: 1px solid #ccc; }
.options { margin-left: 20px; }
.question-title { font-weight: bold; margin-bottom: 5px; }
.btn-group { margin-top: 20px; }
.error { color: red; font-size: 0.9em; }
</style>
</head>
<body>
<div id="app"></div>
<script src="app.js"></script>
</body>
</html>二、JavaScript核心代码
我们将所有逻辑封装在app.js中。核心思路:
使用一个数组
questions保存问卷问题数据。通过
renderQuiz()函数将数据渲染为DOM元素。提供“添加问题”、“删除问题”、“添加选项”等交互按钮。
提交时进行验证,并输出结果。
2.1 数据结构设计
每个问题为一个对象:
// 初始问题示例
const questions = [
{
type: 'radio', // radio, checkbox, text
title: '您最喜欢的前端框架是?',
options: ['React', 'Vue', 'Angular', 'Svelte']
},
{
type: 'checkbox',
title: '您使用过哪些编程语言?',
options: ['JavaScript', 'Python', 'Java', 'C#', 'Go']
},
{
type: 'text',
title: '请简述您的开发经验',
options: []
}
];2.2 渲染函数
renderQuiz()遍历questions数组,动态生成<div>元素,并添加到#app中。每个问题区块包含标题、选项或输入框,以及删除按钮。
const app = document.getElementById('app');
function renderQuiz() {
app.innerHTML = ''; // 清空容器
questions.forEach((q, index) => {
const questionDiv = document.createElement('div');
questionDiv.className = 'question';
questionDiv.dataset.index = index;
// 问题标题 + 删除按钮
const titleDiv = document.createElement('div');
titleDiv.className = 'question-title';
titleDiv.innerHTML = `问题 ${index + 1}: ${escapeHtml(q.title)}`;
const deleteBtn = document.createElement('button');
deleteBtn.textContent = '删除问题';
deleteBtn.onclick = function() {
questions.splice(index, 1);
renderQuiz();
};
titleDiv.appendChild(deleteBtn);
questionDiv.appendChild(titleDiv);
// 选项区域(针对 radio / checkbox)
if (q.type === 'radio' || q.type === 'checkbox') {
const optionsDiv = document.createElement('div');
optionsDiv.className = 'options';
q.options.forEach((opt, optIndex) => {
const label = document.createElement('label');
const input = document.createElement('input');
input.type = q.type;
input.name = `q${index}`;
input.value = opt;
if (q.type === 'checkbox') {
// 每个checkbox独立name
input.name = `q${index}_${optIndex}`;
}
label.appendChild(input);
label.appendChild(document.createTextNode(opt));
optionsDiv.appendChild(label);
optionsDiv.appendChild(document.createElement('br'));
});
// 添加选项按钮
const addOptBtn = document.createElement('button');
addOptBtn.textContent = '添加选项';
addOptBtn.onclick = function() {
const newOpt = prompt('请输入新选项:');
if (newOpt && newOpt.trim()) {
q.options.push(newOpt.trim());
renderQuiz();
}
};
optionsDiv.appendChild(addOptBtn);
questionDiv.appendChild(optionsDiv);
} else if (q.type === 'text') {
const textarea = document.createElement('textarea');
textarea.rows = 3;
textarea.placeholder = '请输入文本...';
textarea.dataset.questionIndex = index;
questionDiv.appendChild(textarea);
}
// 错误信息容器
const errorDiv = document.createElement('div');
errorDiv.className = 'error';
errorDiv.id = `error-${index}`;
questionDiv.appendChild(errorDiv);
app.appendChild(questionDiv);
});
// 添加整体验证和提交按钮
const btnGroup = document.createElement('div');
btnGroup.className = 'btn-group';
const submitBtn = document.createElement('button');
submitBtn.textContent = '提交问卷';
submitBtn.onclick = submitQuiz;
const addQuestionBtn = document.createElement('button');
addQuestionBtn.textContent = '新增问题';
addQuestionBtn.onclick = addQuestion;
btnGroup.appendChild(addQuestionBtn);
btnGroup.appendChild(submitBtn);
app.appendChild(btnGroup);
}
// 防止XSS的简单转义函数
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}2.3 添加问题功能
用户点击“新增问题”按钮时,弹窗询问问题类型和标题,然后将其添加到questions数组中并重新渲染。
function addQuestion() {
const type = prompt('请输入问题类型(radio / checkbox / text):', 'radio');
if (!['radio', 'checkbox', 'text'].includes(type)) {
alert('无效的类型,请重新选择');
return;
}
const title = prompt('请输入问题标题:');
if (!title || title.trim() === '') {
alert('标题不能为空');
return;
}
const newQuestion = {
type: type,
title: title.trim(),
options: type === 'text' ? [] : ['选项1', '选项2'] // 默认两个选项
};
questions.push(newQuestion);
renderQuiz();
}2.4 表单验证与提交
submitQuiz函数遍历所有问题,检查必填项(单选/多选至少选一项,文本必须填写),如果不满足则显示错误信息,否则收集数据。
function submitQuiz() {
let isValid = true;
const results = [];
questions.forEach((q, index) => {
const errorDiv = document.getElementById(`error-${index}`);
errorDiv.textContent = ''; // 清除旧错误
if (q.type === 'radio') {
const radios = document.querySelectorAll(`input[name="q${index}"]:checked`);
if (radios.length === 0) {
errorDiv.textContent = '请选择一个选项';
isValid = false;
} else {
results.push({ question: q.title, answer: radios[0].value });
}
} else if (q.type === 'checkbox') {
const checked = document.querySelectorAll(`input[name^="q${index}_"]:checked`);
if (checked.length === 0) {
errorDiv.textContent = '请至少选择一个选项';
isValid = false;
} else {
const answers = Array.from(checked).map(input => input.value);
results.push({ question: q.title, answer: answers });
}
} else if (q.type === 'text') {
const textarea = document.querySelector(`textarea[data-question-index="${index}"]`);
if (textarea.value.trim() === '') {
errorDiv.textContent = '请输入文本';
isValid = false;
} else {
results.push({ question: q.title, answer: textarea.value.trim() });
}
}
});
if (isValid) {
alert('提交成功!结果如下:\n' + JSON.stringify(results, null, 2));
// 实际应用中可以发送到服务器
console.log(results);
} else {
alert('请修正错误后再次提交');
}
}2.5 初始化
页面加载后立即渲染问卷。
document.addEventListener('DOMContentLoaded', function() {
renderQuiz();
});三、完整交互体验
以上代码实现了以下功能:
动态渲染:根据
questions数组自动生成表单元素。新增问题:通过弹窗指定类型和标题。
删除问题:每个问题区块右侧的删除按钮,删除后重新渲染。
添加选项:针对单选/多选问题,支持动态添加选项。
实时验证:未填写的必填项会显示红色错误提示。
数据收集:验证通过后输出所有答案的JSON对象。
四、进一步扩展
你可以根据实际需求增强此问卷系统:
支持问题排序(拖拽调整顺序)。
允许修改问题标题或选项。
支持其他问题类型:下拉选择、评分、日期等。
将数据保存到本地存储或发送到后端API。
添加实时预览功能。
五、总结
本文详细演示了如何使用纯JavaScript动态构建一个交互式问卷表单。通过模块化的数据驱动渲染,可以轻松管理复杂表单结构,提高开发效率和用户体验。你可以将上述代码直接复制到HTML文件中运行,亲手体验动态表单的魅力。