news 2026/9/28 20:16:19

HTML——庞杂的表单控件元素(一)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HTML——庞杂的表单控件元素(一)

庞杂的表单控件元素

    • 1、先从元素说起
      • 1.1、`<form>`元素的行为与特征
        • 1.1.1、submit事件与submit()方法
        • 1.1.2、requestSubmit()方法
        • 1.1.3、submitter属性
        • 1.1.4、formdata事件
        • 1.1.5、reset方法和事件
        • 1.1.6、method="dialog"
        • 1.1.7、表单元素的关联性
      • 1.2、并不简单的`<button>`按钮
        • 1.2.1、原生弹层效果
        • 1.2.2、表单外提交
        • 1.2.3、form*类型的属性
      • 1.3、好好了解一下`<select>`下拉框
        • 1.3.1、option元素
        • 1.3.2、optgroup与分组
        • 1.3.3、showPicker()方法
      • 1.4、`<textarea>`元素的精华与糟粕
        • 1.4.1、好的部分
        • 1.4.2、不好的部分
        • 1.4.3、妙用特性
      • 1.5、单选按钮、复选框行为与应用
      • 1.6、file类型输入框的隐藏知识
        • 1.6.1、accept、capture和directory属性
        • 1.6.2、样式自定义
        • 1.6.3、不使用file输入框也能上传
      • 1.7、时间日期选择框速览
      • 1.8、范围选择控件的高级应用
      • 1.9、`<datalist>`元素与列表内容的选择
        • 1.9.1、静态输入匹配
        • 1.9.2、动态输入匹配
        • 1.9.3、时间选择推荐
        • 1.9.4、推荐的范围选择值
        • 1.9.5、自定义颜色值
        • 1.9.6、样式无法自定义的遗憾

用户先输入信息,然后提交,这是几乎所有交互类应用中都存在的交互场景。重点来了,目前,由于React、Vue等框架的流行,这类数据的提交几乎都脱离HTML元素,纯数据驱动。例如发送评论,用户输入的评论信息是实时同步到数据层的,用户点击“发送”按钮会直接绕过<form>元素,直接执行POST提交方法,完成整个交互流程。

很多人会觉得这是理所当然的,甚至很多年轻人觉得这种输入型交互只有这一种处理方式。实际上,从开发的便捷程度而言,基于数据驱动的表单交互和基于行为的表单交互是差不多的,这一点我绝对有发言权,因为这两种开发模式我都经常使用。但是,两者的学习成本却有明显差别,基于数据驱动的表单交互的学习成本如果是中杯咖啡,那么基于行为驱动的表单交互的学习成本就是大杯咖啡,因为数据就是数据,只是一种概念,但表单行为却多种多样,你需要全部熟悉才能融会贯通。

所以,对于新人而言,基于数据驱动的表单交互更容易上手,也使得这种处理方式目前成为主流。但是,基于行为驱动的表单交互对用户更加友好,尤其是在机器识别和无障碍访问方面非常出色。所以,两种方法各有千秋,不分伯仲,详见下表中的对比。

“海纳百川,有容乃大”​,技术成长也是如此,不能因为自己现在所掌握的东西可以很好地应付日常开发需求,就觉得其他东西没什么好学的,这种心态不可取。​“存在即合理”​,表单体系中几十种元素和层出不穷的属性和方法都是有其应用价值的,要意识到,我们学习更多知识的目的不是颠覆现有的体系,而是取百家之长,让现有的体系更加强大。换而言之,我们需要去了解表单体系的各种行为,但并不意味着我们要舍弃数据驱动的开发,而是要想办法将表单行为和数据驱动结合,让当下的技术更加成熟与强大。

不妨举个简单的例子说明一下。要实现一个简单的评论发送功能,以Vue为例,如果完全基于数据驱动,则相关的代码会是下面这样(仅展示关键代码)​:

功能完全没问题,但是如果对表单行为比较了解,则会使用下面这样的代码:

两种实现方式的成本差不多,一个是使用<div>元素,事件绑定在按钮上;一个是使用<form>元素,事件绑定在表单元素上。但是第二种方式的用户体验明显要更好,因为用户可以直接通过键盘的回车键提交评论(包括移动端)​,而不需要点击按钮。

虽然我们最终的实现也是基于数据驱动的,但是由于加入了表单行为,让用户体验变得更好了,而不仅仅是满足功能的实现,因此本章内容的含金量其实并不低。

1、先从元素说起

1.1、<form>元素的行为与特征

知识很多,先从基本的行为说起。先抛出一个小问题,当在输入框按回车键的时候,下面的<form>元素会有提交行为吗?

答案是不会。按回车键触发表单提交的内置行为需要有一个前置条件,那就是表单元素内需要有提交按钮,注意,是提交按钮。下面这段代码有按钮元素,但却没有快捷提交行为。

在现代浏览器下,未设置type属性的元素会被认为是提交按钮,注意,在传统的IE浏览器下并非如此,因此,如果你的项目还需要支持老旧的浏览器,需要明确元素的类型,例如:


并且,具有提交行为的按钮还可以使用<input>元素,下面两种type类型均可:

其中type="image"类型的<input>元素可以看成具有表单提交行为的<img>元素,因为这两种元素支持的HTML属性比较一致,包括高宽设置的width、height属性,以及上面演示代码中出现的src和alt属性,甚至CSS中专门设置图片元素缩放规则的object-position和object-fit属性对于<input epub:type=“image”>元素同样支持。

'image’类型的输入框在十几年前经常使用,那时候CSS还很弱,所有的图形表现效果都是使用图片实现的,自然也包括提交按钮的样式,但是随着CSS 3成为Web开发的标配,这种类型的输入框也就被扫入历史的垃圾箱,大家了解一下即可,应该是没有机会使用的。

同样也没机会使用的是<input>类型的提交按钮,因为和<button>元素相比,样式自定义的能力太差了,无法内嵌子元素,也不支持CSS::before/::after伪元素,导致按钮想要实现一个loading效果或者在文字前面添加一个小图标都无法完成,自然就被开发者抛弃了。

