Datalist组件是一个与输入框列表相关联的万能数据列表组件,可以用于各种常见的输入框下拉列表。
作为插件单独使用
引入CSS:
<link rel="stylesheet" href="https://qidian.gtimg.com/lulu/pure/css/common/ui/Datalist.css">
引入JS:
<script src="https://qidian.gtimg.com/lulu/pure/js/common/ui/Follow.js"></script> <script src="https://qidian.gtimg.com/lulu/pure/js/common/ui/Datalist.js"></script>
<datalist>列表功能
借助原生的<datalist>
元素实现数据列表功能,适合静态数据,语义更好,功能更健壮,JS异常用户也能无障碍使用。
1. 姓名匹配-基于元素(推荐)
员工姓名:
HTML代码
<input id="inputPerson" class="ui-input" list="datalistPerson"> <!-- 员工数据 --> <datalist id="datalistPerson"> <!--[if IE 9]><select disabled style="display:none"><![endif]--> <option value="蔡世豪"> ... <option value="彭玉乐"> <!--[if IE 9]></select><![endif]--> </datalist>
JS代码
直接绑定Datalist方法即可:
new Datalist('#inputPerson');
说明
<input>
输入框元素的list
属性值和原生的<datalist>
元素的id
属性值保持一致即可。<datalist>
IE9浏览器不支持,会忽略其中的<option>
元素,可以使用<!--[if IE 9]>
条件注释嵌套一个<select>
元素。如果您的项目无需兼容IE9浏览器,可以去掉相关代码,另外,为了减少干扰,下面案例展示的源码就不显示IE9的条件注释代码了。
- 我们可以在
<input>
元素上设置results
属性指定显示最大的列表个数,可参考后面案例。默认最大显示列表个数是8
项。 <datalist>
列表输入框在点击时候,如果里面有值,会清空变成placeholder
属性值,这样完整列表可以展示,增强体验。如果不希望是这样的交互,可以设置可选参数placeholder
为false
。
2. 姓名匹配-基于数据
员工姓名:
使用可选参数中的data
传参,通过<input>
输入框的results
属性指定列表最多显示5项。
<input id="inputPerson2" class="ui-input" results="5">
new Datalist('#inputPerson2', {
// 如果数据是动态变化的,请使用function类型
data: [{
value: '蔡世豪',
id: 1
}, {
value: '李莲英',
id: 2
}, {
value: '高帆',
id: 3
}, {
value: '郑工',
id: 4
}, {
value: '陈芳',
id: 5
}, {
value: '李依琳',
id: 6
}, {
value: '彭玉乐',
id: 7
}]
});
事件传参
实际开发中,开发者更关心的是用户的ID,而不是姓名,此时,可以通过事件对象的detail
属性获取,代码示意如下:
// 演示数据参数如何传递到事件中
document.querySelector('#inputPerson2').addEventListener('change', function (event) {
console.log('用户变化,当前用户id是:' + event.detail.id);
});
在控制台就可以看到对应的输出。
3. 姓名匹配-支持拼音
Datalist组件默认前值匹配,如果想要模糊匹配,需要使用可选参数中的filter
接口修改过滤规则,可以通过插入HTML设置加粗或高亮。
员工姓名:
可以把拼音数据写在label
属性上,然后使用filter
参数进行匹配过滤。
<input id="inputPerson3" class="ui-input" list="datalistPerson3"> <datalist id="datalistPerson3"> <option value="蔡世豪" label="caishihao"> <option value="郑世军" label="zhengshijun"> <option value="李莲英" label="lilianying"> <option value="高帆" label="gaofan.a"> <option value="郑工" label="zhenggong"> <option value="陈芳" label="chenfang.c"> <option value="李依琳" label="liyilin"> <option value="彭玉乐" label="pengyule"> </datalist>
// 支持拼音 new Datalist('#inputPerson3', { // data是完整数据,value是输入框的值 filter: function (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; }); } });
说明
默认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" list="datalistHour" results="24" value="06">:<input id="inputMinute" 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 { border: 1px solid #d0d0d5; border-radius: 4px; } .input-time-x > input { width: 4em; border: 0; text-align: center; }
JS非常简单:
new Datalist('#inputHour'); new Datalist('#inputMinute');
Autocomplete功能
1. 原生autocomplete行为
会记忆当前输入框元素表单提交过的值,并在下一次连续点击的时候出现历史提交的数据列表,可以使用Del键删除某一项数据,若想清空所有数据,可以使用实例对象暴露的removeStore()
方法。
案例
输入内容并回车,触发表单的submit提交事件,此时,再次点击输入框,之前提交的数据会作为列表项呈现出来。
<form> <span class="ui-input ui-input-search" align="right"> <input type="search" id="inputSimple" name="search" results="5" placeholder="输入内容并回车" required> <button type="submit" class="ui-icon-search">搜索</button> </span> </form> <button id="clearStore" class="ui-button" data-type="primary" disabled>清除当前数据</button> <button id="clearAllStore" class="ui-button" data-type="primary">清除所有数据</button>
// 浏览器原生autocomplete模拟 var datalistSimple = new Datalist('#inputSimple'); // 其实就上面一行就可以了,下面代码是演示如何清除数据 // 两个清除按钮 var eleBtnClearStore = document.getElementById('clearStore'); var eleBtnClearAllStore = document.getElementById('clearAllStore'); eleBtnClearStore.addEventListener('click', function () { // 删除当前实例对象对应输入框里面的值 datalistSimple.removeStore(); new LightTip().success('“' + datalistSimple.value() + '”这条搜索记录已经从本地数据中清除'); }); eleBtnClearAllStore.addEventListener('click', function () { // 传入参数true表示删除所有数据 datalistSimple.removeStore(true); new LightTip().success('所有搜索历史记录已经从本地数据中清除'); });
// 表单提交后存储的提示处理
// 实际开发无需该提示
form.addEventListener('submit', function (event) {
event.preventDefault();
new LightTip().success('当前搜索内容已经在本地存储,再次点击或输入,可以触发匹配');
});
其他说明
这里的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="ajaxSearch" name="kw" placeholder="输入字母"> <button type="submit" class="ui-button" data-type="primary">搜索</button> </form>
var eleSearch = document.getElementById('ajaxSearch'); new Datalist(eleSearch, { data: { url: '/search', data: { userid: 1 }, success: function() { console.log('success callback success!'); } } });
这里的列表数据由后端提供,前端发送搜索内容给后端,后端决定要显示的数据。
参数
当可选参数data
类型为纯对象,并且有url
属性的时候,Datalist组件会认为是Ajax请求列表。
后端返回值必须是'json'类型数据。
data
对象支持下面4个属性:
- url
- 字符串。必需。请求后端数据的地址。注意,IE9浏览器不支持跨域的URL地址。
- data
- 纯对象。可选。请求后端数据时发送的数据。其中输入框数据发送是组件内置的,默认使用
<input>
元素的name
属性值作为字段发送数据,如果缺省,使用字符k
代替。 - success
- 函数。可选。成功回调,支持一个参数,为后端返回的JSON数据。
- error
- 函数。可选。失败回调。
更多说明
然而,我们实际使用的时候不会像上面这么简单,因此不同项目不同开发返回的数据格式和后台接口都是不一样的。
本Datalist组件默认支持的JSON数据结构如下:
{ data: [] }
但有些项目返回的JSON数据中的列表数组在更深的层级中,例如:
{ data: { pageInfo: {}, data: [] } }
也就是数据列表需要使用data.data
获取,此时,我们可以使用filter
参数处理,filter
参数是个专门对数据进行过滤和处理的方法,例如,我们可以这样:
new Datalist(eleSearch, { data: { url: '/search' }, filter: function(data) { return data.data; } });
如果更进一步,data.data
数组中的对象属性名不是value
(必须),也不是label
(可缺省),而是其他,例如:
{ data: { pageInfo: {}, data: [{ content: 'aaaaa' }, { content: 'bbbbb' }] } }
此时,同样在filter
方法中进行处理,返回Datalist需要的数组格式就可以了:
new Datalist($('#inputAjaxSearch'), { data: { url: '/search' }, filter: function(data) { return data.data.map(function (obj) { return { value: obj.content } }); } });
自动补全功能
例如下面的邮箱自动补全功能:
邮箱:
<input type="email" id="inputEmail" class="ui-input" size="30">
// 邮箱匹配
new Datalist('#inputEmail', {
data: ['qq.com', 'gmail.com', '126.com', '163.com'],
placeholder: false,
filter: function (data, value) {
value = value.split('@')[0];
if (!value) {
return [];
}
return data.map(function (domain) {
return {
value: value + '@' + domain
};
});
}
});
is-datalist与初始化
对于 Vue 或 Preact 等框架,直接获取 DOM 元素然后进行组件初始化是不太方便的,此时,可以使用 is-datalist
属性和 connected
自定义事件进行处理。
例如:
<div id="app"> <template v-if="1"> <input class="ui-input" :results="results" @connected="setParams" is-datalist> </template> </div>
new Vue({ el: '#app', data: { results: 5 }, methods: { setParams: function (event) { new Datalist(event.target, { data: [{ value: '蔡世豪', id: 1 }, ..., { value: '彭玉乐', id: 7 }] }); } } });
语法和参数
语法
var myDatalist = new Datalist(trigger, options);
参数
- trigger
- 必需。Object DOM对象|String选择器。指输入框DOM元素或者输入框元素的选择器。
- options
- 可选。Object纯对象。可选参数具体见下表:
参数名称 | 支持类型 | 默认值 | 释义 |
---|---|---|---|
data | String| |
'auto' | 列表的数据,默认 可以指定数据,无论是静态数组,还是函数,最终返回数据结构都要如下: {
可以指定其他自定义数据,在输入框的 |
max | String| |
8 | 最多显示的列表项条数,默认8条。使用关键字'auto' 表示数据没有限制。可以使用HTML原生的类目个数属性results results 最多显示5条列表数据。 |
width | String| |
'auto' | 列表的宽度,默认'auto' trigger |
container | Element| |
document |
DOM元素对象,指装载列表的容器,默认是<body> |
place |
Boolean | 'auto' | 是否是占位符交互模式(点击输入框值清空使用'auto' false ,其他功能识别为true 。我们也可以通过实例对象暴露的placeholder myDatalist 。 |
filter | Function | function (data, value) { /* 首字符匹配过滤 */ } |
对现有的数据进行重新过滤或者重新组装,只要返回需要的数组就好了,数组格式和细节和data 参数一致。其中,this data value |
onSelect | Function | function () {} | 列表选择时候的回调。this data ,触发元素trigger 和列表面板元素target 。只要点击和回车才触发,上下键移动不会触发此回调。 |
onShow | Function | function () {} | 列表显示时候触发的回调。this trigger target 。 |
onHide | Function | function () {} | 列表隐藏时候触发的回调。this trigger target 。 |
关于实例对象
myDatalist
就是实例对象,暴露了如下属性和方法:
{ el: { // 输入框元素 trigger: null, input: null, // append列表的容器 container: null, // 列表面板元素 target: null }, callback: { // 根据params.data生成的获得实时列表数据的方法,真正的数据源 data: function () {}, filter: function () {}, select: function () {}, show: function () {}, hide: function () {} }, params: { width: 'auto', max: 8, // 可选参数和默认参数合并后的data data: [], // 当前选中的列表所在的索引值 index: -1 }, // 交互模式是否是Placeholder模式 placeholder: true, // 当前列表显示与否 display: false, // 当前列表所使用的数据 data: [], // 存储本地输入框的值 store: function () {}, // 清除本地记录,支持1个可选的value参数,有3种逻辑: 1. 字符串内容,表示移除本地该值数据(如果有) 2. true, 表示清空本地对应该存储 3. undefined, 表示使用trigger输入框的value值作为移除对象 removeStore: function () {}, // 根据当前value值刷新列表,可以使用data参数指定数据进行列表呈现 refresh: function (data) {}, // 有value参数表示赋值,没有表示取值(xss过滤后的值) value: function (value) {}, // 事件绑定 events: function () {}, // 列表定位 position: function () {}, // 显示列表 show: function () {}, // 隐藏列表 hide: function () {} }
我们也可以通过trigger.data.datalist
获取实例对象。
new Datalist(trigger); var myDatalist = trigger.data.datalist;
Datalist组件默认关闭了边缘检测,如果希望下拉列表在浏览器外面的时候自动朝上显示,可以这么设置:
myDatalist.params.edgeAdjust = true;
综合拓展案例
这里演示如何借助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组件UI,因为我懒:
<div class="ui-select" style="width:300px;">
<a href="javascript:" id="listTrigger" class="ui-select-button"><span class="ui-select-text">企点服务迭代三(4月6日~5月5日)</span></a>
</div>
<!-- 下拉面板主体结构 -->
<div id="listTarget" class="ui-droplist-x" hidden>
<div class="ui-input ui-input-search" align="right"><input type="search"><label class="ui-icon-search">搜索</label></div>
<div id="listBox" class="datalist-container"></div>
</div>
然后是JS代码,使用了组件体系中的Drop.js,而这里的Datalist则静态处理:
// 下拉带搜索示意 var listTrigger = document.getElementById('listTrigger'); var listTarget = document.getElementById('listTarget'); // 尺寸 listTarget.style.width = listTrigger.clientWidth + 'px'; // 下拉处理 new Drop(listTrigger, listTarget, { eventType: 'click', onShow: function (trigger, target) { var input = target.querySelector('input'); var drop = this; if (!this.datalist) { // 只构造一次 var datalist = new Datalist(input, { data: data, width: 280, container: '#listBox', max: 'auto', filter: function (data, value) { return data.filter(function (obj) { return value == '' || obj.value.indexOf(value) != -1; }); }, onSelect: function (data) { var span = listTrigger.querySelector('span'); if (data && span) { span.innerHTML = data.value; drop.hide(); } } }); datalist.refresh(); this.datalist = datalist; } else { input.click(); } } });
本页贡献者:
zhangxinxu