表单操作

表单获取

获取表单和表单元素的方法。

获取表单

// 通过ID获取
const form = document.getElementById("loginForm");

// 通过name属性获取
const formByName = document.forms.loginForm;
const formByIndex = document.forms[0];

// 通过选择器获取
const formBySelector = document.querySelector("form");

// 获取所有表单
const allForms = document.forms;
for (const form of allForms) {
    console.log(form.name);
}

表单属性

const form = document.querySelector("form");

// 表单属性
console.log(form.name);       // 表单名称
console.log(form.action);     // 提交URL
console.log(form.method);     // 提交方法
console.log(form.enctype);    // 编码类型
console.log(form.target);     // 目标窗口

// 修改属性
form.action = "/api/login";
form.method = "POST";

表单字段

访问和操作表单中的字段。

获取表单字段

const form = document.querySelector("form");

// 通过elements集合
const username = form.elements.username;
const password = form.elements["password"];
const firstField = form.elements[0];

// 通过选择器
const email = form.querySelector("input[name='email']");

// 获取所有字段
const fields = form.elements;
for (const field of fields) {
    console.log(field.name, field.value);
}

表单字段属性

const input = document.querySelector("input");

// 基本属性
console.log(input.name);       // 字段名
console.log(input.value);      // 当前值
console.log(input.type);       // 字段类型
console.log(input.disabled);   // 是否禁用
console.log(input.readOnly);   // 是否只读

// 状态属性
console.log(input.checked);    // 是否选中(radio/checkbox)
console.log(input.selected);   // 是否选中(option)

// 验证属性
console.log(input.required);   // 是否必填
console.log(input.pattern);    // 正则验证
console.log(input.min);        // 最小值
console.log(input.max);        // 最大值
console.log(input.maxLength);  // 最大长度

不同类型字段

// 文本输入
const text = document.querySelector("input[type='text']");
text.value = "东巴文";

// 复选框
const checkbox = document.querySelector("input[type='checkbox']");
checkbox.checked = true;
console.log(checkbox.checked);  // true

// 单选框
const radios = document.querySelectorAll("input[name='gender']");
radios.forEach(radio => {
    if (radio.value === "male") {
        radio.checked = true;
    }
});

// 下拉框
const select = document.querySelector("select");
select.value = "option1";  // 设置选中值
console.log(select.selectedIndex);  // 选中索引

// 多选下拉框
const multiSelect = document.querySelector("select[multiple]");
const selectedValues = [...multiSelect.selectedOptions].map(opt => opt.value);

// 文本域
const textarea = document.querySelector("textarea");
textarea.value = "多行文本内容";

表单验证

表单验证确保用户输入有效数据。

HTML5验证属性

<form id="form">
    <input type="text" name="username" required minlength="3" maxlength="20">
    <input type="email" name="email" required>
    <input type="password" name="password" required pattern=".{8,}">
    <input type="number" name="age" min="1" max="150">
    <input type="url" name="website">
    <button type="submit">提交</button>
</form>

JavaScript验证API

const form = document.querySelector("form");
const input = form.querySelector("input[name='username']");

// 检查有效性
console.log(input.validity.valid);        // 是否有效
console.log(input.validity.valueMissing); // 是否为空(required)
console.log(input.validity.tooShort);     // 是否太短
console.log(input.validity.tooLong);      // 是否太长
console.log(input.validity.patternMismatch); // 是否不匹配pattern
console.log(input.validity.rangeUnderflow);  // 是否小于min
console.log(input.validity.rangeOverflow);   // 是否大于max
console.log(input.validity.typeMismatch);    // 类型不匹配(email/url)

// 验证消息
console.log(input.validationMessage);  // 浏览器默认错误消息

// 检查整个表单
console.log(form.checkValidity());  // 所有字段是否有效

自定义验证

const password = document.querySelector("input[name='password']");
const confirm = document.querySelector("input[name='confirm']");

// 自定义验证
confirm.addEventListener("input", function() {
    if (this.value !== password.value) {
        this.setCustomValidity("两次密码不一致");
    } else {
        this.setCustomValidity("");  // 清除错误
    }
});

// 显示自定义错误
const input = document.querySelector("input");
input.setCustomValidity("自定义错误消息");
console.log(input.validationMessage);  // "自定义错误消息"

验证反馈

const form = document.querySelector("form");

// 提交时验证
form.addEventListener("submit", function(e) {
    if (!this.checkValidity()) {
        e.preventDefault();  // 阻止提交
        
        // 显示第一个错误字段
        const firstInvalid = this.querySelector(":invalid");
        firstInvalid.focus();
        
        // 显示所有错误
        const invalidFields = this.querySelectorAll(":invalid");
        invalidFields.forEach(field => {
            console.log(`${field.name}: ${field.validationMessage}`);
        });
    }
});

// 实时验证
const inputs = form.querySelectorAll("input");
inputs.forEach(input => {
    input.addEventListener("input", function() {
        if (this.validity.valid) {
            this.classList.remove("error");
        } else {
            this.classList.add("error");
        }
    });
});

表单提交

处理表单提交的方式。

提交事件

const form = document.querySelector("form");

form.addEventListener("submit", function(e) {
    e.preventDefault();  // 阻止默认提交
    
    // 获取表单数据
    const formData = new FormData(this);
    const data = Object.fromEntries(formData);
    
    console.log(data);
    
    // 发送请求
    fetch("/api/submit", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(data)
    });
});

submit方法

const form = document.querySelector("form");

