Skip to content

JavaScript 表单

什么是表单

表单(Form)是 HTML 中用于收集用户输入的元素,JavaScript 可以用来操作和验证表单,提高用户体验和数据安全性。

访问表单元素

1. 通过 document.forms 访问

javascript
// 访问所有表单
var forms = document.forms;
console.log("Number of forms:", forms.length);

// 按索引访问表单
var firstForm = document.forms[0];
console.log("First form:", firstForm);

// 按名称访问表单
var namedForm = document.forms["myForm"];
console.log("Named form:", namedForm);

2. 通过 document.getElementById 访问

javascript
// 通过 ID 访问表单
var form = document.getElementById("myForm");
console.log("Form:", form);

3. 访问表单元素

javascript
// 访问表单中的元素
var form = document.getElementById("myForm");

// 按索引访问
var firstElement = form.elements[0];
console.log("First element:", firstElement);

// 按名称或 ID 访问
var usernameInput = form.elements["username"];
console.log("Username input:", usernameInput);

// 直接通过表单对象访问
var passwordInput = form.password;
console.log("Password input:", passwordInput);

表单元素类型

1. 文本输入框(text)

html
<input type="text" id="username" name="username" placeholder="请输入用户名" />
javascript
// 操作文本输入框
var usernameInput = document.getElementById("username");

// 获取值
console.log("Username:", usernameInput.value);

// 设置值
usernameInput.value = "John Doe";

// 获取和设置属性
console.log("Placeholder:", usernameInput.placeholder);
usernameInput.placeholder = "请输入您的用户名";

// 获取和设置状态
console.log("Disabled:", usernameInput.disabled);
usernameInput.disabled = true; // 禁用输入框

console.log("Readonly:", usernameInput.readOnly);
usernameInput.readOnly = true; // 只读

2. 密码输入框(password)

html
<input type="password" id="password" name="password" placeholder="请输入密码" />
javascript
// 操作密码输入框
var passwordInput = document.getElementById("password");

// 获取值
console.log("Password:", passwordInput.value);

// 设置值
passwordInput.value = "secret123";

3. 单选按钮(radio)

html
<input type="radio" id="male" name="gender" value="male" />
<label for="male">男</label>
<input type="radio" id="female" name="gender" value="female" />
<label for="female">女</label>
javascript
// 操作单选按钮
var maleRadio = document.getElementById("male");
var femaleRadio = document.getElementById("female");

// 检查是否选中
console.log("Male checked:", maleRadio.checked);
console.log("Female checked:", femaleRadio.checked);

// 设置选中状态
maleRadio.checked = true;

// 获取选中的值
var genderGroup = document.getElementsByName("gender");
for (var i = 0; i < genderGroup.length; i++) {
  if (genderGroup[i].checked) {
    console.log("Selected gender:", genderGroup[i].value);
    break;
  }
}

4. 复选框(checkbox)

html
<input type="checkbox" id="agree" name="agree" value="yes" />
<label for="agree">我同意条款</label>

<input type="checkbox" id="hobby1" name="hobby" value="reading" />
<label for="hobby1">阅读</label>
<input type="checkbox" id="hobby2" name="hobby" value="sports" />
<label for="hobby2">运动</label>
<input type="checkbox" id="hobby3" name="hobby" value="music" />
<label for="hobby3">音乐</label>
javascript
// 操作复选框
var agreeCheckbox = document.getElementById("agree");

// 检查是否选中
console.log("Agree checked:", agreeCheckbox.checked);

// 设置选中状态
agreeCheckbox.checked = true;

// 获取多个选中的值
var hobbyCheckboxes = document.getElementsByName("hobby");
var selectedHobbies = [];
for (var i = 0; i < hobbyCheckboxes.length; i++) {
  if (hobbyCheckboxes[i].checked) {
    selectedHobbies.push(hobbyCheckboxes[i].value);
  }
}
console.log("Selected hobbies:", selectedHobbies);

5. 下拉列表(select)

html
<select id="country" name="country">
  <option value="">请选择国家</option>
  <option value="china">中国</option>
  <option value="usa">美国</option>
  <option value="japan">日本</option>
</select>
javascript
// 操作下拉列表
var countrySelect = document.getElementById("country");

// 获取选中的值
console.log("Selected country:", countrySelect.value);

// 设置选中的值
countrySelect.value = "china";

// 获取选中的选项
var selectedOption = countrySelect.options[countrySelect.selectedIndex];
console.log("Selected option text:", selectedOption.text);
console.log("Selected option value:", selectedOption.value);

