表單中常見需求:「最多選 N 個選項」並後端正確接收數組。本文提供前端 JS 限制邏輯、後端 PHP 數組處理、無障礙增強、安全過濾的完整方案。
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>選課演示</title>
<style>
.checkbox-group { display: flex; flex-direction: column; gap: 8px; max-width: 300px; }
.error-msg { color: #c00; margin-top: 8px; display: none; }
</style>
</head><body>
<form id="courseForm" method="post" action="submit.php">
<h3>請選擇本學期課程(最多 3 門)</h3>
<div class="checkbox-group">
<label><input type="checkbox" name="course[]" value="語文"> 語文</label>
<label><input type="checkbox" name="course[]" value="數學"> 數學</label>
<label><input type="checkbox" name="course[]" value="英語"> 英語</label>
<label><input type="checkbox" name="course[]" value="化學"> 化學</label>
<label><input type="checkbox" name="course[]" value="物理"> 物理</label>
<label><input type="checkbox" name="course[]" value="政治"> 政治</label>
</div>
<div class="error-msg" id="errMsg">最多只能選 3 門課程</div>
<button type="submit">提交選課</button>
<button type="reset">重置</button>
</form>
<script>
const MAX = 3;
const checkboxes = document.querySelectorAll('input[name="course[]"]');
const errMsg = document.getElementById('errMsg');
const submitBtn = document.querySelector('button[type="submit"]');
function updateState() {
const checked = document.querySelectorAll('input[name="course[]"]:checked');
const over = checked.length > MAX;
errMsg.style.display = over ? 'block' : 'none';
submitBtn.disabled = over;
// 超限時取消最後勾選的
if (over) {
checked[checked.length - 1].checked = false;
updateState(); // 遞歸修正
}
}
checkboxes.forEach(cb => cb.addEventListener('change', updateState));
// 表單提交前最後攔截
document.getElementById('courseForm').addEventListener('submit', e => {
if (document.querySelectorAll('input[name="course[]"]:checked').length > MAX) {
e.preventDefault();
alert('請修正選課數量');
}
});
</script>
</body></html>
name="course[]":中括號讓 PHP 自動解析為數組 $_POST['course']。<label> 包裹,支援鍵盤操作、屏幕閱讀器。submit.php<?php
// submit.php
header('Content-Type: text/html; charset=utf-8');
$courses = $_POST['course'] ?? [];
$max = 3;
// 1. 類型修正:可能被惡意提交為字符串
if (!is_array($courses)) {
$courses = [$courses];
}
// 2. 過濾空值、去重、長度限制
$courses = array_unique(array_filter($courses, fn($v) => is_string($v) && $v !== ''));
if (count($courses) > $max) {
http_response_code(400);
exit('錯誤:選課數量超過限制(最多 ' . $max . ' 門)');
}
// 3. 白名單驗證(防止注入非法值)
$allowed = ['語文','數學','英語','化學','物理','政治'];
$courses = array_intersect($courses, $allowed);
// 4. 業務邏輯:入庫、發送確認郵件等
// $stmt = $pdo->prepare("INSERT INTO selections (student_id, course) VALUES (?, ?)");
// foreach ($courses as $c) { $stmt->execute([$studentId, $c]); }
echo '成功選課:' . implode('、', $courses);
<!-- HTML -->
<fieldset data-max="2" data-group="arts">
<legend>藝術類(最多 2 門)</legend>
<label><input type="checkbox" name="course[]" value="音樂" data-group="arts"> 音樂</label>
<label><input type="checkbox" name="course[]" value="美術" data-group="arts"> 美術</label>
<label><input type="checkbox" name="course[]" value="舞蹈" data-group="arts"> 舞蹈</label>
</fieldset>
<script>
document.querySelectorAll('[data-max]').forEach(group => {
const max = parseInt(group.dataset.max);
const g = group.dataset.group;
const cbs = group.querySelectorAll('input[type="checkbox"]');
cbs.forEach(cb => cb.addEventListener('change', () => {
const checked = group.querySelectorAll('input[type="checkbox"]:checked');
if (checked.length > max) {
alert(`該分組最多選 ${max} 項`);
cb.checked = false;
}
}));
});
</script>
count($arr) > 1000 直接拒絕)。<input type="hidden" name="_token" value="= $_SESSION['csrf'] ?>">。htmlspecialchars()。前端限制提升體驗,後端驗證保障安全,白名單過濾保證數據質量——三位一體構建健壯的多選表單處理流程。