layui-select 或其他选择器组件的滚动显示异常解决方案

693 阅读2分钟

在维护旧项目时,使用了我认为是“上古年代”的 layui组件

大概在这样的场景:在一个有最大高度限制的容器里有很多表单项,表单项中使用了 layui 的 表单select下拉框(表单自动初始化)。

问题来了

问题来了,点击下拉框出现时,选择项下拉框居然被覆盖在下方,观察到右侧出现滚动条。

大概分析了一下原因,下拉框元素是绝对定位于其下拉框包裹器父元素的;但是由于最外层的容易设置了最大高度滚动,因此下拉框的整体高度依然会被动态计算。下方的高度不够,就会导致整体被撑开;虽然并不影响功能,但是却会对用户体验和产品质量造成较大影响。

image.png

解决思路

按照 Ant Design of Vue - Select选择器 的设计思路,下拉框根据底部可视距离,自动调整位置。

Antd的选择器组件默认挂在在 document.body 上,可以根据需要调整。官方的描述是 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位

回到 layui 中,包括其他组件库的选择器组件,比较少有自定义挂载节点的选项,如果手动修改节点的挂载位置,很可能会造成意想不到的bug。比如与原有的处理判断逻辑冲突,而且操作重新构造DOM相对繁琐。故考虑调整下拉框显示位置,个人觉得这种设计也比较合理和人性化。

image.png

代码示例

<div style="width: 400px">
    <form class="layui-form" action="" lay-filter="form-custom">
        <div class="scroll-div" style="max-height: 100px">
            <div class="layui-form-item">
                <label class="layui-form-label">水果名称</label>
                <div class="layui-input-block">
                    <select name="fruit" lay-filter="fruit">
                        <option value="">请选择</option>
                        <option value="1">苹果</option>
                        <option value="2">香蕉</option>
                        <option value="3">草莓</option>
                    </select>
                </div>
            </div>
        </div>
    </form>
</div>
$('.scroll-div .layui-form-select').on('click',function (e) {
    // 获取点击元素
    const current = e.currentTarget
    // 获取滚动 wrapper
    const wrapper = document.querySelector('.scroll-div')
    // 判断处于选中状态
    if (current.classList.contains('layui-form-selected')){
        // 获取下拉列表元素
        const selectList = current.children[1]
        // 获取下拉列表元素高度
        const selectHeight = selectList.getBoundingClientRect().height
        // 获取各自与可视距离底部的距离
        const select_bottom = current.getBoundingClientRect().bottom
        const scroll_bottom = wrapper.getBoundingClientRect().bottom
        // 判断距离差是否可以容纳下下拉菜单元素,如果不能就置于选择框顶部;如果可以放置于原位(假设下拉框高度为42px)
        if (scroll_bottom - select_bottom < selectHeight){
            selectList.style.top = - selectHeight + 'px'
        }else{
            selectList.style.top = '42px'
        }
    }
})

附:

🔗 Codesandbox示例链接