首页
> 计算机技术
> 前端开发
> jQuery
jQuery从零实现树状下拉选择框控件
原创 lihf8515于2025年03月24日 21:04发表
来源:本站 阅读:53
项目中需要用到下拉选择框,但是需要支持无限层级的树状结构,因此,产生了这篇《jQuery从零实现树状下拉选择框控件》的原创文章,原理就是使用div、ul、li等HTML标签模拟select标签。
主要功能:
1、支持无限层级的选择
2、支持禁用指定选项
3、支持回显文本是否显示全部层级路径
4、支持初始选择指定项
5、支持未选择时的信息提示
6、支持自定义ID字段和文本显示字段
7、支持手动快速搜索选择
8、支持是否仅可选择叶子节点
效果演示:
话不多说,直接上具体实现代码:
1、完整html页面文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="layui/css/layui.css" rel="stylesheet">
<link href="layui/extend/xcm/treelist.css" rel="stylesheet">
<title>树状下拉选择框</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
color: #555;
padding: 50px;
}
</style>
</head>
<body>
<button id="save" class="layui-btn">保存</button></br></br>
<div id="typeid" name="typeid" style="width: 20em;height:500px;"></div>
<script src="layui/layui.js"></script>
<script>
layui.config({
base: 'layui/extend/'
}).extend({
xcm: 'xcm/xcm'
});
layui.use(['xcm'], function(){
var layer = layui.layer,form = layui.form,$ = layui.$,xcm=layui.xcm;
var tree = [
{
"id":1,
"pid":0,
"title":"计算机技术",
"children":[
{
"id":2,
"pid":1,
"title":"前端开发",
"children":[
{
"id":4,
"pid":2,
"title":"Html\/Css",
},
{
"id":5,
"pid":2,
"title":"JavaScript",
},
{
"id":6,
"pid":2,
"title":"jQuery",
}
]
},
{
"id":3,
"pid":1,
"title":"后端开发",
"children":[
{
"id":7,
"pid":3,
"title":"C\/C++",
},
{
"id":8,
"pid":3,
"title":"Java",
},
{
"id":9,
"pid":3,
"title":"PHP",
}
]
}]
}];
//初始化显示列表框控件
xcm.treelist({
tree_elem: '#typeid',//树状下拉列表框容器元素id
data: tree,//要显示的具有children节点树状结构数组
placeholder: '根类别',//选择提示信息
idFieldName: 'id',//id字段的名称,用于真实保存到数据库中的值
titleFieldName: 'title',//title字段的名称,用于界面显示
readonly: false,//true只读模式,不能手动搜索;false可以手动搜索
showFullPath: true,//是否显示完整的树路径
onlySelectLeafNode: false,//仅选叶子结点
disabledId: 3,//在列表框中要禁用的项id,它和它的子项都会被禁止选择
selectedId: 5,//在列表框中默认选中的项id
selected: function (res) {//选择后的回调
console.log(res.text +':'+ res.value)
}
});
$('#save').click(function(e) {
if($('#typeid').attr('value') === undefined){
layer.msg('请选择分类');
return;
}
layer.msg($('#typeid').attr('text') + $('#typeid').attr('value'));
});
});
</script>
</body>
</html>
2、完整layui自定义扩展插件源码xcm.js
layui.define(['layer','form', 'element'], function(exports){
var form = layui.form, $ = layui.$, element = layui.element;
var objList = {
treelist:function(object){ //树状下拉列表框插件
//重复某个字符串n次
function repeat(str , n){
return new Array(n+1).join(str);
}
//递归生成下拉列表
function getSubTree(treeArr, tree_level, is_disabled=false){
var strArr = [];
var disabled = false;//默认不禁用
for(var i = 0, len = treeArr.length; i < len; i++) { //遍历项
strArr.push('<li class="item');
if(treeArr[i][object.idFieldName] == object.selectedId){//给默认选择项添加选中样式
strArr.push(' selected');
}
if(is_disabled || treeArr[i][object.idFieldName] == object.disabledId){//是禁用项,它及它的子项都不能被选择
strArr.push(' disabled');
disabled = true;//标志着它及子项都要禁用
}else{//不是禁用项
if(treeArr[i].hasOwnProperty("children") && object.onlySelectLeafNode){//不是叶子节点,并且仅能选中叶子节点,则将其禁用
strArr.push(' disabled');
}
disabled = false;//标志着子项都不需要禁用
}
strArr.push(`" data-option="${treeArr[i][object.idFieldName]}">`);
strArr.push(repeat(" ",tree_level));
if(treeArr[i].hasOwnProperty("children")){
strArr.push('<span class="xcm-expand">▼</span>');
}
strArr.push(`<span class="item-text">${treeArr[i][object.titleFieldName]}</span></li>`);
if(treeArr[i].hasOwnProperty("children")){ //如果有子项,则遍历子项
var new_tree_level = tree_level +1;
strArr.push('<ul class="xcm-select-option">');
strArr.push(getSubTree(treeArr[i].children, new_tree_level, disabled));
strArr.push('</ul>');
}
}
return strArr.join("");
}
function getTree(){
var strArr = [];
strArr.push('<div class="xcm-select-box">');
strArr.push(`<input type="text" class="xcm-select-input" placeholder="${object.placeholder}" value="" data-val=""`);
if(object.readonly){
strArr.push(' readonly />');
}else{
strArr.push(' />');
}
strArr.push('<b class="xcm-arrow-down"></b>');
strArr.push('<ul class="xcm-select-option xcm-gray-border">');
strArr.push(getSubTree(object.data, 0));
strArr.push('</ul></div>');
return strArr.join("");
}
//模拟树状下拉列表select
function selectModel() {
var $box = $('div.xcm-select-box');
var $option = $('ul.xcm-select-option', $box);
var $input = $('input.xcm-select-input', $box);
var speed = 10;
var $arrow = $('b.xcm-arrow-down', $box);
//初始显示框的高度
var $popup_border = $('ul.xcm-select-option.xcm-gray-border', $box);
$popup_border.css('height', object.height);
// 点击小三角,显示下拉列表时保持原样
$arrow.click(function(e){
$(this).siblings('ul.xcm-select-option').slideToggle(speed, function() {//显示隐藏最外显示框
if($arrow.hasClass('xcm-arrow-down')){//如果是向下三角,说明没显示,则显示
$arrow.addClass('xcm-arrow-up').removeClass('xcm-arrow-down');//添加向上三角样式并移除向下三角样式
$option.find('li.item').each(function(index,element){//遍历所有项,显示时保持原样
var $expand = $('span.xcm-expand', element);
if($(this).hasClass('selected')){//有默认选中项,则显示其所有直接父元素
$(this).parents('ul.xcm-select-option').slideDown(speed, function(){});
}else if($expand.length > 0){//有子元素
if ($expand.text() === '▲') {//是展开状态则显示子元素
$(this).next('ul.xcm-select-option').slideDown(speed, function(){});
}
}
});
}else{
$arrow.addClass('xcm-arrow-down').removeClass('xcm-arrow-up');//添加向下三角样式并移除向上三角样式
$option.not($(this).siblings('ul.xcm-select-option')).slideUp(speed, function () { });//除最外显示框都折叠
}
});
return false;
});
// 点击输入框,显示下拉列表时保持原样
$input.click(function(e) {
$(this).siblings('ul.xcm-select-option').slideToggle(speed, function() {//显示隐藏最外显示框
if($arrow.hasClass('xcm-arrow-down')){//如果是向下三角,说明没显示,则显示
$arrow.addClass('xcm-arrow-up').removeClass('xcm-arrow-down');//添加向一三角样式并移除向下三角样式
$option.find('li.item').each(function(index,element){//遍历所有项,显示时保持原样
var $expand = $('span.xcm-expand', element);
if($(this).hasClass('selected')){//有默认选中项,则显示其所有直接父元素
$(this).parents('ul.xcm-select-option').slideDown(speed, function(){});
}else if($expand.length > 0){//有子元素
if ($expand.text() === '▲') {//是展开状态则显示子元素
$(this).next('ul.xcm-select-option').slideDown(speed, function(){});
}
}
});
}else{
$arrow.addClass('xcm-arrow-down').removeClass('xcm-arrow-up');//添加向下三角样式并移除向上三角样式
$option.not($(this).siblings('ul.xcm-select-option')).slideUp(speed, function () { });//除最外显示框都折叠
}
});
return false;
});
// 选择项的处理
$option.find('li.item').each(function(index,element){
//初始化默认选中项
if($(this).hasClass('selected')){
if(object.showFullPath){//如果显示全路径
var path_str = $(this).children('span.item-text').text();//当前选择项的文本
var all_parent_ul = $(this).parents('ul.xcm-select-option');//当前选项项的所有父元素
all_parent_ul.each(function(index, e) {//遍历所有父元素
var prev_li = $(e).prev('li.item');//找到其上一个同级li元素
if(prev_li.length > 0){//元素存在
path_str = $(prev_li).children('span.item-text').text() + '/' + path_str;//将元素文本拼接到路径串前面
}
});
$input.val(path_str);//给输入框赋值,这里是选择后用于显示的文本
}else{
$input.val($(this).children('span.item-text').text());//给输入框赋值,这里是选择后用于显示的文本
}
$input.attr('data-val', $(this).attr('data-option'));//给输入框赋值,这里是选择后的真实值
let ret_obj = new Object();
ret_obj.text = $input.val();
ret_obj.value = $input.attr('data-val');
//设置容器元素的text和value
$(object.tree_elem).attr('text', ret_obj.text);
$(object.tree_elem).attr('value', ret_obj.value);
}
//选项点击处理
$(this).click(function(e){
var $expand = $('span.xcm-expand', element);//取得当前点击项中的箭头图标元素
if($expand.length > 0){//如果箭头图标元素存在
if($(e.target).is($expand)){//如果点击了箭头图标元素
$(this).next('ul.xcm-select-option').slideToggle(speed, function(){//显示隐藏子元素
if ($(this).css('display') === 'none') {
$expand.text('▼');
} else {
$expand.text('▲');
}
});
}else{//如果没点击箭头图标元素,则点击了项
if(!$(this).hasClass('disabled')){//不是禁用项则执行选中动作,否则它及它的子项都不能被选择
if(!object.onlySelectLeafNode){//由于当前不是叶子节点,所以只有在onlySelectLeafNode为false时可选中
$('li.item', $box).removeClass('selected');//清除所有项的selected样式
$(this).addClass('selected');//当前选项项添加selected样式
$arrow.addClass('xcm-arrow-down').removeClass('xcm-arrow-up');//添加向下三角样式并移除向上三角样式
$option.slideUp(speed, function() { });//隐藏所有的显示
//处理是否显示全路径
if(object.showFullPath){//如果显示全路径
var path_str = $(this).children('span.item-text').text();//当前选择项的文本
var all_parent_ul = $(this).parents('ul.xcm-select-option');//当前选项项的所有父元素
all_parent_ul.each(function(index, e) {//遍历所有父元素
var prev_li = $(e).prev('li.item');//找到其上一个同级li元素
if(prev_li.length > 0){//元素存在
path_str = $(prev_li).children('span.item-text').text() + '/' + path_str;//将元素文本拼接到路径串前面
}
});
$input.val(path_str);//给输入框赋值,这里是选择后用于显示的文本
}else{
$input.val($(this).children('span.item-text').text());//给输入框赋值,这里是选择后用于显示的文本
}
$input.attr('data-val', $(this).attr('data-option'));//给输入框赋值,这里是选择后的真实值
//选择后的返回值
let ret_obj = new Object();
ret_obj.text = $input.val();
ret_obj.value = $input.attr('data-val');
object.selected(ret_obj);//调用用户回调函数,将当前选择项传入
//设置容器元素的text和value
$(object.tree_elem).attr('text', ret_obj.text);
$(object.tree_elem).attr('value', ret_obj.value);
}
}
}
}else{//如果箭头图标元素不存在,则为点击了项
if(!$(this).hasClass('disabled')){//不是禁用项则执行选中动作,否则它及它的子项都不能被选择
$('li.item', $box).removeClass('selected');//清除所有项的selected样式
$(this).addClass('selected');//当前选项项添加selected样式
$arrow.addClass('xcm-arrow-down').removeClass('xcm-arrow-up');//添加向下三角样式并移除向上三角样式
$option.slideUp(speed, function() { });//隐藏所有的显示
$//处理是否显示全路径
if(object.showFullPath){//如果显示全路径
var path_str = $(this).children('span.item-text').text();//当前选择项的文本
var all_parent_ul = $(this).parents('ul.xcm-select-option');//当前选项项的所有父元素
all_parent_ul.each(function(index, e) {//遍历所有父元素
var prev_li = $(e).prev('li.item');//找到其上一个同级li元素
if(prev_li.length > 0){//元素存在
path_str = $(prev_li).children('span.item-text').text() + '/' + path_str;//将元素文本拼接到路径串前面
}
});
$input.val(path_str);//给输入框赋值,这里是选择后用于显示的文本
}else{
$input.val($(this).children('span.item-text').text());//给输入框赋值,这里是选择后用于显示的文本
}
$input.attr('data-val', $(this).attr('data-option'));//给输入框赋值,这里是选择后的真实值
//选择后的返回值
let ret_obj = new Object();
ret_obj.text = $input.val();
ret_obj.value = $input.attr('data-val');
object.selected(ret_obj);//调用用户回调函数,将当前选择项传入
//设置容器元素的text和value
$(object.tree_elem).attr('text', ret_obj.text);
$(object.tree_elem).attr('value', ret_obj.value);
}
}
return false;
});
});
//点击文档,隐藏所有下拉
$(document).click(function(e) {
$arrow.addClass('xcm-arrow-down').removeClass('xcm-arrow-up');//添加向下三角样式并移除向上三角样式
$option.slideUp(speed, function() { });//隐藏所有的显示
});
//手动搜索
if(!object.readonly){
search = function(obj) {
var value = $(obj).val().toLowerCase();
$option.find('ul.xcm-select-option').slideUp(speed, function(){});//所有ul项都隐藏
$option.find('li.item').slideUp(speed, function(){});//所有li项都隐藏
$option.find('li.item').each(function(index,element){//遍历所有li项,将符合查找要求的项显示出来
var item_text = $('span.item-text', element).text();
if(item_text.toLowerCase().includes(value)){//当前项包括查找的内容
$(element).slideDown(speed, function(){});//当前项显示
$(element).parents('ul.xcm-select-option').slideDown(speed, function(){});//当前项所有ul父项显示
}
});
}
$input.on('keyup', function() {
search(this);
});
}
}
//处理相关
object.placeholder ??= '请选择';//选择提示信息
object.idFieldName ??= 'id';//id字段的名称,用于真实保存到数据库中的值
object.titleFieldName ??= 'title';//title字段的名称,用于界面显示
object.readonly ??= true;//true只读模式,不能手动搜索;false可以手动搜索
object.showFullPath ??= false;//true显示完整的树路径;false不显示完整树路径
object.onlySelectLeafNode ??= false;//true仅选叶子结点;false可选所有结点
object.disabledId ??= 0;//在列表框中要禁用的项id,它和它的子项都会被禁止选择
object.selectedId ??= 0;//在列表框中默认选中的项id
object.height ??= '200px';//弹出框高度
var select = getTree();//生成select
$(object.tree_elem).append(select);
selectModel();
};
exports('xcm',objList);
});
3、用到的css文件
.xcm-select-box ul {
list-style: none;
}
.xcm-select-box {
position: relative;
}
.xcm-gray-border{
border: 1px solid #ccc;
box-shadow:0px 0px 10px #ccc;
overflow:auto;
height: 100%;
position: absolute;
z-index: 99999999;
}
.xcm-select-input {
width: 100%;
height: 30px;
border:solid 1px;
border-color: #ccc;
border-radius: 5px;
text-indent: 5px;
}
.xcm-arrow-down{
position: absolute;
top:1em;
right:5px;
border-width: 6px;
border-style: solid;
border-color: #000 transparent transparent transparent;
}
.xcm-arrow-up{
position: absolute;
top:0.5em;
right:5px;
border-width: 6px;
border-style: solid;
border-color: transparent transparent #000 transparent;
}
.xcm-select-option {
display: none;
background: #fff;
width: 100%;
overflow:auto;
}
.xcm-select-option li.item {
height: 30px;
line-height: 30px;
color: #000;
cursor: pointer;
}
.xcm-select-option li.item.selected {
background: #1E9FFF;
color: #fff;
}
.xcm-select-option li.item.disabled {
opacity: 0.5;
cursor:not-allowed;
}
.xcm-expand{
cursor: default;
}
阅读排行榜