表单进阶

表单进阶概述

在掌握了表单基础后,我们需要学习更多高级特性,包括表单控件、验证API、数据提交等。

东巴文(db-w.cn) 认为:高级表单技术能够创建更强大、更友好的用户交互界面。

HTML5新输入类型

日期时间类型

<!-- 日期选择器 -->
<input type="date" name="birthday">

<!-- 时间选择器 -->
<input type="time" name="appointment">

<!-- 日期时间选择器 -->
<input type="datetime-local" name="meeting">

<!-- 月份选择器 -->
<input type="month" name="month">

<!-- 周选择器 -->
<input type="week" name="week">

东巴文日期时间类型对比

类型 格式 示例值 说明
date YYYY-MM-DD 2024-01-15 日期
time HH:MM 14:30 时间
datetime-local YYYY-MM-DDTHH:MM 2024-01-15T14:30 日期时间
month YYYY-MM 2024-01 月份
week YYYY-Www 2024-W03

数字和范围

<!-- 数字输入 -->
<input type="number" name="quantity" min="1" max="100" step="1" value="1">

<!-- 范围滑块 -->
<input type="range" name="volume" min="0" max="100" step="10" value="50">
<output for="volume" id="volume-output">50</output>

<script>
    const range = document.querySelector('input[name="volume"]');
    const output = document.getElementById('volume-output');
    
    range.addEventListener('input', function() {
        output.textContent = this.value;
    });
</script>

东巴文数字属性

属性 说明 示例
min 最小值 min="0"
max 最大值 max="100"
step 步长 step="0.1"
value 默认值 value="50"

颜色选择器

<input type="color" name="color" value="#ff0000">
<output for="color" id="color-output">#ff0000</output>

<script>
    const colorInput = document.querySelector('input[name="color"]');
    const colorOutput = document.getElementById('color-output');
    
    colorInput.addEventListener('input', function() {
        colorOutput.textContent = this.value;
    });
</script>

搜索框

<!-- 搜索框 -->
<input type="search" name="q" placeholder="搜索..." results="5" autosave="search-history">

<!-- 带建议的搜索框 -->
<input type="search" name="q" list="search-suggestions" placeholder="搜索课程...">
<datalist id="search-suggestions">
    <option value="HTML基础">
    <option value="CSS样式">
    <option value="JavaScript入门">
    <option value="Python编程">
</datalist>

<datalist>元素

<datalist>元素为输入框提供预定义选项:

<input type="text" name="browser" list="browsers" placeholder="选择浏览器">
<datalist id="browsers">
    <option value="Chrome">
    <option value="Firefox">
    <option value="Safari">
    <option value="Edge">
    <option value="Opera">
</datalist>

东巴文说明

  • <datalist>提供自动完成建议
  • 用户可以选择或输入自定义值
  • <select>不同,不限制输入

<datalist><select>对比

东巴文对比表格

特性 <datalist> <select>
输入方式 可输入+选择 只能选择
自定义值 ✅ 允许 ❌ 不允许
搜索功能 ✅ 支持 ❌ 不支持
移动端体验 较好 很好
适用场景 搜索、建议 固定选项

<output>元素

<output>元素用于显示计算结果:

<form oninput="result.value = parseInt(a.value) + parseInt(b.value)">
    <input type="number" name="a" value="0"> +
    <input type="number" name="b" value="0"> =
    <output name="result">0</output>
</form>

东巴文output属性

属性 说明 示例
for 关联元素ID for="a b"
name 名称 name="result"
form 所属表单 form="myForm"

<progress><meter>元素

<progress>元素

显示任务完成进度:

<!-- 不确定进度 -->
<progress>加载中...</progress>

<!-- 确定进度 -->
<progress value="70" max="100">70%</progress>

<!-- JavaScript控制 -->
<progress id="download-progress" value="0" max="100"></progress>
<button onclick="startDownload()">开始下载</button>

<script>
    function startDownload() {
        const progress = document.getElementById('download-progress');
        let value = 0;
        
        const interval = setInterval(() => {
            value += 10;
            progress.value = value;
            
            if (value >= 100) {
                clearInterval(interval);
                alert('下载完成!');
            }
        }, 500);
    }
</script>

<meter>元素

显示已知范围内的标量测量值:

<!-- 磁盘使用量 -->
<meter value="0.7" min="0" max="1">70%</meter>

<!-- 考试分数 -->
<meter value="85" min="0" max="100" low="60" high="90" optimum="100">
    85分