当然,关于提交按钮的知识远不止上面这些,下一节会专门介绍,有很多你不知道的特性。

如果要从诸多元素中找一个和<a>元素的行为最相似的常见HTML元素,那一定是<form>元素了,因为两者都能触发页面的跳转,也都支持使用target属性设置是否打开新窗口,甚至都支持使用rel属性设置与链接相关的安全策略。

不过表单的跳转和传统链接的跳转还是有区别的,前者会将表单控件元素的值一起带到目标页面。

例如,下面这段代码:

在输入框输入HTML,点击按钮提交后,会跳转到https://www.htmlapi.cn/s/?q=HTML页面,这种跳转行为是脱离JavaScript的,好处在于,哪怕页面仅仅加载出了头部的搜索框,后面的页面内容和业务代码都没有加载,用户也能迅速执行搜索操作,而无须等待页面完全加载完毕,从而体验会好很多。

如果是POST请求,则传递的值不会在URL地址中明文暴露,需要通过其他方式获取,例如:

假设/login是个PHP页面,则可以使用如下所示的代码获取用户提交的手机号和密码:

并且,如果页面刷新,则浏览器会提示是否提交表单,以避免重复操作,如图所示。

不过上面这种POST数据提交行为已经被淘汰很多年了,原因就在于刷新页面的体验并不好,以及难以做到前后端分离,所以,目前Web开发中的POST请求一定都是走fetch或者XMLHttpRequest这种无刷新的开发方式。但是GET请求还是有一席之地的,以上面的搜索提交为例,出于SEO的目的,搜索页面最好有一个落地页,方便爬虫抓取,因此,直接跳转到对应的页面反而是最优解。

那么问题来了,可否在使用<form>元素保持语义的同时不触发浏览器原生的表单跳转行为呢?自然是可以的,下面开始介绍,还会介绍几个大家可能不知道的表单特性。

1.1.1、submit事件与submit()方法

只要是涉及输入类型的数据交互的,不要犹豫,一定使用<form>元素和submit事件。如果表单元素内没有提交按钮,则可以隐藏一个。例如,有些搜索功能只有一个搜索框,没有搜索按钮,就可以像下面这样设置一个hidden属性进行隐藏:


在实际开发中,涉及表单的往往是POST请求,此时可以通过给<form>元素绑定submit事件并阻止默认的行为来模拟Ajax请求功能,例如:

以上就是一整套的实现逻辑,兼顾了无障碍访问和功能实现,是目前相关功能开发的最优解。不过event.preventDefault()方法并不能阻止submit()方法导致的表单刷新,这是什么意思呢?例如,有个多行文本输入框,希望按下Ctrl+Enter组合键可以提交表单。前面的实现逻辑还好,关键是最后一步“提交表单”该如何实现呢?有人会想到通过调用<form>元素的submit()方法触发,以复用先前的submit事件代码,如下所示:

可惜,事与愿违,虽然数据提交了,但页面也刷新了。正是由于存在这样的交互场景,<form>元素新支持了一个名为requestSubmit()的方法,在触发表单提交事件的同时不会引发页面跳转。

1.1.2、requestSubmit()方法

为了直观地体现requestSubmit()方法和submit()方法的区别,我制作了一个演示页面,可以通过在浏览器中输入https://www.htmlapi.cn/7/1-1.html访问来体验。

左边的<textarea>多行输入框在按下Ctrl+Enter组合键后,可以看到页面因为表单提交属性了,同时地址也发生了变化,如图所示。

而右边的<textarea>多行输入框在按下Ctrl+Enter组合键后执行requestSubmit()方法(代码示意如下)​,此时会触发submit事件,但页面不会被提交,因此看到的是无刷新的自定义提示效果,如图所示。


当然,上述案例除可以使用requestSubmit()方法外,还可以使用以下策略:

  • 将表单提交的事件处理函数独立出去,然后重复调用。
  • 使用dispatchEvent方法触发submit事件,例如:

    最后,来看一下requestSubmit()的语法:

    这里出现了一个可选参数submitter,它是用来传递提交按钮元素的,为什么需要知道提交按钮元素呢?

这里就要说到<form>元素的一个新属性—submitter了,这个属性会记录最后一次触发submit事件的提交按钮元素,例如:

如果是点击提交按钮或者按下回车键触发的表单提交,则上面的代码输出的就是当前按钮元素,见图第一行的输出结果;如果是使用form.requestSubmit()语法触发的提交,则输出结果是null,见图第二行的输出结果。

一旦值为null,则某些交互效果就会出问题。例如,在提交表单数据的时候,我们往往会给提交按钮添加loading效果,最优雅的实现就是使用submitter属性,此时requestSubmit(submitter)方法就有了用武之地,可以保证event.submitter的值都是对应的提交按钮元素。

1.1.3、submitter属性

再来好好说说submitter属性,其作用大家想必都知道了,可以让我们知道提交的按钮元素是哪一个,可问题是,为什么需要专门用这个属性呢?想要获知提交的按钮元素,用选择器查询一下不就可以了?就像下面代码所示的效果一样:

常规的表单应用这套处理方法确实够用,也很健壮,但是日常开发场景千千万,总会有上述方法力所不逮的时候,例如:

  • (1)我想开发一个基于<form>元素的表单数据提交组件,组件中有一个小交互,那就是数据提交的过程中,需要给提交按钮设置loading效果。如果是某个具体的表单,那么问题很好解决,因为提交按钮的HTML元素已知,可以轻松匹配。可现在是组件开发,需要兼顾各种各样的表单场景,问题就来了,提交按钮有4种写法,想要准确匹配一定需要诸多逻辑判断,代码立马变得臃肿。

  • (2)<form>元素内可能会有多个提交按钮元素,应该让哪个元素loading呢?

  • (3)表单提交按钮元素可能在<form>元素的外部,例如:

    此时,就无法使用querySelector()方法进行匹配,只能遍历form.elements这个HTMLFormControlsCollection对象进行匹配了,于是又是一段臃肿的代码。

    此时,submitter属性仿佛就是救世主,满足了以上所有场景的需求,太棒了!

