LuLu UI pure版中文文档 » Drop下拉

Fork Me

Drop下拉

安装与调用

引用下面CSS:

<link rel="stylesheet" href="https://qidian.gtimg.com/lulu/edge/css/common/ui/Drop.css">

JS为:

<script type="module" src="https://qidian.gtimg.com/lulu/edge/js/common/ui/Drop.js"></script>

或者:

<script type="module">
    import Drop from "https://qidian.gtimg.com/lulu/edge/js/common/ui/Drop.js";
</script>

概述

Drop组件支持3种使用方式,2个扩展子组件。

3种使用方式分别是:

  1. 使用自定义元素<ui-drop>(推荐);
  2. 普通元素上设置is-drop属性;
  3. 传统的new Drop()构造语法。

2个扩展子组件分别是:

  1. 下拉列表;
  2. 下拉面板;

下面具体介绍Drop组件的使用。

<ui-drop>自定义元素

1. open属性变化与相邻元素的显示

<ui-drop>自定义元素默认自带'click'事件,会根据点击的是否是当前元素切换open属性,因此,<ui-drop>自定义元素即使不设置任何参数,也是可以实现一些基本的交互效果的。

例如下面这段HTML代码,无需任何额外的JavaScript代码就可以实现相邻<div>元素的下拉效果:

<ui-drop><button class="ui-button" data-type="primary">点击我</button></ui-drop>
<div class="ui-drop-target"><img src="1.png"></div>

其中,类名'ui-drop-target'是Drop组件内置的类名,当位于<ui-drop>元素后面的时候,可以自动根据<ui-drop>元素的open状态显示与隐藏。

测试:

当然,开发者可以自定义显示与隐藏的实现,例如:

<ui-drop><button class="ui-button" data-type="primary">点击我</button></ui-drop>
<div class="some-target"><img src="1.png"></div>
:not([open]) + .some-target {
    display: none;
}
测试:

2. 指定目标元素

<ui-drop>可以通过设置'target'属性指定需要下拉显示的元素,例如:

<ui-drop target="t1"><a href="javascript:">click我</a></ui-drop>
<div id="t1" class="ui-droplist-x" hidden>
    <button>测试按钮</button>
    <p><a href="javascript:">测试文字</a></p>
</div>

对应的效果为:click我


支持多个元素指向同一个下拉元素,例如下面这个同样指向't1'元素的按钮:

<ui-drop target="t1" class="ui-button" data-type="primary">click我</ui-drop>
效果测试:click我

3. 指定交互事件类型

Drop组件除了支持默认是'click'点击事件,还支持'hover'鼠标悬停事件以及'null'无绑定事件。

事件的类型通过eventType属性(大小写任意)设置。

例如这个鼠标hover事件案例:hover我

相关代码如下所示:

<ui-drop target="t2" eventtype="hover" class="blue">hover我</ui-drop></p>
<div id="t2" class="ui-droplist-x" hidden>
    <a href="javascript:" class="ui-droplist-li">示意内容</a>
</div>

如果事件类型使用的是'null',则下拉元素的显示与隐藏都需要开发者自己手动触发,例如:

点击显示

相关代码如下所示,通过改变<ui-drop>元素的open属性值来改变下拉元素的显示与隐藏状态。

<a href="javascript:" onclick="this.firstElementChild.open=true">
    <ui-drop target="t3" eventtype="null"点击显示</ui-drop>
</a>
<div id="t3" class="ui-droplist-x" hidden>
    <button onclick="document.querySelector('[target=t3]').open=false;">点击我隐藏</button>
</div>

注意,上面橙色高亮的onclick=...代码只是为了演示方便才直接写在HTML上的,实际开发不推荐这么实现,不利于日后的维护。

4. 显隐控制与对应的事件触发

下拉元素显示与隐藏的控制包括下面这些方法:

// 显示,二选一
drop.open = true;
drop.show();

// 隐藏,二选一
drop.open = false;
drop.hide();

open属性控制下拉元素的显隐状态上面的例子已经演示过了,这里演示下<ui-drop>元素的show()hide()方法。

请点击后面2个按钮

代码如下所示:

<ui-drop id="drop4" target="t4" class="ui-button" data-type="primary">请点击后面2个按钮</ui-drop>

