hidden 隐藏字段
<input type="hidden"> 是 HTML 中的隐藏输入字段,用户在页面上看不到该控件,但它的值会随表单一起提交。隐藏字段常用于传递表单外的元数据、CSRF 令牌、数据库 ID、状态标识等需要包含在提交数据中但不需要用户直接交互的信息。
前置知识
阅读本节前,建议先了解:file 文件上传
基础概念
什么是 hidden 输入
type="hidden" 创建一个不可见的输入字段,它不会在页面上渲染任何可视元素,但其 name 和 value 会作为表单数据的一部分提交到服务器。主要用途:
- CSRF 防护令牌:传递跨站请求伪造令牌
- 数据库标识:传递记录 ID(编辑场景)
- 状态保持:传递页面状态、步骤信息
- 追踪参数:来源页面、用户 ID 等
- 关联数据:关联其他表单外部的数据
特点
- 不可见:不在页面上显示任何可视元素
- 不可编辑:用户无法直接修改(但可通过开发者工具修改)
- 值会提交:表单提交时包含 hidden 字段的 name=value
- 无验证:没有内置的输入验证机制
语法
基本用法
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>hidden 隐藏字段示例</title>
</head>
<body>
<h2>编辑用户信息</h2>
<!-- 隐藏字段传递用户 ID -->
<form action="/user/update" method="post">
<input type="hidden" name="user_id" value="1024">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" value="zhangsan">
<label for="email">邮箱:</label>
<input type="email" id="email" name="email" value="zhangsan@example.com">
<button type="submit">保存修改</button>
</form>
</body>
</html>多个隐藏字段
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>多个隐藏字段</title>
</head>
<body>
<h2>订单确认</h2>
<form action="/order/confirm" method="post">
<!-- 订单相关元数据 -->
<input type="hidden" name="order_id" value="ORD-20250714-001">
<input type="hidden" name="user_id" value="1024">
<input type="hidden" name="source" value="mobile_app">
<input type="hidden" name="timestamp" value="1752470400">
<h3>收货地址</h3>
<label for="address">详细地址:</label>
<textarea id="address" name="address" rows="3"></textarea>
<button type="submit">确认下单</button>
</form>
</body>
</html>详细说明
CSRF 令牌传递
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>CSRF 令牌</title>
</head>
<body>
<h2>修改密码</h2>
<form action="/account/change-password" method="post">
<!-- CSRF 令牌由服务端生成,防止跨站请求伪造 -->
<input type="hidden" name="csrf_token" value="a1b2c3d4e5f6g7h8i9j0">
<label for="old-password">旧密码:</label>
<input type="password" id="old-password" name="old_password" required>
<label for="new-password">新密码:</label>
<input type="password" id="new-password" name="new_password" required>
<label for="confirm-password">确认新密码:</label>
<input type="password" id="confirm-password" name="confirm_password" required>
<button type="submit">修改密码</button>
</form>
<script>
// 提交前可以动态设置 CSRF 令牌
// 实际项目中通常在页面加载时由服务端渲染
const form = document.querySelector('form');
const csrfField = form.querySelector('input[name="csrf_token"]');
console.log('CSRF Token:', csrfField.value);
</script>
</body>
</html>JavaScript 动态操作隐藏字段
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>动态隐藏字段</title>
</head>
<body>
<h2>多步骤表单</h2>
<form id="multi-step-form" action="/submit" method="post">
<!-- 动态跟踪当前步骤 -->
<input type="hidden" id="step" name="step" value="1">
<!-- 收集之前步骤的数据 -->
<input type="hidden" id="prev-data" name="prev_data" value="">
<!-- 步骤 1 -->
<div id="step-1">
<h3>第一步:基本信息</h3>
<label for="name">姓名:</label>
<input type="text" id="name" name="name">
<button type="button" onclick="nextStep()">下一步</button>
</div>
<!-- 步骤 2(初始隐藏) -->
<div id="step-2" style="display: none;">
<h3>第二步:联系方式</h3>
<label for="phone">手机:</label>
<input type="tel" id="phone" name="phone">
<button type="button" onclick="prevStep()">上一步</button>
<button type="button" onclick="nextStep()">下一步</button>
</div>
<!-- 步骤 3(初始隐藏) -->
<div id="step-3" style="display: none;">
<h3>第三步:确认</h3>
<p id="summary"></p>
<button type="button" onclick="prevStep()">上一步</button>
<button type="submit">提交</button>
</div>
</form>
<script>
let currentStep = 1;
const formData = {};
function showStep(step) {
document.querySelectorAll('[id^="step-"]').forEach(el => {
el.style.display = el.id === `step-${step}` ? 'block' : 'none';
});
document.getElementById('step').value = step;
currentStep = step;
}
function nextStep() {
if (currentStep === 1) {
formData.name = document.getElementById('name').value;
}
if (currentStep === 2) {
formData.phone = document.getElementById('phone').value;
}
if (currentStep === 2) {
// 显示汇总
document.getElementById('summary').textContent =
`姓名:${formData.name},手机:${formData.phone}`;
document.getElementById('prev-data').value = JSON.stringify(formData);
}
showStep(currentStep + 1);
}
function prevStep() {
showStep(currentStep - 1);
}
</script>
</body>
</html>服务端状态传递
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>服务端状态传递</title>
</head>
<body>
<h2>服务端渲染的隐藏字段</h2>
<p>以下隐藏字段由服务端模板引擎生成:</p>
<form action="/post/edit" method="post">
<!-- 由服务端填充的实际值 -->
<input type="hidden" name="post_id" value="42">
<input type="hidden" name="author_id" value="1024">
<input type="hidden" name="created_at" value="2025-07-10T08:30:00">
<input type="hidden" name="version" value="3">
<label for="title">文章标题:</label>
<input type="text" id="title" name="title" value="HTML 表单教程">
<label for="content">文章内容:</label>
<textarea id="content" name="content" rows="5">表单是 Web 交互的基础...</textarea>
<button type="submit">保存文章</button>
</form>
<!--
服务端模板示例(伪代码):
<input type="hidden" name="post_id" value="{{ post.id }}">
<input type="hidden" name="author_id" value="{{ post.authorId }}">
<input type="hidden" name="version" value="{{ post.version }}">
-->
</body>
</html>disabled 与 hidden 的区别
| 特性 | type="hidden" | disabled |
|---|---|---|
| 页面可见 | 不可见 | 可见但灰色 |
| 值是否提交 | 提交 | 不提交 |
| 用户编辑 | 不可编辑 | 不可编辑 |
| JavaScript 读写 | 可以 | 可以 |
| 典型用途 | 传递元数据 | 禁用但不隐藏 |
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>hidden vs disabled</title>
</head>
<body>
<h2>hidden 与 disabled 对比</h2>
<form action="/submit" method="post">
<!-- 值会提交 -->
<input type="hidden" name="id" value="42">
<!-- 值不会提交 -->
<input type="text" name="readonly_field" value="自动生成" disabled>
<!-- 可见且只读,值会提交 -->
<input type="text" name="visible_readonly" value="只读内容" readonly>
<label for="editable">可编辑:</label>
<input type="text" id="editable" name="editable" value="用户输入">
<button type="submit">提交</button>
</form>
<p>提交数据将包含:id=42, visible_readonly=只读内容, editable=用户输入</p>
<p>disabled 字段的数据不会被提交</p>
</body>
</html>注意事项
安全警示:不要信任隐藏字段的值
隐藏字段对用户是不可见的,但不代表是安全的。用户可以通过浏览器开发者工具轻松查看和修改隐藏字段的值:
javascript
// 用户可以在控制台中执行以下代码修改隐藏字段
document.querySelector('input[name="user_id"]').value = '9999';
document.querySelector('input[name="csrf_token"]').value = 'fake_token';因此:
- 敏感数据不要放在 hidden 字段中(如密码、密钥、价格等)
- 服务端必须验证所有提交的数据,包括隐藏字段的值
- CSRF 令牌由服务端生成和验证,客户端不应修改
- 价格等关键数据应在服务端查询,而非依赖客户端提交的值
不要用 hidden 存储大量数据
虽然技术上可以在 hidden 字段中存储较长的字符串,但这不是好的做法:
html
<!-- 不推荐:存储大量数据 -->
<input type="hidden" name="big_data" value='{"items":[...大量JSON数据...]}'>更好的做法:
- 使用服务端 session 存储大数据
- 使用 localStorage/sessionStorage 存储客户端数据
- 使用数据库查询获取完整数据
最佳实践
- CSRF 防护:在所有修改数据的表单中包含 CSRF 令牌
- 编辑场景传 ID:编辑记录时,通过隐藏字段传递记录的唯一标识
- 服务端验证:永远不要信任隐藏字段传来的值
- 使用语义化命名:为隐藏字段取有意义的 name,便于服务端处理
- 避免存储敏感数据:密码、密钥等绝不放入隐藏字段
- 动态设置:需要动态值时用 JavaScript 设置,而非硬编码
下一节
继续学习:image 图像按钮