1.1.4、formdata事件

<form>元素现在还支持一个名为formdata的事件,之前在开发LuLu UI组件库的时候,为了实现在表单提交之前插入自定义数据的功能,我自定义了一个名为beforeSend的方法,现在浏览器支持了formdata事件,我就可以把这个自定义的方法删掉了,太酷了!

可能读者会说,你这反应也太夸张了点吧?是这样的,正如本章一开始提到的,Web数据请求有两种方式:一种是面向数据的,由于Vue/React天然可被用于数据驱动渲染,因此,在这类型项目中通过JavaScript组装提交数据具有绝对的优势。另一种是面向元素的,浏览器自带数据提取能力,请求地址是action属性,请求类型是enctype属性(支持自定义类型,例如application/json)​,只要将<form>元素组件化,就能实现和纯数据提交一样的效果,由于隐藏了数据处理细节,因此代码更精简,同时还保证了语义和最佳用户体验,最关键的是可以无限复用,可谓好处多多。

不过面向元素的实现需要对<form>元素的诸多特性非常熟悉才行,而大多数的前端开发人员在这方面的积累都很欠缺,自然对formdata事件就没什么感觉,没错,这些反应都源自知识匮乏。

好,回到正题,看一下所使用的语法,如下所示:

还是很简单的,想要对提交的数据进行提前处理,只要对event.formData的返回值进行增、删、改等处理就可以了。例如,为了防止CSRF攻击,所有的数据请求都会增加token鉴权,此时formdata事件就很有用了。例如:

绑定如下所示的formdata事件:

此时,表单提交后,虽然<form>元素内并没有任何name属性值为token的控件元素,但在提交的数据中还是出现了token字段,如图所示。

你可以通过在浏览器中输入https://www.htmlapi.cn/7/1-2.html访问来体验formdata事件效果。

formdata事件虽然是新特性,但是主流的浏览器均支持,大家可以放心使用。

1.1.5、reset方法和事件

除formdata事件外,传统的reset事件也值得一说。

首先需要了解其作用,看以下案例:

给按钮设置type="reset"声明后,点击之,表单就会重置。在上面的案例中,重置的效果就是输入框里面的值都会还原成“张三”​,注意,是还原成“张三”​,并非清空,这就是重置的作用,还原到默认的表单控件值。

除通过按钮触发外,我们还可以使用form.reset()方法重置表单,此方法比遍历所有的表单控件并还原初始值简单得多。例如有一个评论输入表单,评论发送后数据直接在列表显示,页面无刷新,此时,之前用户输入的信息就需要清除,就可以使用form.reset()。对了,还有一个场景也推荐使用reset重置数据,那就是对file类型的文件选择框的重置,因为在非现代浏览器下,input.value=''语句是无法清空file输入框的值的,此时就轮到form.reset()出马了。代码如下:

通常的业务代码到上面这一步就足够了,但由于表单的reset行为有一处不足,导致有些场景下需要再进行“扫尾”工作,即reset重置虽然改变了输入框的值,但是并不能触发输入框的change事件。这不是bug,而是特性如此,正如使用input.value给输入框赋值的时候也不会触发change事件一样。这就有问题了,例如,表单中有个<output>元素,里面的计算值和某个数值输入框的值强关联,在执行reset重置后,虽然输入框的值变成了初始值,但是这里的计算值并不会改变,因为change事件没有被触发,代码示意如下:

此时可以使用如下所示的代码打个补丁,让reset事件和change事件相关联。实现的原理也很简单,在reset事件被触发的一瞬间,输入框的值还是旧值,因此,只需要在下一个渲染周期判断输入框的值是否发生了变化,来决定是否触发输入框元素的change事件。


将以上代码复制到页面的任意位置就可以了,至此完成了整个交互行为的完美闭环。眼见为实,你可以通过在浏览器中输入https://www.htmlapi.cn/7/1-3.html访问来体验reset重置自动触发change事件的效果。

1.1.6、method=“dialog”

之前已经介绍过,这里再提一句,当<form>元素的method属性值设置为dialog的时候,不是提交表单,而是关闭所在的<dialog>对话框元素。

1.1.7、表单元素的关联性

这里的“关联性”指的是表单内所有控件元素的状态变化,<form>元素都是可感知的。<form>元素的某些属性和设置可以影响所有的表单控件元素。例如:

  • 任意的表单控件元素发生了change事件,<form>元素也会触发change事件。
  • 任意的表单控件元素发生了input事件,<form>元素也会触发input事件。
  • 任意的表单控件元素值不合法,即匹配:invalid伪类,<form>元素也会匹配。我们利用此特性让表单控件元素的值在不合法的时候禁用提交按钮,例如:
  • 表单元素设置autocomplete=“off”,所有控件元素都会关闭自动完成特性,除非自己也设置了autocomplete属性进行重置。

不过批量禁用不在其中,也就是<form>元素并没有disabled属性禁用整个表单,需要借助于<fieldset>元素,或者使用inert属性。
不过inert属性的禁用太“凶残”了,就好像将元素从页面上抹除了一般,应谨慎使用。

另外,还有若干HTML属性和DOM属性让表单元素和控件元素产生关联,form.elements可以返回所有的控件元素,input.form可以返回控件元素所在的表单元素,以及<form>元素支持直接通过id属性值获取对应的控件元素,例如:

<input>元素可以这么获取:

1.2、并不简单的<button>按钮

要使用具有点击行为的元素,哪怕是只有小图标效果的按钮,也建议使用<button>元素,因为它支持无障碍访问,同时行为与样式都控制方便。例如希望当前元素不能响应点击事件,设置disabled属性即可,样式可以充分自定义,支持::before和::after伪元素,也支持内嵌多种类型的子元素,同时还可以使用:enabled/:disabled这些CSS伪类进行专门的样式设置。