// 添加新选项
var newOption = document.createElement("option");
newOption.value = "uk";
newOption.text = "英国";
countrySelect.appendChild(newOption);

// 删除选项
countrySelect.remove(0); // 删除第一个选项

6. 文本域(textarea)

html
<textarea
  id="message"
  name="message"
  rows="4"
  cols="50"
  placeholder="请输入消息"
></textarea>
javascript
// 操作文本域
var messageTextarea = document.getElementById("message");

// 获取值
console.log("Message:", messageTextarea.value);

// 设置值
messageTextarea.value = "Hello, this is a message!";

// 获取和设置行数和列数
console.log("Rows:", messageTextarea.rows);
console.log("Cols:", messageTextarea.cols);
messageTextarea.rows = 6;
messageTextarea.cols = 60;

7. 按钮(button)

html
<button type="button" id="btn1">普通按钮</button>
<button type="submit" id="btn2">提交按钮</button>
<button type="reset" id="btn3">重置按钮</button>
javascript
// 操作按钮
var btn1 = document.getElementById("btn1");

// 更改按钮文本
btn1.textContent = "点击我";

// 禁用按钮
btn1.disabled = true;

// 启用按钮
setTimeout(function () {
  btn1.disabled = false;
}, 2000);

8. 文件上传(file)

html
<input type="file" id="file" name="file" />
<input type="file" id="files" name="files" multiple />
javascript
// 操作文件上传
var fileInput = document.getElementById("file");
var filesInput = document.getElementById("files");

// 监听文件选择
fileInput.addEventListener("change", function (e) {
  var file = e.target.files[0];
  console.log("Selected file:", file.name);
  console.log("File size:", file.size, "bytes");
  console.log("File type:", file.type);
});

// 多文件选择
filesInput.addEventListener("change", function (e) {
  var files = e.target.files;
  console.log("Number of files selected:", files.length);
  for (var i = 0; i < files.length; i++) {
    console.log("File " + (i + 1) + ": " + files[i].name);
  }
});

表单事件

1. submit 事件

当表单提交时触发,可以用来验证表单数据。

javascript
// 监听表单提交事件
var form = document.getElementById("myForm");

form.addEventListener("submit", function (e) {
  // 阻止表单默认提交行为
  e.preventDefault();

  // 验证表单
  var username = form.elements["username"].value;
  var password = form.elements["password"].value;

  if (!username) {
    alert("请输入用户名");
    return;
  }

  if (!password) {
    alert("请输入密码");
    return;
  }

  // 表单验证通过,手动提交或进行其他操作
  console.log("Form submitted successfully!");
  // form.submit(); // 手动提交表单
});

2. reset 事件

当表单重置时触发。

javascript
// 监听表单重置事件
var form = document.getElementById("myForm");

form.addEventListener("reset", function (e) {
  console.log("Form reset!");
  // 可以在这里添加重置后的逻辑
});

3. change 事件

当表单元素的值发生变化时触发。

javascript
// 监听输入框变化事件
var usernameInput = document.getElementById("username");

usernameInput.addEventListener("change", function (e) {
  console.log("Username changed to:", e.target.value);
});

// 监听下拉列表变化事件
var countrySelect = document.getElementById("country");

countrySelect.addEventListener("change", function (e) {
  console.log("Country changed to:", e.target.value);
});

4. input 事件

当表单元素的值发生实时变化时触发,比 change 事件更频繁。

javascript
// 监听输入框实时输入事件
var usernameInput = document.getElementById("username");

usernameInput.addEventListener("input", function (e) {
  console.log("Username input:", e.target.value);
  // 可以在这里添加实时验证逻辑
});

5. focus 事件

当表单元素获得焦点时触发。

javascript
// 监听输入框获得焦点事件
var usernameInput = document.getElementById("username");

usernameInput.addEventListener("focus", function (e) {
  console.log("Username input focused");
  // 可以在这里添加获得焦点时的逻辑,如高亮输入框
  e.target.style.borderColor = "blue";
});

6. blur 事件

当表单元素失去焦点时触发。

javascript
// 监听输入框失去焦点事件
var usernameInput = document.getElementById("username");

usernameInput.addEventListener("blur", function (e) {
  console.log("Username input blurred");
  // 可以在这里添加失去焦点时的逻辑,如验证输入
  e.target.style.borderColor = "";
});

表单操作

1. 提交表单

javascript
// 提交表单
var form = document.getElementById("myForm");

// 方法 1:通过 submit 事件
form.addEventListener("submit", function (e) {
  e.preventDefault();
  // 验证逻辑
  form.submit(); // 手动提交
});

