Datalist组件是一个与输入框列表相关联的万能数据列表组件,可以用于各种常见的输入框下拉列表。
安装与调用
引入CSS:
<link rel="stylesheet" href="https://unpkg.com/lu2/theme/edge/css/common/ui/Datalist.css">
引入JS:
<script type="module"> import 'https://unpkg.com/lu2/theme/edge/js/common/ui/Datalist.js'; </script>
也可以直接src
引用。
<script type="module" src="https://unpkg.com/lu2/theme/edge/js/common/ui/Datalist.js"></script>
本组件为内置自定义元素组件,如果需要兼容Safari浏览器,还需要引入下面的JS。
<script src="https://unpkg.com/lu2/theme/edge/js/common/safari-polyfill.js"></script>
由于此Polyfill执行了querySelectorAll匹配,因此Safari浏览器下,组件connetedCallback生命周期函数的执行时机比Chrome/Firefox浏览器靠后,因此,如果要兼容Safari浏览器,在执行参数设置的时候,可以在当前输入框元素的DOMContentLoaded或connected回调中设置。详见本文档源代码。
如果是npm安装调用,则:
import 'lu2/theme/edge/css/common/ui/Datalist.css'; import 'lu2/theme/edge/js/common/safari-polyfill.js'; import 'lu2/theme/edge/js/common/ui/Datalist.js';
如果 JS import语法报错,试试在业务代码中动态引入。
import('lu2/theme/edge/js/common/safari-polyfill.js'); import('lu2/theme/edge/js/common/ui/Datalist.js');
<datalist>列表功能
借助原生的<datalist>
元素实现数据列表功能,适合静态数据,语义更好,功能更健壮,JS异常用户也能无障碍使用。
给元素添加is="ui-datalist"
属性,即可拥有自定义的Datalist
交互行为。
1. 姓名匹配-基于元素(推荐)
员工姓名:
HTML代码
<input id="i1" class="ui-input" list="d0" is="ui-datalist"> <!-- 员工数据 --> <datalist id="d0"> <option value="蔡伦"> ... <option value="彭大帅"> </datalist>
说明
is="ui-datalist"
的初始化方式只适用于基于<datalist>
元素数据的交互形式。<input>
输入框元素的list
属性值和原生的<datalist>
元素的id
属性值保持一致即可。- 我们可以在
<input>
元素上设置results
属性指定显示最大的列表个数,可参考后面案例。默认最大显示列表个数是8
项。 <datalist>
列表输入框在点击时候,如果里面有值,会清空变成placeholder
属性值,这样完整列表可以展示,增强体验。如果不希望是这样的交互,可以设置可选参数placeholder
为false
。
2. 姓名匹配-基于数据
员工姓名:
使用可选参数中的data
传参,通过<input>
输入框的results
属性指定列表最多显示5项。
<input id="i2" class="ui-input" results="5" is="ui-datalist">
i2.params.data = [{ value: '新数据蔡伦', userid: 1 }, { value: '李莲英', userid: 2 }, { value: '高松正', userid: 3 }, { value: '郑工', userid: 4 }, { value: '陈晓芳', userid: 5 }, { value: '李琳琳', userid: 6 }, { value: '彭大帅', userid: 7 }];
实际开发中,开发者更关心的往往是是userid
值,而不是用户选择的姓名名称,此时可以通过event.detail
获取,例如:
input.addEventListener('change', function (event) { console.log('选择的数据是:', event.detail); });
3. 姓名匹配-支持拼音
Datalist组件默认前值匹配,如果想要模糊匹配,需要使用可选参数中的filter
接口修改过滤规则,可以通过插入HTML设置加粗或高亮。
员工姓名:
可以把拼音数据写在label
属性上,然后使用filter
参数进行匹配过滤。
<input id="i3" class="ui-input" list="d3" is="ui-datalist"> <datalist id="d3"> <option value="蔡伦" label="cailun"> <option value="郑成功" label="zhengchenggong"> <option value="李莲英" label="lilianying"> <option value="高松正" label="gaosongzheng"> <option value="郑工" label="zhenggong"> <option value="陈晓芳" label="chenxiaofang"> <option value="李琳琳" label="lilinlin"> <option value="彭大帅" label="pengdashuai"> </datalist>
i3.params.filter = (data, value) => { if (!value) { return data; } return data.filter(function (obj) { var valueReplace = obj.value.replace(value, '<mark>' + value + '</mark>'); var valueLabel = obj.label.replace(value, '<mark>' + value + '</mark>'); if (valueReplace != obj.value || valueLabel != obj.label) { obj.value = valueReplace; obj.label = valueLabel; return true; } return false; }); } i3.addEventListener('select', function (event) { console.log('选择的数据是:', event.detail); });;
说明
默认label
值为灰色小字,如果有特殊需要想要改变列表的样式,可以通过特殊的类名进行重置。
规则是这样的,列表显示的时候,会基于<input>
输入框元素的id
值生成一个关联类名。例如<input>
元素的id
值是whatever
,则列表容器会包含一个类名ui-datalist-whatever
;又如这里的id是inputPerson3
,则列表容器会有类名ui-datalist-input-person3
。
4. 时间选择-部分选项禁用测试
凌晨00:00~05:59不能选择::
HTML代码如下:
<!-- 输入框部分 --> <span class="ui-input input-time-x"><input id="inputHour" is="ui-datalist" list="datalistHour" results="24" value="06">:<input id="inputMinute" is="ui-datalist" list="datalistMinute" results="60" value="00"></span> <!-- 数据部分,使用原生<datalist>元素 --> <!-- 小时数据 --> <datalist id="datalistHour"> <option value="00" disabled> <option value="01" disabled> ... <option value="23"> </datalist> <!-- 分钟数据 --> <datalist id="datalistMinute"> <option value="00"> <option value="01"> ... <option value="59"> </datalist>
然后CSS部分,需要微调下LuLu UI默认的输入框样式:
.input-time-x { box-shadow: inset 0 0 0 1px #d0d0d5; border-radius: 4px; background-color: #fff; } .input-time-x > input { width: 4em; border: 0; text-align: center; background-color: transparent; }
Autocomplete功能
1. 原生autocomplete行为
会记忆当前输入框元素表单提交过的值,并在下一次连续点击的时候出现历史提交的数据列表,可以使用Del键删除某一项数据,若想清空所有数据,可以使用实例对象暴露的removeStore()
方法。
案例
输入内容并回车,触发表单的submit提交事件,此时,再次点击输入框,之前提交的数据会作为列表项呈现出来。
<form> <span class="ui-input ui-input-search" align="end"> <input type="search" id="s1" name="search" results="5" is="ui-datalist" placeholder="输入内容并回车" required> <button type="submit" class="ui-icon-search">搜索</button> </span> </form> <button id="b1" class="ui-button" data-type="primary" disabled>清除当前数据</button> <button id="b2" class="ui-button" data-type="primary">清除所有数据</button>
// 下面代码是演示如何清除数据 b1.addEventListener('click', function () { // 删除当前输入框里面的值 s1.removeStore(); }); b2.addEventListener('click', function () { // 传入参数true表示删除所有数据 s1.removeStore(true); });
其他说明
这里的autocomplete自动完成功能采用和浏览器自身一样的实现机制,包括:
<form>
元素submit
事件执行的时候,才会记录该数据;- 数据查询key通过
name
属性值进行匹配; autocomplete="off"
可以关闭自动提示和记忆功能。- 操作交互也和浏览器内置交互一致,例如,输入框2次点击,显示对应列表,上下键选择,回车赋值等。
换句话说,就是把浏览器内置的autocomplete功能美化了下,所谓UI组件,本来就应该只关注UI。
当选择列表内容之后,会触发<input>
输入框的change
和input
事件。
2. Ajax autocomplate功能
<form> <input class="ui-input" type="search" id="s2" is="ui-datalist" placeholder="输入字母"> <button type="submit" class="ui-button" data-type="primary">搜索</button> </form>
// 列表内容不转义,支持HTML显示 s2.params.encode = false; // 请求的参数和回调 s2.params.data = { url: '/search', data: { userid: 1, }, name: 'kw', success() { console.log('success callback successed!'); } }
这里的列表数据由后端提供,前端发送搜索内容给后端,后端决定要显示的数据。
参数
当可选参数data
类型为纯对象,并且有url
属性的时候,Datalist组件会认为是Ajax请求列表。
后端返回值必须是'json'类型数据。
input.params.data
对象支持下面4个属性:
- url
- 字符串。必需。请求后端数据的地址。注意,IE9浏览器不支持跨域的URL地址。
- data
- 纯对象。可选。请求后端数据时发送的数据。其中输入框数据发送是组件内置的,无需额外设置。
- name
- 字符串。可选。搜索内容对应的字段名称,如果不设置,使用
<input>
元素的name
属性值,如果name
属性值也没设置,使用字符k
代替。 - success
- 函数。可选。成功回调,支持一个参数,为后端返回的JSON数据。
- error
- 函数。可选。失败回调。
更多说明
实际使用的时候不会像上面这么简单,因为不同项目后台返回的数据格式是不一样的。
本Datalist组件默认支持的JSON数据结构如下:
{ data: [] }
但有些项目返回的JSON数据中的列表数组在更深的层级中,例如:
{ data: { pageInfo: {}, data: [] } }
也就是数据列表需要使用data.data
获取,此时,我们可以使用filter
参数处理,filter
参数是个专门对数据进行过滤和处理的方法,例如,我们可以这样:
input.params.filter = function (data) { return data.data; };
如果更进一步,data.data
数组中的对象属性名不是value
(必须),也不是label
(可缺省),而是其他,例如:
{ data: { pageInfo: {}, data: [{ content: 'aaaaa' }, { content: 'bbbbb' }] } }
此时,同样在filter
方法中进行处理,返回Datalist需要的数组格式就可以了:
input.params.filter = function (data) { return data.data.map(function (obj) { return { value: obj.content } }); }
3. 基于<datalist>的autocomplate
此实现适用于Vue等数据驱动的开发场景,纯HTML驱动,<datalist>
内容是什么,列表显示就是什么,且实时变化更新。
使用方法,输入框元素设置filter="none"
,<datalist>
列表基于数据实时更新即可。
演示案例:
相关代码:
<input id="s4" class="ui-input" type="search" list="l3" filter="none" placeholder="任意输入" is="ui-datalist"> <input type="hidden" id="userid" name="userid" required> <datalist id="l3"></datalist>
// 基于datalist的autocomplete s4.addEventListener('input', function (event) { if (!event.isTrusted) { return; } const value = this.value; fetch('/search?key=' + encodeURIComponent(value)).then(function (response) { return response.json(); }).then(function (json) { l3.innerHTML = ''; json.data.forEach(function (item) { var eleOption = document.createElement('option'); eleOption.setAttribute('value', item.value); eleOption.dataset.id = item.id; l3.appendChild(eleOption); }); }); }); s4.addEventListener('change', function (event) { if (event.detail?.id) { userid.value = event.detail.id; } });
自动补全功能
例如下面的邮箱自动补全功能:
邮箱:
<input type="email" id="e1" class="ui-input" size="30" is="ui-datalist">
// 邮箱匹配
e1.setParams({
data: ['qq.com', 'gmail.com', '126.com', '163.com'],
filter: (data, value) => {
value = value.split('@')[0];
if (!value) {
return [];
}
return data.map(function (domain) {
return {
value: value + '@' + domain
};
});
}
});
语法和参数
已知:
let myDatalist = document.querySelector('input[is="ui-datalist"]');
则myDatalist
元素自身就是一个支持Datalist能力的Web组件。
直接通过对myDatalist
元素进行参数设置实现不同类型的数据列表功能。
语法
参数设置有2种方法,一种是单独设置,语法如下:
myDatalist.params.data = ...; myDatalist.params.filter = ...; ...
还有一种是一次性设置,语法如下:
myDatalist.setParams(options);
其中options
是可选参数。Object纯对象。支持的参数值见下表:
参数名称 | 支持类型 | 默认值 | 释义 |
---|---|---|---|
data | String| |
'auto' | 列表的数据,默认值 可以指定数据,无论是静态数组,还是函数,最终返回数据结构都要如下: {
可以指定其他自定义数据,例如id信息,在输入框的 |
width | String| |
'auto' | 列表的宽度,默认'auto' |
place |
Boolean | 'auto' | 是否是占位符交互模式(点击输入框值清空使用'auto' false ,其他功能识别为true 。 |
filter | Function | function (data, value) { /* 原封不动返回或首字符匹配过滤 */ } |
对现有的数据进行重新过滤或者重新组装,只要返回需要的数组就好了,数组格式和细节和data 参数一致。其中,this data value |
事件
支持的事件类型包括下面这些:
事件类型 | 释义 |
---|---|
show | 列表显示的时候触发,使用示意:input.addEventListener('show', () => { console.log('显示了'); }); |
hide | 列表隐藏的时候触发,使用示意:input.addEventListener('hide', () => { console.log('隐藏了'); }); |
select | 列表选择的时候触发,非组件触发的'select'事件event.detail 的值是undefined 。使用示意:input.addEventListener('select', (event) => {
// 上下键选择时候event.detail没有数据,走的是原生select事件
console.log(event.detail);
}); |
change | 输入框内容因为列表选择变化的时候触发,非组件触发的'change'事件event.detail 的值是undefined 。使用示意:input.addEventListener('change', event => { console.log('值选择且变化了'); }); |
input | 同change。 |
DOM |
当下拉列表面板元素初始化完毕的时候执行。 |
connected | 当组件和页面建立连接的时候执行。 |
暴露的属性和方法
myDatalist
暴露了如下属性和方法:
{ element: { // 列表面板元素 target: null }, callback: { // 根据params.data生成的获得实时列表数据的方法,真正的数据源 data: function () {}, filter: function () {}, select: function () {}, show: function () {}, hide: function () {} }, params: { width: 'auto', // 原始数据过滤和筛选方法 filter: function () {}, // 原始数据,任意类型,不同类型不同列表交互 data: object, // 占位符交互细节处理 placeholder: 'auto', // 当前列表选择的索引值 index: -1 }, // 当前列表显示与否 display: false, // 当前列表所使用的数据 datalist: [], // 存储本地输入框的值 store: function () {}, // 清除本地记录,支持1个可选的value参数,有3种逻辑: 1. 字符串内容,表示移除本地该值数据(如果有) 2. true, 表示清空本地对应该存储 3. undefined, 表示使用trigger输入框的value值作为移除对象 removeStore: function () {}, // 根据当前value值刷新列表,可以使用data参数指定数据进行列表呈现 refresh: function (data) {}, // 有value参数表示赋值,没有表示取值(HTML转义后的值) val: function (value) {}, // 列表浮层容器元素创建 create: function () {}, // 列表定位 position: function () {}, // 显示列表 show: function () {}, // 隐藏列表 hide: function () {} }
浮层的控制
Datalist组件浮层的控制参见“Color颜色选择”文档中“浮层的控制”。
综合拓展案例
这里演示如何借助Datalist组件的筛选过滤逻辑实现其他更复杂的交互效果。
带搜索的下拉列表
使用CSS重置了一些样式,例如,绝对定位就不要了,阴影效果也不要了:
.datalist-container .ui-datalist { display: block!important; position: static!important; } .datalist-container .ui-datalist-datalist { margin-top: 10px; border: 1px solid #d0d0d5; box-shadow: none; }
这里的按钮HTML直接取自Select组件:
<div class="ui-select" style="width:300px;">
<a href="javascript:" id="b0" class="ui-select-button" data-target="t0" is-drop>
<span class="ui-select-text">企点服务迭代三(4月6日~5月5日)</span>
</a>
</div>
<!-- 下拉主体结构 -->
<div id="t0" class="ui-droplist-x" hidden>
<div class="ui-input ui-input-search" align="end">
<input type="search" id="s0" is="ui-datalist"><label class="ui-icon-search">搜索</label>
</div>
<div id="c0" class="datalist-container"></div>
</div>
然后是JS代码,需要引用组件体系中的Drop.js:
// 下拉处理 s0.setParams({ width: 280, data: data, filter: function (data, value) { return data.filter(function (obj) { return value == '' || obj.value.indexOf(value) != -1; }); } }); // 选择列表修改按钮中的值 s0.addEventListener('select', function (event) { if (event.detail) { b0.querySelector('span').textContent = event.detail.value; // 下拉关闭 b0.click(); } }); // 下拉列表在特定容器显示 s0.refresh(); c0.appendChild(s0.element.target);
本页贡献者:
zhangxinxu,sunseekers