除此之外,<button>元素还有其他元素没有的优势特性。

1.2.1、原生弹层效果

<button>元素支持popovertarget和popovertargetaction,可以让设置了popover属性的任意元素以弹出层的形式显示或隐藏。

虽然都与弹出层效果相关,但popover属性是全局属性,popovertarget和popovertargetaction并不是,只能设置在按钮元素上(包括type="button"的<input>元素)​。代码如下:

1.2.2、表单外提交

因为布局的限制,或者说希望重复利用某些表单,提交按钮就需要设置在<form>元素之外,没问题,很好解决,设置form属性即可,代码如下:

此时点击按钮,就可以触发id属性值是formId的<form>元素的提交行为了。我之前在开发Dialog对话框组件的时候就用了该属性,因为对话框组件自带“确定”按钮,所以如果对话框主体有表单元素,就可以与form属性进行关联,而不用担心HTML结构的限制了。

其实不只是按钮元素,任意的表单控件元素都支持form属性,可以决定自己的数据向哪个表单提交,这种特性让表单控件元素的使用极其灵活。

1.2.3、form*类型的属性

除form属性外,<button>元素还支持formaction、formenctype、formmethod、formnovalidate、formtarget这些属性,这些属性的作用和<form>元素的同名属性作用一致,只不过是局部的。比较具有代表性的案例就是获取手机验证码的需求实现,代码如下:

<form>元素不支持嵌套,而获取验证码和表单提交属于不同的数据处理,但是提交的数据却是类似的,都需要手机号,因此,通过给“获取验证码”按钮设置formaction属性,就可以在不改变布局结构的前提下,复用当前的<form>元素,这是非常巧妙的实现。

1.3、好好了解一下<select>下拉框

<select>下拉框是常见的表单控件元素,需要配合<option>元素一起使用,代码如下:


如果<option>元素均没有selected属性,则会默认选中第一项。如果第一项是“请选择”这样的占位选项,则设置其value属性值为空字符串,有些开发人员会设置<optionvalue=“-1”>,这是一种糟糕的做法,因为浏览器自带的required验证规则会认为-1是合法的,只有空字符串才认为是未选择,才会匹配:invalid或者:user-invalid伪类。例如:

此时提交表单,若<select>元素没有选择合适的选项,则 边框会变成红色(默认不会变红,除非使用:invalid伪类)​,如图所示。

<select>元素不像<button>元素那样可以完全自定义,有诸多限制与不便,不过好在随着这些年的发展,<select>元素的自定义能力越来越强了,如果不考虑<option>元素,在现代浏览器下也是可以实现近似设计稿一般的效果的。注意,这里有个前提,那就是不考虑<option>元素,原因就在于一直到现在,<option>元素依然是无法自定义的,除颜色和背景色之外,所有浏览器均是如此。

这就使得原生的<select>元素效果不受待见,往往都使用button+ul列表的方式进行模拟(<selectlist>元素目前浏览器尚未完全支持)​,这是一件憾事,因为<select>下拉框除语义好,键盘访问支持好的优点外,还有其他一些优秀的特性,例如下拉层的顶层特性,具体描述就是,无论元素的层级有多高,只要不是top-layer,下拉列表都会在其上面显示。比如页面中有个z-index层级是999的黑色半透明覆盖层,但<select>就是个普通元素,此时,只要下拉列表显示,就一定会浮在这个层级999的元素之上,示意效果如图所示。

而自定义的下拉列表除非通过HTMLElement.showPopover()方法作为Popover元素使用,否则是无法处于层级顶层的,总会遇到被其他元素覆盖的情况。

除此之外,<select>元素还有一些属性和方法,可以快速获取或设置下拉选项的状态,例如,想要匹配选中的<option>选项,只需写一行代码:


万万不可根据selected属性去匹配,例如:

此时使用select.querySelector(‘[selected]’)语句去匹配会出问题,因为下拉框在用户切换选项的时候,在DOM元素层面,selected属性是不会跟着变化的,也就是说,虽然看起来是“选项1”被选中了,但selected属性依然在“选项2”上面。

多选

<select>元素还支持多选,语法颇为简单,设置multiple属性即可,例如:

多选模式下无须设置“请选择”这样的占位选项,因为多选选择框的各个选项默认都是展开的,不过高度并非自适应的,而是有最大高度,样式效果可参考下图。

多选框的无障碍访问特性极佳,支持拖曳连选,支持Ctrl和Shift键点击多选,看起来这是一个很棒的特性,但其实multiple多选并不实用,甚至可以说是鸡肋。因为有“一个影响”加“两个问题”​。

“一个影响”指的是由于<select>元素多选的存在,导致所有的表单控件元素中,唯独<select>元素的type属性值比较特殊,不是很多人以为的select,而是select-one或select-multiple,在遇到相关问题的时候,一定要当心,避免判断错误。

“两个问题”有大有小,第一个问题是小问题,就是选中值的获取,使用select.value返回的是最后一个选中项的值,而非所有选中项的值,因此,需要使用其他方法处理。例如:

此时,values就包含了所有选中项的值(以逗号分隔)​。在实际开发中,我们可以重置<select>元素的value属性,让其返回的是所有选中项的值(如以下代码所示)​,也可以另外扩展一个名为values的属性,保留原始的value特性。



所以第一个问题是有解的,而第二个问题则是大麻烦,那就是多选框的样式自定义,外框还好,关键是里面的<option>元素仅支持部分的CSS样式设置(例如不支持高度设置,相比过去,目前浏览器支持的CSS属性已经多了很多)​。同时,选项被选中时的背景色和文字颜色是很难被重置的,这并不表示完全没有办法被重置,只是比较麻烦。例如,虽然<option>元素自身的背景色无法重置,但是可以使用混合模式使其变色,以达到样式设置的效果,例如:


可以实现如图所示的多选框样式效果,可以看到比默认的样式效果好看多了。

可以通过在浏览器中输入https://www.htmlapi.cn/7/1-4.html访问来体验上图所示的效果。