</meter>

<!-- 温度 -->
<meter value="25" min="0" max="40" low="10" high="30" optimum="20">
    25°C
</meter>

东巴文meter属性

属性 说明 示例
value 当前值 value="70"
min 最小值 min="0"
max 最大值 max="100"
low 低值区域 low="30"
high 高值区域 high="70"
optimum 最优值 optimum="50"

东巴文说明

  • <progress>:用于任务进度(动态)
  • <meter>:用于静态测量值

表单验证API

约束验证API

东巴文验证API属性

属性/方法 说明 返回值
checkValidity() 检查有效性 boolean
reportValidity() 检查并报告 boolean
setCustomValidity(msg) 设置自定义消息 void
validationMessage 验证消息 string
validity 有效性状态对象 ValidityState
willValidate 是否会验证 boolean

ValidityState对象

东巴文ValidityState属性

属性 说明 示例场景
valueMissing 值缺失 required字段为空
typeMismatch 类型不匹配 email格式错误
patternMismatch 模式不匹配 pattern验证失败
tooLong 超出最大长度 超过maxlength
tooShort 小于最小长度 小于minlength
rangeUnderflow 小于最小值 小于min
rangeOverflow 大于最大值 大于max
stepMismatch 步长不匹配 不符合step
customError 自定义错误 setCustomValidity设置
valid 是否有效 所有验证通过

验证示例

<form id="myForm">
    <input type="email" id="email" name="email" required>
    <span id="email-error" class="error"></span>
    
    <input type="password" id="password" name="password" 
           minlength="6" required>
    <span id="password-error" class="error"></span>
    
    <button type="submit">提交</button>
</form>

<script>
    const form = document.getElementById('myForm');
    const email = document.getElementById('email');
    const password = document.getElementById('password');
    
    // 实时验证
    email.addEventListener('input', function() {
        validateEmail();
    });
    
    password.addEventListener('input', function() {
        validatePassword();
    });
    
    function validateEmail() {
        const error = document.getElementById('email-error');
        
        if (email.validity.valueMissing) {
            error.textContent = '请输入邮箱地址';
        } else if (email.validity.typeMismatch) {
            error.textContent = '请输入有效的邮箱地址';
        } else {
            error.textContent = '';
        }
    }
    
    function validatePassword() {
        const error = document.getElementById('password-error');
        
        if (password.validity.valueMissing) {
            error.textContent = '请输入密码';
        } else if (password.validity.tooShort) {
            error.textContent = `密码至少需要${password.minLength}个字符`;
        } else {
            error.textContent = '';
        }
    }
    
    form.addEventListener('submit', function(e) {
        validateEmail();
        validatePassword();
        
        if (!form.checkValidity()) {
            e.preventDefault();
        }
    });
</script>

表单提交

FormData对象

FormData对象用于构建表单数据:

<form id="myForm">
    <input type="text" name="username" value="东巴文">
    <input type="email" name="email" value="test@db-w.cn">
    <button type="submit">提交</button>
</form>

<script>
    const form = document.getElementById('myForm');
    
    form.addEventListener('submit', function(e) {
        e.preventDefault();
        
        // 方式1:从表单创建
        const formData = new FormData(form);
        
        // 方式2:手动创建
        const formData2 = new FormData();
        formData2.append('username', '东巴文');
        formData2.append('email', 'test@db-w.cn');
        
        // 发送数据
        fetch('/api/submit', {
            method: 'POST',
            body: formData
        })
        .then(response => response.json())
        .then(data => console.log(data));
    });
</script>

东巴文FormData方法

方法 说明 示例
append(name, value) 添加数据 formData.append('name', 'value')
delete(name) 删除数据 formData.delete('name')
get(name) 获取值 formData.get('name')
getAll(name) 获取所有值 formData.getAll('hobby')
has(name) 是否存在 formData.has('name')
set(name, value) 设置值 formData.set('name', 'value')
entries() 所有条目 formData.entries()
keys() 所有键 formData.keys()
values() 所有值 formData.values()

文件上传

<form id="uploadForm">
    <input type="file" id="fileInput" name="file" multiple>
    <button type="submit">上传</button>
</form>

<script>
    const form = document.getElementById('uploadForm');
    const fileInput = document.getElementById('fileInput');
    
    form.addEventListener('submit', function(e) {
        e.preventDefault();
        
        const formData = new FormData();
        
        // 添加多个文件
        for (let file of fileInput.files) {
            formData.append('files', file);
        }
        
        // 上传
        fetch('/api/upload', {
            method: 'POST',
            body: formData
        })
        .then(response => response.json())
        .then(data => {
            console.log('上传成功', data);
        })
        .catch(error => {
            console.error('上传失败', error);
        });
    });
