HTML 5.0在2014年定稿(其草案发表的时间甚至更早),随之而来的是<datalist> 元素。 它是2020年的,尽管它可能看起来是自定义自动完成部件的一个很好的替代品,但浏览器的问题使我摆脱了它。
我建立了一个网络应用来帮助我跟踪我的支出。这个应用是用Django写的,而且是开源的。其中一个目标是拥有一个简单的代码库,对外部JS的依赖性有限,以及在禁用JS时的基本可用性。这部分是为了方便学习标准的DOM操作程序和TypeScript。
最大的项目是一个交互式的账单编辑器(一个具有添加/编辑/删除操作的表格,以常规的HTML POST方式提交数据<form> )和一个自动完成框架(由账单编辑器以高级方式使用,以及由应用程序中的其他屏幕以更基本的功能集使用)。自动完成框架正是你所期望的:把它指向一个输入字段和一个URL,按键会导致URL被查询到这个字段以前的值,这些值会作为可能的值显示给用户,以节省输入。
使用HTML 5的自动完成功能:标签
但如何向用户显示这些选项呢?大多数人都会显示一个带有链接/按钮的position: absolute 框,围绕焦点和模糊事件再加入一些逻辑,然后就可以了。有很多现成的解决方案可以为你做这一切,尽管它们中的大多数都很糟糕。但是!HTML 5引入了一个<datalist> 标签。它看起来是你所需要的一切。你把一个<datalist> 标签链接到一个<input> ,它就会在一个自动完成风格的框中显示匹配的选项。 事实上,这里有一个简单的演示,如果你的浏览器支持它的话。
最喜欢的编程语言。 C Sharp
现在,从该演示中可以得到一些启示。
-
选项的显示顺序与源文件中的
<datalist>标签相同,这个列表在源文件中是按字母顺序反向排序的,源文件中也是这样显示的。 -
该列表是根据用户输入的子字符串进行大小写不敏感过滤的。在Chrome、Firefox和Safari中,子串可以出现在字符串中的任何一点。 但在Edge(微软的旧引擎)中,它只看字符串的开头。
-
一些浏览器在字段上显示一个箭头来显示条目,有时双击会打开列表。
-
C#的条目如下。
<option value="C#">C Sharp</option>.Chrome显示为"C# C Sharp"(分两行),Safari只显示 "C#",Firefox和Edge显示 "C Sharp"。选择该选项总是输入C#。 -
移动版Safari浏览器默认不展开列表,但在键盘上方显示一些选项(如打字预测)。你可以点击箭头,在一个滚动的选取器中显示所有的选项。
-
Android上的Chrome浏览器的显示方式与桌面上的相同(下拉列表)。
这个演示使用静态的、硬编码的数据。在 "支出 "应用程序中这样做对性能来说是很糟糕的--这将浪费带宽,迫使浏览器解析一个相当长的列表,而且当它试图扩大列表时很容易使浏览器超载。但是把它连接到一个fetch() 调用到REST API应该不难,而且当数据列表发生变化时,浏览器可以正确工作。
表情符号黑客
我需要的一个功能是让自动完成功能一次填写一个以上的字段。嗯,<datalist> ,没有具体的支持。它只支持显示一个列表,并把值放在它所连接的输入框中。但是从列表中选择一些东西会触发通常的input 事件。我选择这样做:显示每个条目,前面有一个闪光的表情符号(✨),其他两个字段也在这个字符串内,由其他表情符号分隔,然后捕捉input 事件。 如果该字段以✨为标志,那么就使用一个重构函数,从一个以表情符号为界限的字符串变成三个,并将正确的字符串放在三个输入框中(同时也从第一个字段中删除火花)。
是的,这是个黑客。但从外观上看,它相当不错,而且确实有效。它在Edge中不会工作得那么好,但我在写这篇博文之前甚至都不知道这种行为,而且最初的火花表情符号可以放弃,我仍然可以使它工作。
在手机上能用?是的,除了...
我继续并在我的网站上部署了基于<datalist> 的自动完成功能。它看起来不错,工作正常。为了在手机上使用这个东西,我有一个特殊的启动器应用程序。它存在的主要原因是什么?我想要一个主屏幕图标,但Chrome浏览器只允许渐进式网络应用程序这样做(而这是我不想做的繁忙工作),而且当时,Firefox(它没有这样的限制)不支持Android上的<datalist> 。 这个应用相当简单,有一个标准的WebView小部件和一个滑出式导航抽屉,还有其他一些好东西,而且是120 SLOC的Kotlin。
但后来,我买了一部新手机,并随之从Android 7升级到9。我在Chrome浏览器中遇到了一个bug,至今仍未修复。这个bug是:HTML数据列表在Android 8或更高版本的WebView中无法工作。
哦。我们遇到了一点问题。Firefox似乎仍然不支持<datalist> 。但是还有一种方法可以让应用程序显示网页。自定义标签。这是一个你可能已经在安卓周围看到的功能,它介于两者之间。应用程序可以对工具栏的外观进行最低限度的控制,但 "真正的 "网络浏览器负责渲染页面。自定义标签中的Chrome浏览器支持<datalist> 。所以我建立了一个小的应用程序来做我想要的事情。
只有一件小事需要解决。我在手机上的默认浏览器是Firefox Focus。Focus的主要功能是跟踪保护、内容拦截,以及存储零历史和cookies(永久隐身模式,一键清除)。这对于点击随机链接来说是完美的,尤其是我讨厌Chrome浏览器在自动完成URL时坚持显示你5年前访问过的网页。 Chrome是我在手机上的次要浏览器;在桌面上,我几乎总是打开一个隐身窗口)。
为什么Focus与这个故事有关?第一,它(仍然)不支持这个标签。 第二,这个默认浏览器也是自定义标签的提供者。这对我的网络浏览习惯来说是很好的,但不会解决这个问题。 幸运的是,这只是一个单行的修改,可以直接将意图发送到Chrome。整个东西不到30行。你可以看到完整的CustomTabsActivity.java文件,但相关部分在下面。
它似乎运行良好,显示了列表,并且可以用来输入东西,表情符号黑客也可以工作。
Android上的Chrome还有一个bug。输入一个字符有时会导致它出现两次:我输入A,提示出现,然后文本框开始显示AA,而我的提示消失了。我现在无法重现它,但这也使整个流程变得很烦人。
后果
由于所有的浏览器错误、支持问题和各种故障,我决定建立一个自己的自动完成部件。我采用了Bootstrap 4的CSS,并使用Popper.js来做定位。它看起来和工作都更好,支持键盘,而且绝对不那么黑(表情符号仍然存在,因为它们看起来不错,但我的条目知道它们的原始对象,可以直接告诉处理程序使用它,而不是使用regex)。而且,它比许多自动完成的小工具都要好,因为当你按住鼠标的时间稍长时,它们往往会失败;另外,如果有更多的空间,它可以将自己重新定位到顶部。所有这些只需要198 SLOC的TypeScript。(我还发现了我的代码中的一个bug,使它的工作变得有点糟糕,为旧的实现修复它仍然不能解决其他的问题)。
这个故事的寓意是什么?尽管HTML 5已经成为标准很多年了,但浏览器对新标签的支持似乎仍然是个问题。有时候,与其相信浏览器会做得很好,不如自己多花点功夫,建立一个好的用户界面。
这同样适用于其他 "新 "HTML 5表单元素。 <input type="date"> 在桌面Safari中不支持,在桌面Firefox和Chrome中也相当难看。 它在手机上显示标准的操作系统选取器,在安卓上得到一个日历,但在iOS上是一个滚动的选取器。datetime-local``month ,让你在Chrome浏览器上点击一天并最终选择整个月。 一个带有JavaScript的自定义组件将更加一致,而且通常更容易使用。