不过上述策略只适用于桌面浏览器,且仅适用于现代浏览器,因此并不值得称道。总之,由于<option>元素孱弱的自定义能力,以及单选按钮、复选框元素的存在,使得原生的下拉框元素在目前的Web开发中不受待见。既然提到了<option>元素,那就多说两句吧。

1.3.1、option元素

首先,和过去不同,如今的<option>元素是可以单独使用的,虽然语义不对,但是并不影响渲染,例如:

依然可以匹配CSS option:checked伪类并设置对应的CSS样式。

其次,<option>元素支持名为label的属性,一旦设置,则渲染的文本是label属性值,而非元素文本,例如:

此时页面中显示的文字是“标签内容”​,而非“文本内容”​,如图所示。

但并不意味着此时<option>元素的文本内容是多余的,因为当<option>元素没有设置value属性的时候,会把里面的文本字符作为value值,如图所示。

另外,<option>元素的子元素只能是纯文本,因此支持直接使用text属性获取,而非像其他普通元素那样,只能使用textContent或innerText属性获取,例如:

且<option>元素有着和<img>、<audio>等元素一样的待遇,可以直接使用new语法进行创建,代码如下所示:

最后,<option>元素除可以作为<select>元素的项目元素之外,还可以作为<datalist>元素的项目元素。

1.3.2、optgroup与分组

下拉选项还可以使用<optgroup>元素进行分组,使用示意:

分组标题使用label属性进行设置,此时的渲染效果参见下图。其结构清晰,功能完善,但还是有同样的问题,由于样式自定义不方便,因此在前端圈子中并不流行。

1.3.3、showPicker()方法

在移动端,下拉选项大多是以Popover浮层出现的(具体还要看操作系统和版本)​,体验其实挺好的,但是下拉框本身的样式却不好看。在过去,常见的做法是将<select>元素的透明度设置为0,同时将尺寸设置为自定义按钮的大小,最后再覆盖在自定义的按钮元素上。现在有了showPicker()方法,就无需这么精准的尺寸设置了。

使用示意:

此时,配合些许的JavaScript代码,就可以实现完整的效果了,代码如下所示,效果可以通过在浏览器中输入https://www.htmlapi.cn/7/1-5.html访问来体验。

showPicker()方法不仅适用于<select>元素,也适用于日期选择输入框、颜色选择框,例如:


点击按钮后,就可以看到如图所示的颜色选择效果。

1.4、<textarea>元素的精华与糟粕

<textarea>元素也是值得提及的。

1.4.1、好的部分

<textarea>元素值得称赞的地方有两个:其一,属于正统的表单控件元素,disabled禁用、readonly只读、placeholder占位符等以及其他表单控件都有的特性均支持。其二,样式几乎可以完全自定义,可以满足日常开发的需求。

1.4.2、不好的部分

<textarea>元素被吐槽的地方更多。首先,要吐槽一下cols和rows两个HTML属性。它们是<textarea>元素专有的,前者设置列数,后者设置行数,简单易懂,也是专属属性,本该荣耀加成,可惜实用价值不大,因为不同浏览器的兼容性差异太大,最终的尺寸表现受字号、行高等值影响,难以定性,仅仅适合用在对文本域的尺寸要求不高、想要快速实现功能的场景。

其次,<textarea>元素无法根据内容的多少进行高度自适应,这一点不好。在移动端,屏幕高度有限,而通常用户输入的信息都比较少,因此,不少Web应用的输入框默认仅有一行文字的高度,只有当内容多了之后,才会自动撑开高度显示为多行,微信、QQ等APP的对话框均是如此。可问题来了,<textarea>元素的高度不能自适应对这样的需求难以支持,而大多数的HTML元素是天然高度自适应的。

目前这一问题的解决方法有如下三种。

  • (1)每当输入内容后,就使用JavaScript代码把<textarea>元素的高度设置为一行内容的高度,如果有滚动高度(scrollHeight大于clientHeight)​,就将<textarea>元素的高度设置为scrollHeight值。

  • (2)这种方法主要靠HTML和CSS实现,原理示意如下:


    此时,<textarea>元素的高度会按照<p>元素的高度实时渲染,在视觉表现上就是<textarea>元素根据输入内容的多少高度自适应了。此方法的实现依然离不开JavaScript,不过JavaScript的作用不再是计算高度,而是输入文本的信息同步,在Vue或者React等框架中,对数据同步是天然支持的,JavaScript部分的成本等于是0,因此本方法也是有一定的受众群体的。

  • (3)使用<div>元素模拟,在操作层面也有两种不同的方法,一种是HTML方法,另一种是CSS方法。首先说HTML方法,它要借助全局HTML属性contenteditable,这个HTML属性不少开发者应该知道,它可以让任意的HTML元素变得可输出,包括<style>元素。然而,我们常用的contenteditable="true"设置是有问题的,因为此时的<div>元素是支持富文本输入的,而大多数的评论或消息发送只能是纯文本的。别急,有解决方法,contenteditable属性除支持true和false外,还支持plaintext-only,顾名思义,只能是纯文本,如:

    此时,输入框既保留了<div>元素的高度自适应特性,又保证了只能输入纯文本的安全性。不过IE和传统的Edge浏览器均不支持值plaintext-only,需要注意使用范围。

    其次,CSS方法,设置user-modify:read-write-plaintext-only这个CSS声明,如:

    同样,IE浏览器是不支持user-modify属性的,但相对而言,CSS方法的兼容性要更好一些,因此,推荐大家使用CSS方法让<div>元素模拟的输入框只支持纯文本输入。

    接下来对上面三个方法做个总结。对于非React/Vue项目,请使用方法(1);对于Vue/React项目,请使用方法(2);对自己技术不自信的、对语义要求不高的,请使用方法(3)。

    最后,<textarea>元素不支持快捷键提交的表单,而很多时候,<textarea>元素就是表单输入的最后一项,支持快捷键提交还是很有必要的,此时只能辛苦开发人员进行额外的开发,要是有个原生的HTML属性支持一下就完美了。