</script>

表单自动完成

autocomplete属性

<!-- 开启自动完成 -->
<form autocomplete="on">
    <input type="text" name="name" autocomplete="name">
    <input type="email" name="email" autocomplete="email">
    <input type="tel" name="tel" autocomplete="tel">
    <input type="text" name="address" autocomplete="street-address">
</form>

<!-- 关闭自动完成 -->
<form autocomplete="off">
    <input type="text" name="captcha" autocomplete="off">
</form>

东巴文autocomplete属性值

属性值 说明 使用场景
on 开启 默认值
off 关闭 验证码、一次性输入
name 姓名 全名
email 邮箱 邮箱地址
tel 电话 电话号码
username 用户名 登录用户名
current-password 当前密码 登录密码
new-password 新密码 注册/修改密码
street-address 街道地址 详细地址
cc-number 信用卡号 支付信息

表单最佳实践

东巴文表单进阶法则

法则1:使用合适的输入类型

<!-- ✅ 推荐:使用正确的类型 -->
<input type="email" name="email">
<input type="tel" name="phone">
<input type="url" name="website">

<!-- ❌ 不推荐:全部使用text -->
<input type="text" name="email">
<input type="text" name="phone">

法则2:提供自动完成支持

<!-- ✅ 推荐:添加autocomplete -->
<input type="text" name="name" autocomplete="name">
<input type="email" name="email" autocomplete="email">

<!-- ❌ 不推荐:无autocomplete -->
<input type="text" name="name">
<input type="email" name="email">

法则3:使用datalist提供建议

<!-- ✅ 推荐:提供建议选项 -->
<input type="text" name="city" list="cities">
<datalist id="cities">
    <option value="北京">
    <option value="上海">
    <option value="广州">
</datalist>

法则4:实时验证反馈

<!-- ✅ 推荐:实时验证 -->
<input type="email" name="email" 
       oninput="validateEmail(this)"
       oninvalid="showError(this)">

<!-- ❌ 不推荐:只在提交时验证 -->
<form onsubmit="return validateForm()">

法则5:使用FormData处理数据

<!-- ✅ 推荐:使用FormData -->
const formData = new FormData(form);

<!-- ❌ 不推荐:手动拼接数据 -->
const data = 'username=' + username + '&email=' + email;

学习检验

知识点测试

问题1:以下哪个元素用于显示计算结果?

A. <result> B. <output> C. <display> D. <show>

<details> <summary>点击查看答案</summary>

答案:B

东巴文解释<output>元素用于显示计算结果或用户操作的结果。HTML中没有<result><display><show>这些元素。

</details>

问题2:以下哪个方法用于检查表单元素的有效性?

A. validate() B. checkValidity() C. isValid() D. testValidity()

<details> <summary>点击查看答案</summary>

答案:B

东巴文解释checkValidity()方法用于检查表单元素的有效性,返回boolean值。HTML5验证API中没有validate()isValid()testValidity()这些方法。

</details>

实践任务

任务:创建一个包含以下特性的高级表单:

  1. 多种HTML5输入类型(日期、颜色、范围等)
  2. datalist自动完成
  3. 实时验证反馈
  4. FormData提交
  5. 进度显示