// 方法 2:直接调用 submit() 方法
function submitForm() {
  var form = document.getElementById("myForm");
  form.submit();
}

2. 重置表单

javascript
// 重置表单
var form = document.getElementById("myForm");

// 方法 1:通过 reset 按钮
// <button type="reset">重置</button>

// 方法 2:直接调用 reset() 方法
function resetForm() {
  var form = document.getElementById("myForm");
  form.reset();
}

3. 序列化表单数据

将表单数据序列化为 URL 编码字符串,常用于 AJAX 提交。

javascript
// 序列化表单数据
function serializeForm(form) {
  var formData = new FormData(form);
  var serialized = [];

  for (var pair of formData.entries()) {
    serialized.push(
      encodeURIComponent(pair[0]) + "=" + encodeURIComponent(pair[1])
    );
  }

  return serialized.join("&");
}

// 使用示例
var form = document.getElementById("myForm");
var serializedData = serializeForm(form);
console.log("Serialized form data:", serializedData);

// 或者使用 URLSearchParams
function serializeFormWithURLSearchParams(form) {
  var formData = new FormData(form);
  return new URLSearchParams(formData).toString();
}

var serializedData2 = serializeFormWithURLSearchParams(form);
console.log("Serialized form data (URLSearchParams):", serializedData2);

4. 使用 FormData 对象

FormData 对象用于构建表单数据,可用于 AJAX 提交。

javascript
// 使用 FormData 对象
var form = document.getElementById("myForm");

// 方法 1:从表单创建
var formData = new FormData(form);

// 方法 2:手动构建
var formData2 = new FormData();
formData2.append("username", "John");
formData2.append("password", "secret123");
formData2.append("hobby", "reading");
formData2.append("hobby", "sports");

// 遍历 FormData
for (var pair of formData.entries()) {
  console.log(pair[0] + ": " + pair[1]);
}

// AJAX 提交示例
var xhr = new XMLHttpRequest();
xhr.open("POST", "submit.php", true);
xhr.onload = function () {
  if (xhr.status === 200) {
    console.log("Form submitted successfully!");
  } else {
    console.error("Error submitting form:", xhr.status);
  }
};
xhr.send(formData);

// 使用 fetch API 提交
fetch("submit.php", {
  method: "POST",
  body: formData,
})
  .then((response) => response.text())
  .then((data) => {
    console.log("Form submitted successfully:", data);
  })
  .catch((error) => {
    console.error("Error submitting form:", error);
  });

表单验证

1. 客户端验证

客户端验证在浏览器中进行,提高用户体验,减少服务器负载。

javascript
// 简单的表单验证
function validateForm() {
  var form = document.getElementById("myForm");
  var username = form.elements["username"].value;
  var password = form.elements["password"].value;
  var email = form.elements["email"].value;
  var agree = form.elements["agree"].checked;

  var isValid = true;

  // 验证用户名
  if (!username) {
    alert("请输入用户名");
    isValid = false;
  }

  // 验证密码
  else if (password.length < 6) {
    alert("密码长度至少为 6 位");
    isValid = false;
  }

  // 验证邮箱
  else if (!validateEmail(email)) {
    alert("请输入有效的邮箱地址");
    isValid = false;
  }

  // 验证同意条款
  else if (!agree) {
    alert("请同意条款");
    isValid = false;
  }

  return isValid;
}

// 邮箱验证函数
function validateEmail(email) {
  var re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return re.test(email);
}

// 监听表单提交事件
var form = document.getElementById("myForm");
form.addEventListener("submit", function (e) {
  if (!validateForm()) {
    e.preventDefault();
  }
});

2. HTML5 表单验证

HTML5 提供了内置的表单验证功能,如 required、pattern、min、max 等属性。

html
<form id="html5Form" novalidate>
  <input
    type="text"
    id="username"
    name="username"
    required
    placeholder="请输入用户名"
  />
  <input
    type="password"
    id="password"
    name="password"
    required
    minlength="6"
    placeholder="请输入密码"
  />
  <input
    type="email"
    id="email"
    name="email"
    required
    placeholder="请输入邮箱"
  />
  <input
    type="number"
    id="age"
    name="age"
    min="18"
    max="100"
    placeholder="请输入年龄"
  />
  <input type="submit" value="提交" />
</form>
javascript
// 监听 HTML5 表单提交事件
var html5Form = document.getElementById("html5Form");

html5Form.addEventListener("submit", function (e) {
  if (!html5Form.checkValidity()) {
    e.preventDefault();
    console.log("Form validation failed");

    // 显示自定义错误信息
    var elements = html5Form.elements;
    for (var i = 0; i < elements.length; i++) {
      if (!elements[i].checkValidity()) {
        console.log(
          "Error in",
          elements[i].name,
          ":",
          elements[i].validationMessage
        );
      }
    }
  } else {
    console.log("Form validation passed");
  }
});