1.4.3、妙用特性

讲完好的部分和不好的部分,下面再说一个过去很实用的特性,那就是<textarea>元素可以包含任意的非转义的HTML标签元素,且不会影响渲染,例如:

此时的渲染效果如图所示,所有内嵌的HTML元素全部变成了字符串。


这个特性很妙,既然HTML代码不会影响渲染,那么岂不是可以利用<textarea>元素来存放HTML模板?没错,在过去,聪明的Web前端开发人员就喜欢使用隐藏的<textarea>元素来存放HTML模板数据。不过如今很少有人这么做了,因为HTML 5元素中有一个名为<template>的元素,专门用来放置HTML渲染模板,更加好用。

1.5、单选按钮、复选框行为与应用

技术的流行往往取决于一些不起眼的点,而表单控件元素中的单选按钮、复选框,就因为增强的样式自定义能力,已经成为所有控件元素中交互能力最强的存在。

在深入介绍之前,先简单了解一下单选按钮、复选框元素的行为。首先,单选按钮元素一定是成组出现的,不是基于位置分组,而是基于name属性值分组,例如:

虽然上述代码的4个单选按钮结构一致,但是只有后面3个单选按钮元素是一组,因此,第一个单选按钮可以和后面的单选按钮元素同时被选中,效果如图所示。

正是由于单选按钮元素的单选能力不受位置影响,因此,在技术层面,我们可以使用单选按钮元素模拟选项卡切换效果,这是网上的某个体验地址:https://demo.cssworld.cn/selector2/11/2-2.php,不过并不推荐在实际项目中这么使用,因为语义不对,并且代码维护成本高。

复选框元素是可以单独出现的,例如:

效果如下图所示,默认是一个方形框,选中后会有个对钩。

这里的“单选按钮、复选框技术”指的是利用单选按钮的

单选特性和复选框的开关特性可以实现任意的一对一和一对多的点击交互,是单纯使用CSS实现的,无任何JavaScript参与,兼容IE 9及其以上浏览器,是所有Web前端开发人员都必学必会的前端技术。

单选按钮、复选框技术的发展可以分为三个阶段。

  • 第一阶段以<label>元素、​:checked伪类和相邻兄弟选择符为主要技术点。此阶段活跃在3~10年前。
  • 第二阶段以:checked伪类和相邻兄弟选择符为主要技术点,<label>元素可有可无。此阶段出现在3~5年前。
  • 第三阶段是:checked伪类加:has()阶段,对DOM结构已经没有任何要求。此阶段最近一年开始出现。

其中,第一阶段到第二阶段的变化主要是现代浏览器终于支持了单选按钮、复选框元素样式的完全自定义。不仅如此,还支持::before和::after伪元素,这使得单纯用一个单选按钮元素或者复选框元素就可以模拟较为复杂的图形效果,典型的例子就是开关组件的实现,如图所示。

其HTML代码非常简单,就是复选框元素,如下所示:

再配合若干自定义的CSS代码就可以实现图7-17所示的开关效果了,没有任何JavaScript代码参与,语义良好,性能优异,维护方便,是目前类似效果的最佳实现。想要查看完整的CSS代码,可以通过在浏览器中输入https://www.htmlapi.cn/7/1-6.html访问来体验。

再者,第二阶段到第三阶段的变化主要是由于现代浏览器都支持:has()伪类,可以实现类似父选择器一样的效果,将单选按钮、复选框技术的应用从DOM结构的限制中解放出来了,这是一个巨大的提升,使得类似下图这样的选择效果的最佳实现一定是使用单选按钮、复选框元素。

这里,我们以模拟<select>选择框效果来示意:has()伪类阶段的单选按钮、复选框技术是如何使用的。

由于<option>元素的样式只能部分自定义,在对样式表现要求较高的项目中,肯定是不能使用的,但是,使用<div>元素模拟又会失去表单特性,给开发带来诸多不便。此时,使用单选按钮(单选下拉)或复选框模拟(多选下拉)成为最佳选择,因为它们同属于表单元素,对最终的表单数据提交没有任何影响。

代码如下所示:

利用popover属性实现下拉列表的top-layer顶层特性,利用单选按钮模拟列表的单选效果,至于样式的设置,可以使用:has()伪类,核心代码如下:


再配合其他具有装饰作用的CSS代码,就可以实现如图所示的效果。

眼见为实,可以通过在浏览器中输入https://www.htmlapi.cn/7/1-7.html访问来体验。

1.6、file类型输入框的隐藏知识

1.6.1、accept、capture和directory属性

默认情况下,文件输入框可以选择任意的文件,但实际开发中,往往只能选择一两个类型的文件,例如要么是图像,要么是文档,此时,可以使用accept属性指定接收的文件类型。例如,希望只能传图片类型,可以使用如下所示的代码:

此时,呼起的系统选择面板的类型按钮会显示为“图片文件”​,如图所示。

如果你只想指定具体的某几种类型,则可以设置具体的MIME Type值,并使用逗号分隔,例如:

当然,多个通配符类型也支持使用逗号分隔,例如:

需要注意的是,在某些Android设备中,accept属性可能是无效的,不过这种情况并不多见,也不影响功能运行,因此可以不用在意这个细节。

下面讲一下capture属性,很多人都不知道,这个属性允许开发者调用一些设备的媒体功能。例如调用前置摄像头进行拍照:


调用后置摄像头:

不过并不是所有设备都支持呼起摄像头,因此,此功能只能作为渐进增强特性使用。

最后,文件选择框还支持文件夹上传,属性名是directory,为布尔属性,不过实际上使用的都是webkitdirectory这个私有名称,例如:


此时,当你选择完一个文件夹且经过浏览器提示确认之后(如图所示)​,就可以看到选择的文件列表信息了。

眼见为实,可以通过在浏览器中输入https://www.htmlapi.cn/7/1-8.html访问来体验。

1.6.2、样式自定义

