在项目开发中,问卷调查是一种常见的用户数据收集方式。本文将介绍如何使用 HTML、CSS 和 JavaScript 构建一个简易的问卷调查页面。
效果演示
项目概述
本项目是一个前端静态页面,包含以下主要功能:
- 动态生成问卷题目
- 支持多种题型:单选、多选、文本输入、评分
- 表单验证机制
- 提交后显示结果
- 简洁美观的 UI 设计
页面结构与样式
创建 HTML 结构
<div class="survey-container">
<h1>产品使用情况调查</h1>
<div id="surveyQuestions"></div>
<button onclick="submitSurvey()">提交问卷</button>
<div id="results" class="results"></div>
</div>
设计 CSS 样式
.survey-container {
max-width: 800px;
margin: 20px auto;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
}
.question {
margin-bottom: 25px;
padding: 15px;
background: white;
border-radius: 5px;
border-left: 4px solid #007bff;
}
.question-title {
font-size: 1.1em;
margin-bottom: 15px;
color: #333;
}
.options-group {
margin: 10px 0;
}
.option-item {
margin: 8px 0;
display: flex;
align-items: center;
}
input[type="radio"],
input[type="checkbox"] {
margin-right: 8px;
}
label {
cursor: pointer;
user-select: none;
}
textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
}
button {
display: block;
margin: 0 auto;
background: #28a745;
color: white;
padding: 12px 25px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
}
.error-msg {
color: #dc3545;
font-size: 0.9em;
margin-top: 5px;
display: none;
}
.results {
display: none;
margin-top: 30px;
padding: 20px;
background: #e9ecef;
border-radius: 5px;
}
定义基础数据
所有问题都定义在一个 surveyConfig 数组中,每道题可以配置类型、问题描述、是否必填等属性。
const surveyConfig = [
{
type: "radio",
question: "1. 您的年龄范围",
name: "age",
required: true,
options: [
{ value: "18-25", label: "18-25岁" },
{ value: "26-35", label: "26-35岁" },
{ value: "36-45", label: "36-45岁" },
{ value: "46+", label: "46岁及以上" }
]
},
// ...
];
交互功能
动态生成问卷
通过遍历 surveyConfig,动态创建 DOM 元素并插入到页面中,支持不同类型的题目。
function initSurvey() {
const container = document.getElementById('surveyQuestions');
surveyConfig.forEach((question, index) => {
const questionDiv = document.createElement('div');
questionDiv.className = 'question';
questionDiv.innerHTML = `<div class="question-title">${question.question}</div><div class="error-msg">请回答此题</div>`;
// 添加输入控件
const inputGroup = document.createElement('div');
inputGroup.className = 'options-group';
switch(question.type) {
case 'radio':
case 'checkbox':
question.options.forEach(option => {
const optionId = `${question.name}_${option.value}`; // 生成唯一ID
inputGroup.innerHTML += `<div class="option-item">
<input type="${question.type}" id="${optionId}" name="${question.name}" value="${option.value}">
<label for="${optionId}">${option.label}</label>
</div>`;
});
break;
// ...
}
questionDiv.appendChild(inputGroup);
container.appendChild(questionDiv);
});
}
表单验证
验证失败时会高亮问题区域,并显示错误信息。
function validateForm() {
let isValid = true;
surveyConfig.forEach((question, index) => {
const questionDiv = document.getElementsByClassName('question')[index];
const errorMsg = questionDiv.querySelector('.error-msg');
// 重置错误状态
errorMsg.style.display = 'none';
questionDiv.style.borderColor = '#007bff';
if(question.required) {
let isAnswered = false;
switch(question.type) {
case 'radio':
case 'rating':
isAnswered = !!questionDiv.querySelector(`input[name="${question.name}"]:checked`);
break;
// ...
}
if(!isAnswered) {
errorMsg.style.display = 'block';
questionDiv.style.borderColor = '#dc3545';
isValid = false;
}
}
});
return isValid;
}
数据收集
从页面中提取每个问题的答案,返回一个 JSON 对象,便于后续处理或提交。
function collectData() {
const result = {};
surveyConfig.forEach(question => {
switch(question.type) {
case 'radio':
case 'rating':
const radio = document.querySelector(`input[name="${question.name}"]:checked`);
result[question.name] = radio ? radio.value : null;
break;
// ...
}
});
return result;
}
提交问卷
表单验证 validateForm() 通过后调用 collectData() 获取数据,展示结果,可替换为请求发送至服务器。
function submitSurvey() {
if(!validateForm()) return;
const formData = collectData();
// 显示结果
const resultsDiv = document.getElementById('results');
resultsDiv.style.display = 'block';
resultsDiv.innerHTML = `<h3>提交成功,感谢您的参与!</h3>
<p>您的回答:</p>
<pre>${JSON.stringify(formData, null, 2)}</pre>`;
// 清空表单
document.querySelectorAll('input, textarea').forEach(el => {
if(el.type === 'radio' || el.type === 'checkbox') {
el.checked = false;
} else {
el.value = '';
}
});
}
扩展建议
虽然这是一个简单的问卷系统,但仍具备良好的扩展性:
- 后端集成:通过接口获取表单配置信息 surveyConfig,将 submitSurvey() 中的数据通过请求发送至服务端
- 更多题型:支持下拉选择、文件上传等
完整代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>简易问卷调查</title>
<style>
.survey-container {
max-width: 800px;
margin: 20px auto;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
}
.question {
margin-bottom: 25px;
padding: 15px;
background: white;
border-radius: 5px;
border-left: 4px solid #007bff;
}
.question-title {
font-size: 1.1em;
margin-bottom: 15px;
color: #333;
}
.options-group {
margin: 10px 0;
}
.option-item {
margin: 8px 0;
display: flex;
align-items: center;
}
input[type="radio"],
input[type="checkbox"] {
margin-right: 8px;
}
label {
cursor: pointer;
user-select: none;
}
textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
}
button {
display: block;
margin: 0 auto;
background: #28a745;
color: white;
padding: 12px 25px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
}
.error-msg {
color: #dc3545;
font-size: 0.9em;
margin-top: 5px;
display: none;
}
.results {
display: none;
margin-top: 30px;
padding: 20px;
background: #e9ecef;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="survey-container">
<h1>产品使用情况调查</h1>
<div id="surveyQuestions"></div>
<button onclick="submitSurvey()">提交问卷</button>
<div id="results" class="results"></div>
</div>
<script>
// 问卷配置
const surveyConfig = [
{
type: "radio",
question: "1. 您的年龄范围",
name: "age",
required: true,
options: [
{ value: "18-25", label: "18-25岁" },
{ value: "26-35", label: "26-35岁" },
{ value: "36-45", label: "36-45岁" },
{ value: "46+", label: "46岁及以上" }
]
},
{
type: "checkbox",
question: "2. 您使用我们产品的场景(多选)",
name: "scenarios",
required: true,
options: [
{ value: "work", label: "工作" },
{ value: "study", label: "学习" },
{ value: "entertainment", label: "娱乐" },
{ value: "other", label: "其他" }
]
},
{
type: "rating",
question: "3. 请为产品满意度打分(1-5分)",
name: "satisfaction",
required: true
},
{
type: "textarea",
question: "4. 请提供您的宝贵意见",
name: "suggestion",
required: true,
placeholder: "请输入意见"
},
];
// 初始化问卷
function initSurvey() {
const container = document.getElementById('surveyQuestions');
surveyConfig.forEach((question, index) => {
const questionDiv = document.createElement('div');
questionDiv.className = 'question';
questionDiv.innerHTML = `<div class="question-title">${question.question}</div><div class="error-msg">请回答此题</div>`;
// 添加输入控件
const inputGroup = document.createElement('div');
inputGroup.className = 'options-group';
switch(question.type) {
case 'radio':
case 'checkbox':
question.options.forEach(option => {
const optionId = `${question.name}_${option.value}`; // 生成唯一ID
inputGroup.innerHTML += `<div class="option-item">
<input type="${question.type}" id="${optionId}" name="${question.name}" value="${option.value}">
<label for="${optionId}">${option.label}</label>
</div>`;
});
break;
case 'textarea':
inputGroup.innerHTML = `
<textarea name="${question.name}" placeholder="${question.placeholder || ''}" rows="3"></textarea>
`;
break;
case 'rating':
for(let i=1; i<=5; i++) {
const optionId = `${question.name}_${i}`; // 生成唯一ID
inputGroup.innerHTML += `<div class="option-item">
<input type="radio" id="${optionId}" name="${question.name}" value="${i}">
<label for="${optionId}">${i} 分</label>
</div>`;
}
break;
}
questionDiv.appendChild(inputGroup);
container.appendChild(questionDiv);
});
}
// 验证问卷
function validateForm() {
let isValid = true;
surveyConfig.forEach((question, index) => {
const questionDiv = document.getElementsByClassName('question')[index];
const errorMsg = questionDiv.querySelector('.error-msg');
// 重置错误状态
errorMsg.style.display = 'none';
questionDiv.style.borderColor = '#007bff';
if(question.required) {
let isAnswered = false;
switch(question.type) {
case 'radio':
case 'rating':
isAnswered = !!questionDiv.querySelector(`input[name="${question.name}"]:checked`);
break;
case 'checkbox':
isAnswered = questionDiv.querySelectorAll(`input[name="${question.name}"]:checked`).length > 0;
break;
case 'textarea':
const textValue = questionDiv.querySelector('textarea').value.trim();
isAnswered = textValue.length > 0;
break;
}
if(!isAnswered) {
errorMsg.style.display = 'block';
questionDiv.style.borderColor = '#dc3545';
isValid = false;
}
}
});
return isValid;
}
// 收集数据
function collectData() {
const result = {};
surveyConfig.forEach(question => {
switch(question.type) {
case 'radio':
case 'rating':
const radio = document.querySelector(`input[name="${question.name}"]:checked`);
result[question.name] = radio ? radio.value : null;
break;
case 'checkbox':
const checkboxes = [...document.querySelectorAll(`input[name="${question.name}"]:checked`)];
result[question.name] = checkboxes.map(cb => cb.value);
break;
case 'text':
result[question.name] = document.querySelector(`textarea[name="${question.name}"]`).value.trim();
break;
}
});
return result;
}
// 提交问卷
function submitSurvey() {
if(!validateForm()) return;
const formData = collectData();
// 显示结果(实际使用中可替换为AJAX请求)
const resultsDiv = document.getElementById('results');
resultsDiv.style.display = 'block';
resultsDiv.innerHTML = `<h3>提交成功,感谢您的参与!</h3>
<p>您的回答:</p>
<pre>${JSON.stringify(formData, null, 2)}</pre>`;
// 清空表单
document.querySelectorAll('input, textarea').forEach(el => {
if(el.type === 'radio' || el.type === 'checkbox') {
el.checked = false;
} else {
el.value = '';
}
});
}
// 初始化
initSurvey();
</script>
</body>
</html>