LuLu UI Edge版中文文档 » Datalist数据列表

教程

Datalist数据列表

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>

说明

  1. is="ui-datalist"的初始化方式只适用于基于<datalist>元素数据的交互形式。
  2. <input>输入框元素的list属性值和原生的<datalist>元素的id属性值保持一致即可。
  3. 我们可以在<input>元素上设置results属性指定显示最大的列表个数,可参考后面案例。默认最大显示列表个数是8项。
  4. <datalist>列表输入框在点击时候,如果里面有值,会清空变成placeholder属性值,这样完整列表可以展示,增强体验。如果不希望是这样的交互,可以设置可选参数placeholderfalse

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自动完成功能采用和浏览器自身一样的实现机制,包括:

  1. <form>元素submit事件执行的时候,才会记录该数据;
  2. 数据查询key通过name属性值进行匹配;
  3. autocomplete="off"可以关闭自动提示和记忆功能。
  4. 操作交互也和浏览器内置交互一致,例如,输入框2次点击,显示对应列表,上下键选择,回车赋值等。

换句话说,就是把浏览器内置的autocomplete功能美化了下,所谓UI组件,本来就应该只关注UI。

当选择列表内容之后,会触发<input>输入框的changeinput事件。

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|Array|Function|Object 'auto'

列表的数据,默认值auto表示根据输入框元素自身属性进行类型识别。

可以指定数据,无论是静态数组,还是函数,最终返回数据结构都要如下:

{
  // 可选,禁用
  disabled: '',
  // 可选,右侧小文字
  label: '',
  // 必须
  value: ''
}

可以指定其他自定义数据,例如id信息,在输入框的'change''input'事件中可以通过event.detail获取。

width String|Number 'auto' 列表的宽度,默认'auto'表示和输入框元素的宽度一致。
placeholder Boolean 'auto' 是否是占位符交互模式(点击输入框值清空使用placeholder属性值代替),默认'auto'表示自动识别,规则如下:Autocomplete功能的参数值认为是false,其他功能识别为true
filter Function function (data, value) {
  /* 原封不动返回或首字符匹配过滤 */
}
对现有的数据进行重新过滤或者重新组装,只要返回需要的数组就好了,数组格式和细节和data参数一致。其中,this指当前输入框元素,支持2个参数,data为需要处理的数据(数组格式),value是输入框HTML标签转义后的值。

事件

支持的事件类型包括下面这些:

事件类型 释义
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。
DOMContentLoaded 当下拉列表面板元素初始化完毕的时候执行。
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