file类型的<input>输入框的样式自定义有下面两个方法。

  • 其一,使用<label>元素模拟按钮效果,例如:

  • 其二,使用CSS::file-selector-button伪元素,例如:


    此时的效果如图所示。

    另外,如果大家希望隐藏按钮后面的“未选择任何文件”的文字,可以对当前<input>元素设置font-size:0,如图所示。

1.6.3、不使用file输入框也能上传

随着Web技术的不断发展,现在不需要依赖file类型的<input>输入框也能实现文件选择效果—使用showOpenFilePicker方法。假设页面上有个按钮,其HTML如下所示:

则下面几行JavaScript代码就可以实现点击按钮出现文件选择的效果:

真是简单又粗暴,直截又了当。另外,我们也可以使用showDirectoryPicker()方法来选择文件夹。

由于两个API参数和作用类似,因此这里只详细介绍文件的选择。语法如下所示:

其中,options是可选参数,支持下面这些属性。

  • multiple:布尔值,默认值是false,表示只能选择一个文件。
  • excludeAcceptAllOption:布尔值,默认值是false,表示是否排除下面types中所有的accept文件类型。
  • types:可选择的文件类型数组,每个数组项也是一个对象,支持下面两个参数。
    • description:表示文件或者文件夹的描述,字符串格式;可选。
    • accept:接受的文件类型,对象,然后对象的键是文件的MIME匹配,值是数组,表示支持的文件后缀。具体如何使用可以参见下面的代码。

例如,下面的JavaScript代码执行后可以实现一次性选择多张本地图片的效果:

showOpenFilePicker是个Promise方法,可以返回选择的文件列表数据。可以通过在浏览器中输入https://www.htmlapi.cn/7/1-9.html访问来体验完整案例。

1.7、时间日期选择框速览

目前支持5种类型的时间日期选择框,包括:

  • date
  • datetime-local(以前是datetime,现已废弃)
  • month
  • time
  • week

代码如下:

以上所有类型都有对应的日期时间选择面板,在不同的浏览器、不同的操作系统中都会有不一样的样式表现,下图所示为datetime-local类型的选择框在Windows系统Chrome 119版本下的效果。


所有的时间选择输入框都支持属性值min、max和step,它们分别表示最小时间、最大时间和步阶变化值。

其中,min、max包括value值都需要是合法的时间字符串,下表所示的时间表示格式都是合法的。

step属性并不会影响时间列表可选项目的数量。以time类型的输入框为例,如果希望用户选择的时间以10分钟为最小步阶,则需要设置step=“600”,因为此时step的单位是秒。

此时在时间选择面板显示时,所有的分钟选项都是存在的,如图所示。

那么step属性岂不是摆设?其实不是,step属性还是会带来两个影响。其一,当分钟值被聚焦的时候,按下上下键,会看到数值都是以10分钟为单位进行变化的,如图所示。

其二,step属性的值与表单验证强相关,假设设置如下所示的CSS代码,那么在选择的时间不是10分钟的倍数的时候,边框就会表现为红色,如图所示。


虽然浏览器内置的日期选择组件使用非常方便,但是目前其样式却无法完全自定义,准确地说,是输入框部分可以自定义(可使用如下所示的CSS伪元素)​,但是下拉面板目前却不支持。

因此,目前原生的日期选择控件元素仅适合用在对视觉效果要求不高的产品中。不过,说句心里话,浏览器默认的控件效果其实还是不错的,尤其在移动端,我觉得是可以直接使用的,毕竟,大多数用户对视觉表现其实并没有那么在意。

另外,min、max和step这三个HTML属性对于数值输入框和范围选择框同样适用,其中数值输入框比较简单,示意代码如下:

也支持样式自定义,至于具体的样式设置伪元素可以通过打开开发者工具查看,如图所示。
至于范围选择框,那可要好好介绍一下了。

1.8、范围选择控件的高级应用

范围选择控件指的是type属性值为range的<input>元素,此HTML元素的CSS自定义能力极强,加上其少有的滑动选择特性,可以实现非常罕见却又精妙绝伦的高级应用。

首先,我们来看一下范围选择控件的基本使用方法,如下所示:

此时的效果如图所示,可以看到,范围选择控件主要由背景轨道、拖曳按钮等部件组成,而现代浏览器均提供了对应的伪元素进行样式设置。

例如,使用如下所示的CSS代码,就可以让默认的范围选择控件样式变成下图所示的效果。



更高级的应用

范围选择控件还有一个非常具有代表性的应用,就是实现星星评分的效果,该应用是通过纯CSS方法实现的,无需任何JavaScript代码,支持滑动评分,同时属于表单体系元素,可以和评论等信息一起提交。

例如,下图所示的效果就是基于范围选择输入框元素实现的。

其HTML代码如下所示:

至于CSS代码,由于有一定的代码量,就不在书中展示了,大家可以通过在浏览器中输入https://www.htmlapi.cn/7/1-10.html访问来体验。

1.9、<datalist>元素与列表内容的选择

<datalist>元素是给<input>元素提供便捷输入选项的,从功能上而言,非常实用,可以大大地增强用户体验,下面通过若干案例演示其作用。

1.9.1、静态输入匹配

直接看代码:

此时,在输入框单击或双击(如Firefox浏览器)​,则会显示自动填充的列表信息,如图所示。

输入部分内容后,浏览器会自动根据输入信息进行匹配,只显示符合条件的列表,例如输入“zhang”这个字符单,此时,列表信息就只剩下两条了,如图所示。

因此,如果你有一些常用信息想提供给用户,那么<datalist>元素是一个非常好的选择,语法也非常简单,在输入框设置list属性,其值等于<datalist>元素的id值即可。

<datalist>元素里面只能是<option>元素,并且虽然<option>也支持label属性,但没有必要设置,因为会有兼容性和视觉表现的双重问题,使用value属性赋值即可。

1.9.2、动态输入匹配