// 编程式提交
form.submit();  // 不会触发submit事件

// 带验证的提交
function submitForm() {
    if (form.checkValidity()) {
        form.submit();
    } else {
        form.reportValidity();  // 显示验证错误
    }
}

重置表单

const form = document.querySelector("form");

// 重置按钮
// <button type="reset">重置</button>

// 编程式重置
form.reset();  // 恢复初始值

// 清空表单(不是重置)
function clearForm(form) {
    const inputs = form.querySelectorAll("input, textarea, select");
    inputs.forEach(input => {
        if (input.type === "checkbox" || input.type === "radio") {
            input.checked = false;
        } else {
            input.value = "";
        }
    });
}

表单序列化

将表单数据转换为可发送的格式。

FormData

const form = document.querySelector("form");

// 创建FormData
const formData = new FormData(form);

// 遍历数据
for (const [name, value] of formData) {
    console.log(name, value);
}

// 获取特定字段
console.log(formData.get("username"));
console.log(formData.getAll("hobbies"));  // 多值字段

// 添加数据
formData.append("extra", "额外数据");

// 删除数据
formData.delete("extra");

// 检查是否存在
console.log(formData.has("username"));

转换为对象

const form = document.querySelector("form");
const formData = new FormData(form);

// 转换为普通对象
const data = Object.fromEntries(formData);
console.log(data);  // { username: "东巴文", email: "test@example.com" }

// 处理多值字段
function formToObject(form) {
    const formData = new FormData(form);
    const result = {};
    
    for (const [name, value] of formData) {
        if (result[name]) {
            if (Array.isArray(result[name])) {
                result[name].push(value);
            } else {
                result[name] = [result[name], value];
            }
        } else {
            result[name] = value;
        }
    }
    
    return result;
}

序列化为查询字符串

function serialize(form) {
    const formData = new FormData(form);
    const params = new URLSearchParams();
    
    for (const [name, value] of formData) {
        params.append(name, value);
    }
    
    return params.toString();
}

// 使用
const queryString = serialize(form);
console.log(queryString);  // "username=东巴文&email=test@example.com"

文件上传

处理文件上传功能。

获取文件

const fileInput = document.querySelector("input[type='file']");

fileInput.addEventListener("change", function() {
    // 获取文件列表
    const files = this.files;
    
    // 遍历文件
    for (const file of files) {
        console.log(file.name);      // 文件名
        console.log(file.size);      // 文件大小(字节)
        console.log(file.type);      // MIME类型
        console.log(file.lastModified);  // 最后修改时间
    }
});

// 多文件上传
// <input type="file" multiple>
const multiFiles = fileInput.files;  // FileList

文件预览

const fileInput = document.querySelector("input[type='file']");
const preview = document.querySelector("#preview");

fileInput.addEventListener("change", function() {
    const file = this.files[0];
    
    if (file && file.type.startsWith("image/")) {
        const reader = new FileReader();
        
        reader.onload = function(e) {
            const img = document.createElement("img");
            img.src = e.target.result;
            preview.appendChild(img);
        };
        
        reader.readAsDataURL(file);
    }
});

上传文件

async function uploadFile(file) {
    const formData = new FormData();
    formData.append("file", file);
    
    const response = await fetch("/api/upload", {
        method: "POST",
        body: formData
    });
    
    return response.json();
}

// 带进度上传
function uploadWithProgress(file, onProgress) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        const formData = new FormData();
        formData.append("file", file);
        
        xhr.upload.addEventListener("progress", (e) => {
            if (e.lengthComputable) {
                const percent = (e.loaded / e.total) * 100;
                onProgress(percent);
            }
        });
        
        xhr.addEventListener("load", () => {
            resolve(JSON.parse(xhr.responseText));
        });
        
        xhr.addEventListener("error", reject);
        
        xhr.open("POST", "/api/upload");
        xhr.send(formData);
    });
}

富文本编辑

实现简单的富文本编辑功能。

contenteditable

// HTML: <div contenteditable="true" id="editor"></div>
const editor = document.querySelector("#editor");

// 获取内容
console.log(editor.innerHTML);
console.log(editor.textContent);

// 设置内容
editor.innerHTML = "<p>东巴文</p>";

// 监听内容变化
editor.addEventListener("input", function() {
    console.log("内容已更改");
});

document.execCommand

const editor = document.querySelector("#editor");

// 加粗
document.execCommand("bold");

// 斜体
document.execCommand("italic");

// 下划线
document.execCommand("underline");

// 插入链接
document.execCommand("createLink", false, "https://db-w.cn");

// 插入图片
document.execCommand("insertImage", false, "image.png");

// 格式块
document.execCommand("formatBlock", false, "h1");

// 注意:execCommand已废弃,建议使用现代API

Selection API

// 获取选区
const selection = window.getSelection();
const range = selection.getRangeAt(0);

// 获取选区内容
const selectedText = selection.toString();

// 获取选区位置
const rect = range.getBoundingClientRect();

// 包裹选区
const span = document.createElement("span");
span.className = "highlight";
range.surroundContents(span);

下一步

掌握了表单操作后,让我们继续学习:

  1. 事件基础 - 学习事件处理
  2. 事件绑定 - 学习事件绑定
  3. 事件类型 - 学习各种事件

东巴文(db-w.cn) - 让编程学习更简单

📝 东巴文寄语:表单是用户与网页交互的重要方式,掌握表单获取、验证、提交和文件上传等技术,能让你构建功能完善的用户界面。在 db-w.cn,我们帮你精通表单操作!