<button id="dropShow" class="ui-button" data-type="warning">点击执行show()</button>
<button id="dropHide" class="ui-button" data-type="danger">点击执行hide()</button>
dropShow.addEventListener('click', function (event) {
    // 阻止冒泡
    event.stopPropagation();
    // 安心显示
    drop4.show();
});
dropHide.addEventListener('click', function (event) {
    // 阻止冒泡
    event.stopPropagation();
    // 安心隐藏
    drop4.hide();
});

如果希望在下拉元素显示和隐藏的时候做些事情,可以使用自定义的show和hide事件,代码示意如下所示:

drop4.addEventListener('show', () => {
    console.log('下拉显示啦~');
});
drop4.addEventListener('hide', () => {
    console.log('下拉隐藏了~');
});

如果希望默认下拉就显示,只需要设置<ui-drop>元素的open属性值为任意字符串。

例如:

<ui-drop target="t5" class="ui-button" data-type="primary" position="2-1" open>

效果如下(任意点击操作会触发隐藏,因此如果浮层不可见可以刷新一下页面)

默认显示

is-drop快捷语法

本Drop组件支持在原生HTML元素上添加is-drop属性实现下拉效果。

需要注意的是,此语法不支持open属性,也不包含show()hide()方法;以及JS设置is-drop属性是不会产生任何变化的(权衡利弊后的策略,这种场景请使用new Drop()构造方法)。

使用示意(下面2种用法效果一致):

<a href="javascript:" is-drop="t2">click我</a>
<a href="javascript:" data-target="t2" is-drop>click我</a>
click我click我

is-drop快捷语法支持自定义其他参数,包括交互事件类型,定位的方位以及偏移大小等,均使用data-*语法进行设置。例如:

<a href data-eventtype="hover" data-position="4-1" is-drop="t2">hover我</a>

效果测试:hover我


下面测试下直接append一段包含is-drop设置的HTML代码,看看下拉效果是不是正常出现。

相关JS代码如下所示:

button.addEventListener('click', function (event) {
    this.parentElement.insertAdjacentHTML('afterend', '<p><a href is-drop="t2">插入内容,点击我</a></p>');
});

new Drop()构造语法

new Drop()构造语法用来给普通的HTML元素绑定下拉交互行为。

new Drop()执行的时候会创建<ui-drop>自定义元素并返回,和new Dialog()不同,new Drop()方法创建的元素不会插入到页面上下文中,只会存在内存中。

例如:

let eleDrop = new Drop();

此时eleDrop就是一个存在内存中的<ui-drop>元素。

因此,<ui-drop>元素包含的属性和方法均可以在eleDrop对象中使用。

例如执行eleDrop.hide()可以隐藏下拉元素。

1. 基本使用演示

相关代码如下:

<button id="b2" class="ui-button" data-type="primary">执行new Drop('#b2', '#t2')</button>
new Drop('#b2', '#t2');
演示兼测试:

2. 常见参数使用示意

new Drop()构造语法支持DOM元素上设置参数,以及通过JS传参这两种方法,例如:

<button id="b3" class="ui-button" data-type="primary" data-position="5-7">传参测试</button>
// 传参使用演示
let d3 = new Drop('#b3', t3, {
    // 等同于d3.addEventListener('show', ...)
    onShow: function () {
        // this就是这里的d3
        this.element.trigger.dataset.type = 'warning';
    }
});
d3.addEventListener('hide', function () {
    this.element.trigger.dataset.type = 'primary';
});
测试:

3. 事件的委托

new Drop()构造语法支持一个特有的selector参数,可以让trigger元素的某些子元素作为下拉元素显示的事件源,适用于下拉触发元素是动态生成的场景。

例如:

<p id="freshBtn"><a>内容</a></p>
<img id="i1" src="9.png">
// 点击<a>元素显示下拉
new Drop('#freshBtn', i1, {
    selector: 'a'
});

内容

上面链接HTML刷新


下面再看下hover事件中动态trigger元素的表现。

  1. 内容a
  2. 内容纯文本
  3. 内容a
  4. 内容纯文本
  5. 内容a
  6. 内容纯文本
  7. 内容a
  8. 内容纯文本

上面链接HTML刷新

相关的代码如下:

// hover委托
new Drop('#freshBtn2', i1.cloneNode(), {
    eventType: 'hover',
    selector: 'a',
    edgeAdjust: false
});

Drop语法和参数

语法为:

let eleDrop = new Drop(trigger, target, options);

其中:

trigger
可选。Object元素或String选择器。触发Drop浮层显示的DOM元素,如果缺省,表示触发元素是eleDrop自身。
target
可选。Object元素或String选择器。Drop浮层元素。如果triggertarget只设置了其中一个,则这个参数认为是trigger
options
可选。Object对象。可选参数见下表(红色星号参数是new Drop()构造语法特有参数):
参数名称 支持类型 默认值 释义
eventType String 'click' 表示触发元素显示的事件,默认是'click', 表示点击事件。'hover'表示hover时候显示下拉;'null'表示没有任何事件,需要手动调用show()方法才显示,隐藏也需要自己调用hide()方法实现。
selector* String '' click或hover的委托处理需要的选择器。如果参数值不为空,则trigger认为是容器。
offsets Object {
  x: 0,
  y: 0
}
表示浮动元素的偏移大小,相对于右上角偏移,数值越大右上角偏移越大。
position String '7-5' 表示触发元素和浮层元素的定位关系。基于'trigger-target'规则,数字表示对应元素的位置,具体如下图。例如'7-5'就表示触发元素的位置7个浮层元素的5对齐。

edgeAdjust Boolean true 表示浮层屏幕之外的时候,是否自动调整方向。默认是自动调整。
onShow* Function function () {} 浮层显示时候的回调方法。函数的上下文this为当前eleDrop元素,支持1个参数,为事件对象。
onHide* Function function () {} 浮层隐藏时候的回调方法。函数的上下文this为当前eleDrop元素,支持1个参数,为事件对象。

在DOM元素上设置参数

上表所有参数均支持直接在DOM元素上设置,其中onShowonHide参数对应的效果需要通过addEventListener方法进行设置,例如:

trigger.addEventListener('show', function () {});
trigger.addEventListener('hide', function () {});

其余所有参数均采用data-*的自定义参数的形式进行设置,注意保持小写,例如:

<a data-offsets="10, 10" data-eventtype="hover" data-edge-adjust="false" data-target="i1" is-drop>经过我</a>
经过我

对于<ui-drop>元素,则无需使用data-*,直接设置对应参数即可,且不区分大小写,例如:

<ui-drop offsets="10, 10" eventType="hover" edgeAdjust="false" target="i1">经过我</ui-drop>
经过我

Drop拓展的List方法

1. 基本效果演示与代码

跳转到 所有评论 未处理

// 下拉导航链接列表
new Drop('.dropPageEvery').list([{
    href: 'http://www.qidian.com/',
    value: '起点中文网',
    target: '_lulu'
}, {
    href: 'http://www.hongxiu.com/',
    value: '红袖添香',
    target: '_lulu'
}, {
    href: 'http://www.xs8.cn/',
    value: '言情小说吧',
    target: '_lulu',
    disabled: true
}, {
    value: '其他',
    data: [{
        href: 'http://www.qidian.com/',
        value: '1-起点中文网',
        target: '_lulu'
    }, {
        href: 'http://www.hongxiu.com/',
        value: '1-红袖添香',
        target: '_lulu'
    }, {
        value: '1-其他',
        data: [{
            href: 'http://www.qidian.com/',
            value: '2-起点中文网',
            target: '_lulu'
        }, {
            href: 'http://www.hongxiu.com/',
            value: '2-红袖添香',
            target: '_lulu'
        }]
    }]
}], {
    width: 100
});
// 评论筛选列表
new Drop(document.querySelector('#dropComment')).list([{
    id: 1,
    value: '所有评论'
}, {
    id: 2,
    value: '未通过评论'
}, {
    id: 3,
    value: '未审核评论'
}, {
    id: 4,
    value: '已通过评论'
}], {
    onSelect: function (obj) {
        console.log('选中的评论id是:' + obj.id);
    }
});
// 评论状态修改列表
new Drop('#dropDeal').list([{
    value: '<i class="icon_undeal"></i>未处理'
}, {
    value: '<i class="icon_pass"></i>已通过'
}, {
    value: '<i class="icon_unpass"></i>未通过'
}], {
    position: '7-5'
});

