庞杂的表单控件元素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、methoddialog1.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类型均可其中typeimage类型的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/qHTML页面这种跳转行为是脱离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()方法导致的表单刷新这是什么意思呢例如有个多行文本输入框希望按下CtrlEnter组合键可以提交表单。前面的实现逻辑还好关键是最后一步“提交表单”该如何实现呢有人会想到通过调用form元素的submit()方法触发以复用先前的submit事件代码如下所示可惜事与愿违虽然数据提交了但页面也刷新了。正是由于存在这样的交互场景form元素新支持了一个名为requestSubmit()的方法在触发表单提交事件的同时不会引发页面跳转。1.1.2、requestSubmit()方法为了直观地体现requestSubmit()方法和submit()方法的区别我制作了一个演示页面可以通过在浏览器中输入https://www.htmlapi.cn/7/1-1.html访问来体验。左边的textarea多行输入框在按下CtrlEnter组合键后可以看到页面因为表单提交属性了同时地址也发生了变化如图所示。而右边的textarea多行输入框在按下CtrlEnter组合键后执行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事件也值得一说。首先需要了解其作用看以下案例给按钮设置typereset声明后点击之表单就会重置。在上面的案例中重置的效果就是输入框里面的值都会还原成“张三”注意是还原成“张三”并非清空这就是重置的作用还原到默认的表单控件值。除通过按钮触发外我们还可以使用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并不是只能设置在按钮元素上包括typebutton的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元素效果不受待见往往都使用buttonul列表的方式进行模拟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 optionchecked伪类并设置对应的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元素。然而我们常用的contenteditabletrue设置是有问题的因为此时的div元素是支持富文本输入的而大多数的评论或消息发送只能是纯文本的。别急有解决方法contenteditable属性除支持true和false外还支持plaintext-only顾名思义只能是纯文本如此时输入框既保留了div元素的高度自适应特性又保证了只能输入纯文本的安全性。不过IE和传统的Edge浏览器均不支持值plaintext-only需要注意使用范围。其次CSS方法设置user-modifyread-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伪类和相邻兄弟选择符为主要技术点。此阶段活跃在310年前。第二阶段以checked伪类和相邻兄弟选择符为主要技术点label元素可有可无。此阶段出现在35年前。第三阶段是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-size0如图所示。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种类型的时间日期选择框包括datedatetime-local以前是datetime现已废弃monthtimeweek代码如下以上所有类型都有对应的日期时间选择面板在不同的浏览器、不同的操作系统中都会有不一样的样式表现下图所示为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元素只适用于原型页面、内部系统等对视觉表现要求不高的场景着实遗憾。
阅读完成 · 觉得有帮助?