<details> <summary>点击查看参考答案</summary>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>高级表单 - 东巴文</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: #f5f5f5;
            padding: 20px;
        }
        
        .container {
            max-width: 800px;
            margin: 0 auto;
            background: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        
        h1 {
            color: #333;
            margin-bottom: 30px;
            text-align: center;
        }
        
        .form-section {
            margin-bottom: 30px;
            padding: 20px;
            background: #f9f9f9;
            border-radius: 6px;
        }
        
        .form-section h2 {
            color: #555;
            font-size: 18px;
            margin-bottom: 15px;
            border-bottom: 2px solid #4CAF50;
            padding-bottom: 8px;
        }
        
        .form-group {
            margin-bottom: 15px;
        }
        
        label {
            display: block;
            margin-bottom: 5px;
            color: #555;
            font-weight: 500;
        }
        
        input[type="text"],
        input[type="email"],
        input[type="password"],
        input[type="date"],
        input[type="time"],
        input[type="datetime-local"],
        input[type="month"],
        input[type="week"],
        input[type="number"],
        input[type="tel"],
        input[type="url"],
        input[type="search"],
        select,
        textarea {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
        }
        
        input:focus,
        select:focus,
        textarea:focus {
            outline: none;
            border-color: #4CAF50;
            box-shadow: 0 0 5px rgba(76, 175, 80, 0.3);
        }
        
        input:invalid:not(:placeholder-shown) {
            border-color: #f44336;
        }
        
        input:valid:not(:placeholder-shown) {
            border-color: #4CAF50;
        }
        
        .error-message {
            color: #f44336;
            font-size: 12px;
            margin-top: 5px;
        }
        
        .range-output {
            display: inline-block;
            margin-left: 10px;
            font-weight: bold;
            color: #4CAF50;
        }
        
        .color-preview {
            display: inline-block;
            width: 30px;
            height: 30px;
            border-radius: 4px;
            vertical-align: middle;
            margin-left: 10px;
            border: 1px solid #ddd;
        }
        
        input[type="range"] {
            width: calc(100% - 50px);
        }
        
        input[type="color"] {
            width: 60px;
            height: 40px;
            padding: 2px;
        }
        
        button {
            padding: 12px 30px;
            background: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            margin-right: 10px;
            transition: background 0.3s;
        }
        
        button:hover {
            background: #45a049;
        }
        
        button:disabled {
            background: #ccc;
            cursor: not-allowed;
        }
        
        .progress-container {
            margin-top: 20px;
            display: none;
        }
        
        progress {
            width: 100%;
            height: 20px;
        }
        
        .result {
            margin-top: 20px;
            padding: 15px;
            background: #e8f5e9;
            border-radius: 4px;
            display: none;
        }
        
        .result h3 {
            color: #4CAF50;
            margin-bottom: 10px;
        }
        
        .result pre {
            background: white;
            padding: 10px;
            border-radius: 4px;
            overflow-x: auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>东巴文高级表单示例</h1>
        
        <form id="advancedForm">
            <!-- 日期时间类型 -->
            <div class="form-section">
                <h2>日期时间输入</h2>
                
                <div class="form-group">
                    <label for="birthday">出生日期</label>
                    <input type="date" id="birthday" name="birthday" required>
                </div>
                
                <div class="form-group">
                    <label for="appointment">预约时间</label>
                    <input type="time" id="appointment" name="appointment" required>
                </div>
                
                <div class="form-group">
                    <label for="meeting">会议时间</label>
                    <input type="datetime-local" id="meeting" name="meeting">
                </div>
            </div>
            
            <!-- 数字和范围 -->
            <div class="form-section">
                <h2>数字和范围</h2>
                
                <div class="form-group">
                    <label for="age">年龄</label>
                    <input type="number" id="age" name="age" min="1" max="120" value="25">
                </div>
                
                <div class="form-group">
                    <label for="volume">音量</label>
                    <input type="range" id="volume" name="volume" min="0" max="100" value="50">
                    <output for="volume" class="range-output">50</output>
                </div>
                
                <div class="form-group">
                    <label for="price">价格(元)</label>
                    <input type="number" id="price" name="price" min="0" step="0.01" value="99.99">
                </div>
            </div>
            
            <!-- 颜色选择器 -->
            <div class="form-section">
                <h2>颜色选择</h2>
                
                <div class="form-group">
                    <label for="color">主题颜色</label>
                    <input type="color" id="color" name="color" value="#4CAF50">
                    <span class="color-preview" id="colorPreview" style="background: #4CAF50;"></span>
                    <output for="color" class="range-output">#4CAF50</output>
                </div>
            </div>
            
            <!-- 自动完成 -->
            <div class="form-section">
                <h2>自动完成建议</h2>
                
                <div class="form-group">
                    <label for="browser">浏览器</label>
                    <input type="text" id="browser" name="browser" list="browsers" 
                           placeholder="选择或输入浏览器名称">
                    <datalist id="browsers">
                        <option value="Chrome">
                        <option value="Firefox">
                        <option value="Safari">
                        <option value="Edge">
                        <option value="Opera">
                    </datalist>
                </div>
                
                <div class="form-group">
                    <label for="city">城市</label>
                    <input type="text" id="city" name="city" list="cities" 
                           placeholder="选择或输入城市名称">
                    <datalist id="cities">
                        <option value="北京">
                        <option value="上海">
                        <option value="广州">
                        <option value="深圳">
                        <option value="杭州">
                    </datalist>
                </div>
            </div>
            
            <!-- 实时验证 -->
            <div class="form-section">
                <h2>实时验证</h2>
                
                <div class="form-group">
                    <label for="email">邮箱</label>
                    <input type="email" id="email" name="email" required
                           placeholder="请输入邮箱地址">
                    <span class="error-message" id="email-error"></span>
                </div>
                
                <div class="form-group">
                    <label for="phone">手机号</label>
                    <input type="tel" id="phone" name="phone" 
                           pattern="[0-9]{11}" required
                           placeholder="请输入11位手机号">
                    <span class="error-message" id="phone-error"></span>
                </div>
                
                <div class="form-group">
                    <label for="password">密码</label>
                    <input type="password" id="password" name="password" 
                           minlength="6" maxlength="20" required
                           placeholder="6-20个字符">
                    <span class="error-message" id="password-error"></span>
                </div>
            </div>
            
            <!-- 提交按钮 -->
            <div style="text-align: center; margin-top: 30px;">
                <button type="submit">提交表单</button>
                <button type="reset">重置</button>
            </div>
            
            <!-- 进度条 -->
            <div class="progress-container" id="progressContainer">
                <progress id="submitProgress" value="0" max="100"></progress>
                <p style="text-align: center; margin-top: 10px;">提交中...</p>
            </div>
            
            <!-- 结果显示 -->
            <div class="result" id="result">
                <h3>提交成功!</h3>
                <p>表单数据:</p>
                <pre id="resultData"></pre>
            </div>
        </form>
    </div>
    
    <script>
        const form = document.getElementById('advancedForm');
        
        // 范围滑块实时显示
        const volumeInput = document.getElementById('volume');
        const volumeOutput = document.querySelector('output[for="volume"]');
        
        volumeInput.addEventListener('input', function() {
            volumeOutput.textContent = this.value;
        });
        
        // 颜色选择器实时显示
        const colorInput = document.getElementById('color');
        const colorPreview = document.getElementById('colorPreview');
        const colorOutput = document.querySelector('output[for="color"]');
        
        colorInput.addEventListener('input', function() {
            colorPreview.style.background = this.value;
            colorOutput.textContent = this.value;
        });
        
        // 实时验证
        const emailInput = document.getElementById('email');
        const phoneInput = document.getElementById('phone');
        const passwordInput = document.getElementById('password');
        
        emailInput.addEventListener('input', function() {
            const error = document.getElementById('email-error');
            if (this.validity.valueMissing) {
                error.textContent = '请输入邮箱地址';
            } else if (this.validity.typeMismatch) {
                error.textContent = '请输入有效的邮箱地址';
            } else {
                error.textContent = '';
            }
        });
        
        phoneInput.addEventListener('input', function() {
            const error = document.getElementById('phone-error');
            if (this.validity.valueMissing) {
                error.textContent = '请输入手机号';
            } else if (this.validity.patternMismatch) {
                error.textContent = '请输入11位手机号';
            } else {
                error.textContent = '';
            }
        });
        
        passwordInput.addEventListener('input', function() {
            const error = document.getElementById('password-error');
            if (this.validity.valueMissing) {
                error.textContent = '请输入密码';
            } else if (this.validity.tooShort) {
                error.textContent = `密码至少需要${this.minLength}个字符`;
            } else if (this.validity.tooLong) {
                error.textContent = `密码最多${this.maxLength}个字符`;
            } else {
                error.textContent = '';
            }
        });
        
        // 表单提交
        form.addEventListener('submit', function(e) {
            e.preventDefault();
            
            if (!form.checkValidity()) {
                alert('请正确填写所有必填项!');
                return;
            }
            
            // 显示进度
            const progressContainer = document.getElementById('progressContainer');
            const progress = document.getElementById('submitProgress');
            progressContainer.style.display = 'block';
            
            // 模拟提交进度
            let value = 0;
            const interval = setInterval(() => {
                value += 10;
                progress.value = value;
                
                if (value >= 100) {
                    clearInterval(interval);
                    
                    // 显示结果
                    const formData = new FormData(form);
                    const data = {};
                    for (let [key, value] of formData.entries()) {
                        data[key] = value;
                    }
                    
                    document.getElementById('resultData').textContent = 
                        JSON.stringify(data, null, 2);
                    document.getElementById('result').style.display = 'block';
                    progressContainer.style.display = 'none';
                }
            }, 200);
        });
    </script>
</body>
</html>
</details>

东巴文(db-w.cn) - 让编程学习更有趣、更高效!