2. 动态列表数据+列表选择不会改变trigger元素

列表默认选中后会改变点击元素里面的内容,如果希望列表选择后按钮的内容不发生变化,可以设置selector参数的值为和当前点击元素的匹配的选择器即可。

// 基于数据的列表
new Drop(trigger).list(function () {
    return [...select.querySelectorAll('option')].map(function (option) {
        return {
            id: option.value,
            value: option.innerHTML,
            disabled: option.selected
        };
    });
}, {
    selector: 'button',
    onSelect: function (obj) {
        eleSelDropList.querySelector('option[value="' + obj.id + '"]').selected = true;
    }
});

3. 右键上下文菜单+多级数据结构

右键下面的图片:

示意图片

完整JS代码:

// 右键处理
new Drop('#menuImg').list([{
    id: '0',
    value: '居左显示'
}, {
    id: '1',
    value: '居中显示'
}, {
    id: '2',
    value: '居右显示'
}, '-', {
    id: '3',
    value: '应用滤镜',
    data: [{
        id: '3-1',
        value: '灰度'
    }, {
        id: '3-2',
        value: '模糊',
        data: [{
            id: '3-2-1',
            value: '0px'
        }, {
            id: '3-2-2',
            value: '5px'
        }, {
            id: '3-2-3',
            value: '10px',
            disabled: true
        }, {
            id: '3-2-3',
            value: '15px'
        }]
    }, {
        id: '3-3',
        value: '褐色'
    }]
}], {
    eventType: 'contextmenu',
    offsets: {
        x: 8,
        y: 8
    },
    onSelect: function (obj) {
        var eleImg = this.element.trigger;

        switch (obj.id) {
            case '0': {
                eleImg.style.display = 'block';
                eleImg.style.marginLeft = '';
                eleImg.style.marginRight = 'auto';
                break;
            }
            case '1': {
                eleImg.style.display = 'block';
                eleImg.style.marginLeft = 'auto';
                eleImg.style.marginRight = 'auto';
                break;
            }
            case '2': {
                eleImg.style.display = 'block';
                eleImg.style.marginLeft = 'auto';
                eleImg.style.marginRight = '';
                break;
            }
            case '3-1': {
                eleImg.style.filter = 'grayscale(100%)';
                break;
            }
            case '3-2-1': {
                eleImg.style.filter = 'blur(0)';
                break;
            }
            case '3-2-2': {
                eleImg.style.filter = 'blur(5px)';
                break;
            }
            case '3-2-4': {
                eleImg.style.filter = 'blur(15px)';
                break;
            }
            case '3-3': {
                eleImg.style.filter = 'sepia(100%)';
                break;
            }
        }
    }
});

不使用new Drop()语法实现右键菜单效果,效果示意。

代码如下:

<button is-drop data-eventtype="contextmenu" data-target="fd">右键我</button></p>
<datalist id="fd">
    <option value="1">删除关键帧</option>
    <option value="2">新建关键帧</option>
    <option value="3">移动关键帧</option>
</datalist>

注意,这种实现不支持嵌套数据结构。

如果想要知道右键选择的是哪一项,可以使用自定义的'select'事件完成,例如:

button.addEventListener('select', event => {
    // event.detail.data包含选择的列表项数据
    console.log(event.detail.data);
});

4. <datalist>元素实现下拉列表

可以指向一个<datalist>元素实现。代码示意:

<ui-drop class="ui-button" data-type="primary" target="t6">点击我</ui-drop>
<datalist id="t6">
    <option value="1">列表1</option>
    <option value="2">列表2</option>
    <!-- 空option当作分隔线 -->
    <option></option>
    <option value="3">其他1</option>
    <option value="4">其他2</option>
    <option value="5"><mark>标记</mark></option>
</datalist>

点击我

也可以使用list()方法设置,例如有个按钮是:

<ui-drop id="d4" class="ui-button" data-type="primary">点击我</ui-drop>

则使用下面的JS代码就可以有下拉列表效果了:

d4.list([{
    id: 1,
    value: '列表1'
}, {
    id: 2,
    value: '列表2'
}]);

测试:点击我

5. 自定义click事件+异步处理显示