<datalist>元素里面的项目列表是可以动态改变的,此时浏览器会实时匹配,因此,理论上,我们是可以使用<datalist>元素实现AutoComplete自动补全效果的。这里有个典型的案例—邮箱的输入,HTML代码如下所示:
默认是一个空<datalist>元素,然后,我们可以在用户输入的时候,动态创建<option>列表,代码如下所示:

其作用一目了然,根据用户输入的内容,自动拼接邮箱地址列表,例如,在输入框中输入“zhangxinxu”​,就会有下图所示的效果。


此时,就可以使用上下键进行选择,使用ESC退出键进行取消,非常便捷。

1.9.3、时间选择推荐

<datalist>元素不仅适用于text、email、url、tel等纯输入类型的输入框,还适用于时间选择框、范围选择框及颜色选择框,其作用是提供推荐值。例如:

此时,点击输入框中的时间选择小图标,显示的就不是完整的时间选择列表面板,而是推荐选择列表,效果如图所示,不同浏览器细节上会有差异。

只有点击底部的“其他”选项,才会回到完整的列表选择界面。

1.9.4、推荐的范围选择值

range类型的<input>输入框和<datalist>元素配合使用后,会对自身的样式产生很微妙的影响,会在推荐值的位置处留下竖线作为刻度,例如:

结果,在14、16、18和20这4个推荐值所在的位置底部出现了如图所示的刻度,这真是少见,颇为新奇。

1.9.5、自定义颜色值

体验较好的颜色选择框一定会有自定义颜色值的功能,因为用户常用的颜色往往是固定的几个,具体如何实现呢?出乎很多人的意料,其实是用<datalist>元素。

使用<datalist>元素提供一些默认色值列表,这些颜色就会暴露在颜色选择面板上,当然,不同浏览器的展示布局会有差异,以我目前使用的Chrome浏览器为例,点击输入框后出现如图所示的效果,优先展示推荐的色值,并以色块的方式显示。

其实,Chrome也是近些年才把颜色选择面板改成图7-37这种自定义风格的。在数年之前,在Windows系统下,Chrome浏览器的颜色选择框使用的还是系统颜色选择框,和现在的Firefox浏览器一样。例如,上述颜色选择代码在Firefox浏览器下会是下图这样的效果,可以看到自定义颜色那里出现的4个色值就是<datalist>元素提供的4个颜色选项。

以上5个案例效果,均可以通过在浏览器中输入https://www.htmlapi.cn/7/1-11.html访问来体验,看看你手中的浏览器呈现出来的是何种效果。

1.9.6、样式无法自定义的遗憾

从上面诸多案例可以看出,<datalist>元素比预想的要实用得多,使用方便,体验又好,具备宝藏特性。然而,<datalist>元素有一个巨大的缺陷导致其在业界并不怎么为人所知,那就是自动填充的列表样式是无法自定义的,只能跟着系统样式走,而正式的对外产品,尤其是面向客户的产品,显然是无法容忍这样粗糙的样式效果的。

因此,目前<datalist>元素只适用于原型页面、内部系统等对视觉表现要求不高的场景,着实遗憾。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 20:14:50

国内高校学生常用的AI论文网站有哪些?

国内高校学生常用的 AI 论文工具&#xff0c;以本土化全流程产品为主&#xff0c;结合通用大模型与专业辅助功能&#xff0c;覆盖选题、框架搭建、初稿撰写、语言润色、降重处理、查重检测及格式排版等关键环节&#xff0c;以下是主流工具详解与对比&#xff1a;一、本土全流程…

作者头像 李华
网站建设 2026/9/28 20:14:34

CTF 流量分析实战:从 Wireshark 到 tshark 还原 HTTP 盲注流量中的 Flag

文档网络安全教程 【免费下载链接】ctf-wiki Come and join us, we need you! 项目地址&#xff1a; https://gitcode.com/gh_mirrors/ct/ctf-wiki 点击查看 免费下载 导读 本篇技术指南以 CTF 中经典的流量包取证场景为主线&#xff0c;围绕 ctf-wiki 仓库中 HTTP 协议分析文…

作者头像 李华
网站建设 2026/9/28 20:14:23

Jev视觉生成模型全解析:定位、部署、参数调优与避坑指南

最近后台一直有人在问同一个问题&#xff1a;Jev到底是什么AI模型&#xff1f;明明不做自然语言生成&#xff0c;为什么讨论度这么高&#xff1f;我把零散的信息合并起来&#xff0c;结合自己的实测体验&#xff0c;这篇一次性把Jev的定位、申请、部署思路和典型坑全部讲透。先…

作者头像 李华
网站建设 2026/9/28 20:13:54

本地部署多模态家庭记忆档案:OCR、语音识别与向量检索搭建指南

“把短短几年的人生拆碎了&#xff0c;铺满了妻子的一生”——这句话如果从技术角度翻译&#xff0c;其实是在描述一个非常具体的数据工程任务&#xff1a;把一个人短暂人生里散落的照片、录像、录音、手写文字、社交媒体记录全部拆成可检索的原子数据&#xff0c;再通过时间线…

作者头像 李华
网站建设 2026/9/28 20:12:53

基于Catmull-Rom的曲线编辑器Demo演示

一、整体架构&#xff1a;MVC 式的 Delegate 模式 这套代码的核心思想是数据与逻辑分离&#xff1a;角色谁职责数据模型&#xff08;Model&#xff09;CurveDelegate 里的 std::vector<ImVec2> points只存数据&#xff0c;不画、不处理鼠标视图控制器&#xff08;View/Co…

作者头像 李华
网站建设 2026/9/28 20:12:48

深度学习情感分析实战:数据预处理与BiLSTM模型部署的坑与细节

简介&#xff1a;一份基于深度学习的电影评论情感分析系统完整项目&#xff0c;面向Python毕业设计、课程设计或希望入门情感分析、Web开发的读者。系统功能完善、界面美观&#xff0c;操作简单&#xff0c;管理便捷&#xff0c;前端采用HTML/CSS/JavaScript&#xff0c;后端为…

作者头像 李华