// 自定义验证
var passwordInput = document.getElementById("password");

passwordInput.addEventListener("input", function (e) {
  if (passwordInput.value.length < 6) {
    passwordInput.setCustomValidity("密码长度至少为 6 位");
  } else {
    passwordInput.setCustomValidity("");
  }
});

3. 实时验证

实时验证可以在用户输入时立即提供反馈,提高用户体验。

javascript
// 实时验证用户名
var usernameInput = document.getElementById("username");
var usernameError = document.getElementById("usernameError");

usernameInput.addEventListener("input", function (e) {
  var username = e.target.value;

  if (!username) {
    usernameError.textContent = "请输入用户名";
    usernameError.style.color = "red";
  } else if (username.length < 3) {
    usernameError.textContent = "用户名长度至少为 3 位";
    usernameError.style.color = "red";
  } else {
    usernameError.textContent = "用户名可用";
    usernameError.style.color = "green";
  }
});

// 实时验证邮箱
var emailInput = document.getElementById("email");
var emailError = document.getElementById("emailError");

emailInput.addEventListener("input", function (e) {
  var email = e.target.value;

  if (!email) {
    emailError.textContent = "请输入邮箱";
    emailError.style.color = "red";
  } else if (!validateEmail(email)) {
    emailError.textContent = "请输入有效的邮箱地址";
    emailError.style.color = "red";
  } else {
    emailError.textContent = "邮箱格式正确";
    emailError.style.color = "green";
  }
});

表单样式操作

1. 操作表单元素样式

javascript
// 操作输入框样式
var usernameInput = document.getElementById("username");

// 获得焦点时的样式
usernameInput.addEventListener("focus", function (e) {
  e.target.style.borderColor = "blue";
  e.target.style.boxShadow = "0 0 5px rgba(0, 0, 255, 0.5)";
});

// 失去焦点时的样式
usernameInput.addEventListener("blur", function (e) {
  e.target.style.borderColor = "";
  e.target.style.boxShadow = "";
});

// 输入错误时的样式
function markError(element) {
  element.style.borderColor = "red";
  element.style.boxShadow = "0 0 5px rgba(255, 0, 0, 0.5)";
}

function clearError(element) {
  element.style.borderColor = "";
  element.style.boxShadow = "";
}

2. 添加和移除 CSS 类

javascript
// 添加和移除 CSS 类
var usernameInput = document.getElementById("username");

// 获得焦点时添加类
usernameInput.addEventListener("focus", function (e) {
  e.target.classList.add("focused");
});

// 失去焦点时移除类
usernameInput.addEventListener("blur", function (e) {
  e.target.classList.remove("focused");
});

// 输入错误时添加错误类
function markError(element) {
  element.classList.add("error");
}

function clearError(element) {
  element.classList.remove("error");
}
css
/* 对应的 CSS 样式 */
.focused {
  border-color: blue;
  box-shadow: 0 0 5px rgba(0, 0, 255, 0.5);
}

.error {
  border-color: red;
  box-shadow: 0 0 5px rgba(255, 0, 0, 0.5);
}

表单的最佳实践

1. 表单设计

  • 简洁明了:只收集必要的信息,避免表单过长
  • 分组:将相关的表单元素分组,使用字段集(fieldset)和图例(legend)
  • 标签:为每个表单元素添加清晰的标签(label),使用 for 属性关联
  • 占位符:使用 placeholder 属性提供输入提示
  • 帮助文本:为复杂的输入项提供帮助文本
  • 错误提示:及时显示清晰的错误提示

2. 表单验证

  • 客户端验证:在浏览器中进行基本验证,提高用户体验
  • 服务器端验证:必须在服务器端进行验证,确保数据安全
  • 实时验证:对关键字段进行实时验证,及时提供反馈
  • 错误提示:错误提示应清晰、具体,并显示在相关字段附近
  • 可访问性:确保验证错误对屏幕阅读器友好

3. 表单提交

  • 防止重复提交:在提交后禁用提交按钮,防止用户重复点击
  • 异步提交:使用 AJAX 或 Fetch API 进行异步提交,提高用户体验
  • 进度指示:在提交过程中显示加载指示器
  • 成功反馈:提交成功后显示清晰的成功信息
  • 错误处理:优雅处理提交失败的情况,显示错误信息