使用一个200毫秒定时器模拟异步请求处理。

<button id="dl5" class="ui-button" data-type="primary">点击我</button>
let drop5 = new Drop(dl5).list({
    eventType: 'custom'
});

dl5.addEventListener('click', function () {
    this.loading = true;
    setTimeout(() => {
        drop5.data = ['项目1', '项目2'];
        drop5.show();

        this.loading = false;
    }, 200);
});

List方法语法和参数

let eleDrop = new Drop(trigger).list(data, options);

或者

let eleDrop = new Drop().list(trigger, data, options);

其中:

trigger
必需。Object DOM对象或String选择器字符串。表示按钮或者委托容器元素(如果可选selector参数不为空)。
data
必需。Array数组或者Function函数或者<datalist>元素。如果是Function类型,则执行后需要返回数组。数组项都是纯对象,列表呈现需要的是value, selected, disabled等属性,如果有href属性则表示链接,其他属性为自定义使用, 例如:
[{
    id: 1,
    value: '所有评论',
    selected: true
}, {
    id: 2,
    value: '未审核评论',
    disabled: true
}, {
    id: 3,
    value: '链接',
    href: '//l-ui.com',
    target: '_blank'
}]
红色的关键字是组件可识别的关键属性,其余属性(蓝色)可以在onSelect回调中为己所用。
可使用data属性嵌套多层级列表,例如:
[{
    value: '多级列表',
    data: [{
        value: '二级列表1'
    }, {
        value: '二级列表2'
    }]
}]
可以使用字符串'-',或者null或者{}表示分隔线。例如:
[{
    id: '0',
    value: '居左显示'
}, {
    id: '1',
    value: '居中显示'
}, {
    id: '2',
    value: '居右显示'
}, {}, {
    id: '3',
    value: '应用滤镜'
}]
options
可选。Object纯对象。可选参数,具体见下表:
参数名称 支持类型 默认值 释义
eventType String 'click' 表示触发元素显示的事件,默认是'click', click时候显示下拉列表。还可以是'null'表示无事件,下拉浮层直接显示;'hover'表示hover时候显示下拉;如果是其他字符串,需要手动调用show()方法才显示,隐藏也需要自己控制。
offsets Object {
  x: 0,
  y: 0
}
表示浮动元素的偏移大小,相对于右上角偏移,数值越大右上角偏移越大。
position String '4-1' 表示触发元素和浮层元素的定位关系。具体参见Drop方法中的position参数。

selector String '' 同Drop的selector参数,委托用。
width String
Number
'' 下拉列表的宽度,默认CSS中是111像素宽度。
onShow Function function () {} 浮层显示时候的回调方法。函数的上下文this为当前eleDrop元素,支持两个参数,第一个参数为触发元素,第二个参数为浮层元素。
onHide Function function () {} 浮层隐藏时候的回调方法。函数的上下文this为当前eleDrop元素,支持两个参数,第一个参数为触发元素,第二个参数为浮层元素。
onSelect Function function () {} 选择某一个下拉元素的时候的回调。上下文this为当前eleDrop元素。支持两个参数,第一个参数为对应的纯对象数据,如{ id: 1, value: '所有评论' };第二个参数当前选中的列表元素(data-index标记了索引值)。

Drop拓展的Panel方法

1. 基本使用演示和代码

删除评论 更多内容,更大宽度

// 轻弹框面板
var eleDropPanel = document.querySelector('#dropPanel');
new Drop(eleDropPanel).panel({
    title: '删除评论',
    content: '删除后,您的粉丝在文章中将无法看到该评论。',
    buttons: [{
        value: '删除',
        type: 'primary',
        events: function(event){
            // event.drop就是<ui-drop>元素
            event.drop.hide()
        }
    }, {}]
});
new Drop('#dropPanel2').panel({
    eventType: "hover",
    title: '删除多图文消息',
    content: '...',
    buttons: [{}],
    width: 400
});

2. <ui-drop>使用示意

可以直接在<ui-drop>元素上调用panel()方法实现轻弹框面板效果,例如:

<ui-drop id="d5" class="ui-button" data-type="primary">点击我</ui-drop>

则使用下面的JS代码就可以有下拉面板效果:

d5.panel({
    content: '你好,LuLu UI!',
});

测试:点击我

3. 轻弹框内容直接DOM显示

如果<ui-drop>元素关联的元素是<dialog>元素(切勿设置is="ui-dialog"),会认为是轻弹框提示。

测试:ui-drop元素

耶,成功显示!

相关代码如下:

<ui-drop class="ui-button" data-type="primary" target="dg">ui-drop元素</ui-drop>
<button class="ui-button" data-type="primary" data-target="dg" is-drop>ui-drop元素</button></p>
<dialog id="dg" data-buttons="删除">耶,成功显示!</dialog>

按钮文字通过data-buttons属性设置,使用逗号分隔,如果只设置第一个“确定”按钮的文字内容,无需逗号。

点击确定按钮和取消按钮会分别触发<dialog>元素的'ensure''cancel'事件。

例如,本例中,点击“删除”的事件是这么处理的。

dg.addEventListener('ensure', function (event) {
    console.log('点击了删除按钮');
    event.detail.drop.hide();
});

Panel方法语法和参数

语法如下:

let eleDrop = new Drop(trigger).panel(options);
let eleDrop = new Drop().panel(trigger, options);

其中:

trigger
必需。Object DOM元素或者String选择器字符串。表示触发轻弹框显示的元素。
options
可选。Object纯对象。可选参数具体见下表:
参数名称 支持类型 默认值 释义
title String '' 轻弹框的标题。
content String '' 轻弹框的内容。
buttons Array [{}, {}] 轻弹框的按钮。按钮规则和Dialog弹框组件按钮规则基本一致。默认第一个按钮是danger类型确认按钮,第二个按钮为白色取消按钮。不一样的地方在于事件中若想获得eleDrop元素,是通过event.drop,例如:

events: function (event) {
  // event.drop就是返回的eleDrop元素
}

eventType String 'click' 表示触发元素显示的事件,默认是'click', click时候显示下拉列表。还可以是'null'表示无事件,下拉浮层直接显示;'hover'表示hover时候显示下拉;如果是其他字符串,需要手动调用show()方法才显示,隐藏也需要自己控制。
offsets Object {
  x: 0,
  y: 0
}
表示浮动元素的偏移大小,相对于右上角偏移,数值越大右上角偏移越大。
position String '4-1' 表示触发元素和浮层元素的定位关系。具体参见Drop方法中的position参数。

width Number | String 'auto' 轻弹框的宽度。任意合法的width属性值。
onShow Function function () {} 浮层显示时候的回调方法。函数的上下文this为当前eleDrop元素,支持两个参数,第一个参数为触发元素,第二个参数为浮层元素。
onHide Function function () {} 浮层隐藏时候的回调方法。函数的上下文this为当前eleDrop元素,支持两个参数,第一个参数为触发元素,第二个参数为浮层元素。

ui-drop元素的属性和方法

eleDrop就是<ui-drop>元素,暴露了以下属性和方法:

{
    // 暴露的元素们
    element = {
        // 触发元素
        trigger: null,
        // 激活面板元素
        target: null,

        // 下面的元素是Panel扩展专有的
        panel: null,
        // 面板标题元素
        title: null,
        // 面板关闭按钮元素
        close: null,
        // 面板内容元素
        content: element,
        // 面板底部元素
        footer: element,
        // 面板第1个按钮(如果有)
        button0: element,
        // 面板第2个按钮(如果有)
        button1: element
    },
    params: {
        // 事件类型
        eventtype: "click"
        // 浮动元素的偏移大小
        offsets: {x: 0, y: 0}
        // 触发元素和浮层元素的定位关系
        position: "",
        // 委托功能选择器
        selector: "",

        // 下面的元素是Panel扩展专有的
        // 标题内容
        title: "",
        // 轻弹框的按钮
        buttons: [{…}, {…}]
        // 轻弹框的内容
        content: ""
        // 轻弹框的宽度
        width: "auto"
    },
    // 参数设置
    setParams: function (options) {},
    // 重定位
    position: function () {},
    // 显示
    show: function () {},
    // 隐藏
    hide: function () {},
    // 下拉列表
    list: function () {},
    // 轻面板
    panel: function () {}
}
本页贡献者:

zhangxinxu,nanaSun,ziven27, lennonover, wiia