4. 可访问性

  • 标签关联:使用 label 元素的 for 属性与输入元素关联
  • 字段集和图例:使用 fieldset 和 legend 对表单元素进行分组
  • ARIA 属性:使用 ARIA 属性增强表单的可访问性
  • 键盘导航:确保表单可以通过键盘导航
  • 错误提示:使用 ARIA 错误状态和消息

5. 性能优化

  • 减少 DOM 操作:批量操作 DOM,减少重排和重绘
  • 事件委托:使用事件委托处理多个表单元素的事件
  • 节流和防抖:对实时验证等频繁触发的事件使用节流或防抖
  • 表单缓存:对于大型表单,可以考虑缓存表单数据,防止意外刷新导致数据丢失

常见问题及解决方案

1. 表单提交后页面刷新

问题:表单提交后页面会刷新,导致用户体验不佳。

解决方案:使用 AJAX 或 Fetch API 进行异步提交,或阻止表单的默认提交行为。

javascript
// 阻止表单默认提交行为
var form = document.getElementById("myForm");
form.addEventListener("submit", function (e) {
  e.preventDefault();
  // 进行表单验证和提交
});

2. 表单数据丢失

问题:用户输入数据后,页面意外刷新,导致数据丢失。

解决方案:使用 localStorage 或 sessionStorage 缓存表单数据。

javascript
// 缓存表单数据
var form = document.getElementById("myForm");
var elements = form.elements;

// 监听输入事件,缓存数据
for (var i = 0; i < elements.length; i++) {
  if (elements[i].name) {
    elements[i].addEventListener("input", function (e) {
      var formData = JSON.parse(localStorage.getItem("formData") || "{}");
      formData[e.target.name] = e.target.value;
      localStorage.setItem("formData", JSON.stringify(formData));
    });
  }
}

// 页面加载时恢复数据
window.addEventListener("load", function () {
  var formData = JSON.parse(localStorage.getItem("formData") || "{}");
  for (var name in formData) {
    var element = form.elements[name];
    if (element) {
      element.value = formData[name];
    }
  }
});

// 表单提交后清除缓存
form.addEventListener("submit", function (e) {
  e.preventDefault();
  // 验证和提交逻辑
  localStorage.removeItem("formData"); // 清除缓存
});

3. 跨域表单提交

问题:表单需要提交到不同域名的服务器,遇到跨域限制。

解决方案:使用 CORS(跨域资源共享)或 JSONP。

javascript
// 使用 Fetch API 提交跨域表单
fetch("https://api.example.com/submit", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    username: document.getElementById("username").value,
    email: document.getElementById("email").value,
  }),
  credentials: "include", // 包含凭据
})
  .then((response) => response.json())
  .then((data) => {
    console.log("Form submitted successfully:", data);
  })
  .catch((error) => {
    console.error("Error submitting form:", error);
  });

4. 文件上传进度

问题:文件上传时无法显示进度。

解决方案:使用 XMLHttpRequest 的 progress 事件。

javascript
// 文件上传进度
var fileInput = document.getElementById("file");
var progressBar = document.getElementById("progressBar");

fileInput.addEventListener("change", function (e) {
  var file = e.target.files[0];
  var formData = new FormData();
  formData.append("file", file);

  var xhr = new XMLHttpRequest();

  // 监听进度事件
  xhr.upload.addEventListener("progress", function (e) {
    if (e.lengthComputable) {
      var percentComplete = (e.loaded / e.total) * 100;
      progressBar.style.width = percentComplete + "%";
      progressBar.textContent = Math.round(percentComplete) + "%";
    }
  });

  xhr.open("POST", "upload.php", true);
  xhr.onload = function () {
    if (xhr.status === 200) {
      console.log("File uploaded successfully!");
    } else {
      console.error("Error uploading file:", xhr.status);
    }
  };

  xhr.send(formData);
});

总结

JavaScript 表单操作是前端开发中的重要部分,它可以帮助我们:

  1. 访问和操作表单元素:获取和设置表单元素的值、属性和状态
  2. 处理表单事件:监听和响应表单的各种事件,如提交、重置、输入等
  3. 验证表单数据:在客户端进行表单验证,提高用户体验
  4. 提交表单数据:使用 AJAX 或 Fetch API 异步提交表单数据
  5. 优化表单样式:根据用户交互动态调整表单样式
  6. 提高可访问性:确保表单对所有用户(包括使用屏幕阅读器的用户)友好
  7. 优化性能:减少 DOM 操作,使用事件委托等技术提高性能

通过掌握 JavaScript 表单操作的各种技巧,我们可以创建更加用户友好、功能完善的表单,提高网站的整体质量和用户体验。