需求:要在select标签实现下拉多选,搜索,限制选中,默认回显等操作,之前同事用的yselect.js,网上用的简直是寥寥无几,找了半天没找到限制选中的方法,看了源代码才发现根本没有,所以自己加上了,最终实现需求。
1.全选+搜索+默认选中前n个选项
1.1效果
1.2 引入yselect.js等(源码放在最后)
yselect.js 代码如下,建议先看源码再编写代码,有些方法都是封装了的
(function ($) { $.fn.ySelect = function (options) { var defaultOptions = { placeholder: '请选择', numDisplayed: 4, overflowText: '{n} selected', searchText: '搜索', showSearch: true } if (typeof options == 'string') { var settings = options; } else { var settings = $.extend(true, {}, defaultOptions, options); } function ySelect(select, settings) { this.$select = $(select); this.settings = settings; this.create(); } ySelect.prototype = { create: function () { var multiple = this.$select.is('[multiple]') ? ' multiple' : ''; this.$select.wrap('<div class="fs-wrap' + multiple + '"></div>'); this.$select.before('<div class="fs-label-wrap"><div class="fs-label">' + this.settings.placeholder + '</div><span class="fs-arrow"></span></div>'); this.$select.before('<div class="fs-dropdown hidden"><div class="fs-options"></div></div>'); this.$select.addClass('hidden'); this.$wrap = this.$select.closest('.fs-wrap'); this.reload(); }, reload: function () { if (this.settings.showSearch) { var search = '<div class="fs-search"><input type="search" placeholder="' + this.settings.searchText + '" /><span class="fs-selectAll"><i class="fa fa-check-square-o"></i></span></div>'; this.$wrap.find('.fs-dropdown').prepend(search); } var choices = this.buildOptions(this.$select); this.$wrap.find('.fs-options').html(choices); this.reloadDropdownLabel(); }, destroy: function () { this.$wrap.find('.fs-label-wrap').remove(); this.$wrap.find('.fs-dropdown').remove(); this.$select.unwrap().removeClass('hidden'); }, buildOptions: function ($element) { var $this = this; var choices = ''; $element.children().each(function (i, el) { var $el = $(el); if ('optgroup' == $el.prop('nodeName').toLowerCase()) { choices += '<div class="fs-optgroup">'; choices += '<div class="fs-optgroup-label">' + $el.prop('label') + '</div>'; choices += $this.buildOptions($el); choices += '</div>'; } else { var selected = $el.is('[selected]') ? ' selected' : ''; choices += '<div class="fs-option' + selected + '" data-value="' + $el.prop('value') + '"><span class="fs-checkbox"><i></i></span><div class="fs-option-label">' + $el.html() + '</div></div>'; } }); return choices; }, reloadDropdownLabel: function () { var settings = this.settings; var labelText = []; this.$wrap.find('.fs-option.selected').each(function (i, el) { labelText.push($(el).find('.fs-option-label').text()); }); if (labelText.length < 1) { labelText = settings.placeholder; } else if (labelText.length > settings.numDisplayed) { labelText = settings.overflowText.replace('{n}', labelText.length); } else { labelText = labelText.join(', '); } this.$wrap.find('.fs-label').html(labelText); this.$select.change(); }, setwrap: function () { return "123"; }, } return this.each(function () { var data = $(this).data('ySelect'); if (!data) { data = new ySelect(this, settings); $(this).data('ySelect', data); } if (typeof settings == 'string') { data[settings](); } }); } window.ySelect = { 'active': null, 'idx': -1 }; function setIndexes($wrap) { $wrap.find('.fs-option:not(.hidden)').each(function (i, el) { $(el).attr('data-index', i); $wrap.find('.fs-option').removeClass('hl'); }); $wrap.find('.fs-search input').focus(); window.ySelect.idx = -1; } function setScroll($wrap) { var $container = $wrap.find('.fs-options'); var $selected = $wrap.find('.fs-option.hl'); var itemMin = $selected.offset().top + $container.scrollTop(); var itemMax = itemMin + $selected.outerHeight(); var containerMin = $container.offset().top + $container.scrollTop(); var containerMax = containerMin + $container.outerHeight(); if (itemMax > containerMax) { var to = $container.scrollTop() + itemMax - containerMax; $container.scrollTop(to); } else if (itemMin < containerMin) { var to = $container.scrollTop() - containerMin - itemMin; $container.scrollTop(to); } } //$(document).on('click', '.fs-selectAll', function () { // $(this).parent().next().find('.fs-option.selected').click(); // $(this).parent().next().find('.fs-option').click(); // $(this).addClass('selected'); //}); //$(document).on('click', '.fs-selectAll.selected', function () { // $(this).parent().next().find('.fs-option.selected').click(); // $(this).removeClass('selected'); //}); //全选 $(document).on('click', '.fs-selectAll', function () { if ($(this).hasClass("selected")) { $(this).removeClass('selected'); $(this).parent().next().find('.fs-option').removeClass('selected'); $(this).parent().find(".fs-label").text(""); $(this).parent().next().find('.fs-option:eq(0)').addClass('selected'); $(this).parent().next().find('.fs-option:eq(0)').click(); } else { $(this).addClass('selected'); $(this).parent().next().find('.fs-option').addClass('selected'); $(this).parent().next().find('.fs-option:eq(0)').removeClass('selected'); $(this).parent().next().find('.fs-option:eq(0)').click(); } }) //获取选择项 $.fn.ySelectVal = function () { var list = []; $(this).prev().find(".fs-options>div").each(function () { if ($(this).hasClass("selected")) { list.push($(this).attr("data-value")); } }); return list; } //重新绑定数据 $.fn.ySelectReload = function () { var fs_optionsObj = $(this).prev().find(".fs-options"); fs_optionsObj.html(""); $(this).parent().find(".fs-label").text(""); var data_index = 0; $(this).prev().find('.fs-selectAll').removeClass("selected"); $(this).find("option").each(function () { var fs_options = '<div class="fs-option " data-value="' + $(this).attr("value") + '" data-index="' + data_index + '"><span class="fs-checkbox"><i></i></span><div class="fs-option-label">' + $(this).text() + '</div></div>'; fs_optionsObj.append(fs_options); data_index++; }) } //触发全选事件 $.fn.ySelectAll = function () { $(this).prev().find(".fs-selectAll").addClass('selected'); $(this).prev().find(".fs-options>div").each(function () { $(this).addClass('selected'); }); var count = $(this).prev().find(".fs-options>div").length; $(this).parent().find(".fs-label").text("已选中 " + count + "项"); //$(this).prev().find('.fs-option:eq(0)').click(); //$(this).prev().find('.fs-option:eq(0)').click(); } // 默认选中前n个选中 $.fn.ySelectDefault = function (num) { var $options = $(this).prev().find(".fs-options>div"); // 选中前n个选项 $options.slice(0, num).each(function () { $(this).addClass('selected'); }); // 更新已选中项的数量 var count = $options.filter('.selected').length; $(this).parent().find(".fs-label").text("已选中 " + count + "项"); } $(document).on('click', '.fs-option', function () { var $wrap = $(this).closest('.fs-wrap'); if ($wrap.hasClass('multiple')) { var selected = []; $(this).toggleClass('selected'); $wrap.find('.fs-option.selected').each(function (i, el) { selected.push($(el).attr('data-value')); }); } else { var selected = $(this).attr('data-value'); $wrap.find('.fs-option').removeClass('selected'); $(this).addClass('selected'); $wrap.find('.fs-dropdown').hide(); } $wrap.find('select').val(selected); $wrap.find('select').ySelect('reloadDropdownLabel'); $wrap.find('select').ySelect('setwrap'); }); $(document).on('keyup', '.fs-search input', function (e) { if (40 == e.which) { $(this).blur(); return; } var $wrap = $(this).closest('.fs-wrap'); var keywords = $(this).val(); $wrap.find('.fs-option, .fs-optgroup-label').removeClass('hidden'); if ('' != keywords) { $wrap.find('.fs-option').each(function () { var regex = new RegExp(keywords, 'gi'); if (null === $(this).find('.fs-option-label').text().match(regex)) { $(this).addClass('hidden'); } }); $wrap.find('.fs-optgroup-label').each(function () { var num_visible = $(this).closest('.fs-optgroup').find('.fs-option:not(.hidden)').length; if (num_visible < 1) { $(this).addClass('hidden'); } }); } setIndexes($wrap); }); $(document).on('click', function (e) { var $el = $(e.target); var $wrap = $el.closest('.fs-wrap'); if (0 < $wrap.length) { if ($el.hasClass('fs-label') || $el.hasClass('fs-arrow')) { window.ySelect.active = $wrap; var is_hidden = $wrap.find('.fs-dropdown').hasClass('hidden'); $('.fs-dropdown').addClass('hidden'); if (is_hidden) { $wrap.find('.fs-dropdown').removeClass('hidden'); } else { $wrap.find('.fs-dropdown').addClass('hidden'); } setIndexes($wrap); } } else { $('.fs-dropdown').addClass('hidden'); window.ySelect.active = null; } }); $(document).on('keydown', function (e) { var $wrap = window.ySelect.active; if (null === $wrap) { return; } else if (38 == e.which) { e.preventDefault(); $wrap.find('.fs-option').removeClass('hl'); if (window.ySelect.idx > 0) { window.ySelect.idx--; $wrap.find('.fs-option[data-index=' + window.ySelect.idx + ']').addClass('hl'); setScroll($wrap); } else { window.ySelect.idx = -1; $wrap.find('.fs-search input').focus(); } } else if (40 == e.which) { e.preventDefault(); var last_index = $wrap.find('.fs-option:last').attr('data-index'); if (window.ySelect.idx < parseInt(last_index)) { window.ySelect.idx++; $wrap.find('.fs-option').removeClass('hl'); $wrap.find('.fs-option[data-index=' + window.ySelect.idx + ']').addClass('hl'); setScroll($wrap); } } else if (32 == e.which || 13 == e.which) { $wrap.find('.fs-option.hl').click(); } else if (27 == e.which) { $('.fs-dropdown').addClass('hidden'); window.ySelect.active = null; } }); $.fn.ySelectedValues = function (splitString) { var result = ""; var $selects = this.find("option:selected"); for (var i = 0; i < $selects.length; i++) { result += $selects[i].value + ((i == $selects.length - 1) ? "" : splitString); } return result; } $.fn.ySelectedTexts = function (splitString) { var result = ""; var $selects = this.find("option:selected"); for (var i = 0; i < $selects.length; i++) { result += $selects[i].text + ((i == $selects.length - 1) ? "" : splitString); } return result; } } )(jQuery);
复制
还用到了font-awesome的小图标,如果不引入的话下来搜索右边的勾选图表不会显,bootstrap看个人需求引入,jquery必须引入
<script src="js/jquery-1.11.3.min.js"></script> <link href="css/bootstrap.min.css" rel="stylesheet" type="text/css" /> <link href="css/font-awesome.min.css" rel="stylesheet" type="text/css" /> <link href="css/ySelect.css" rel="stylesheet" type="text/css" /> <script src="js/bootstrap.min.js"></script> <script src="js/ySelect.js"></script>
复制
1.3主要代码讲解
必须要先使用ySelect加载之后再使用其他方法
$(".demo1").ySelect({ placeholder: "请先选择一些项目", //提示信息 searchText: "搜索", //下拉搜索占位符 showSearch: true, //显示搜索 (true/false) numDisplayed: 4, //超出数量整合 overflowText: "已选中 {n}项", //超出数量整合文本提示,{n}为数量 });
复制
默认选中前n个和默认全选调用代码
//触发全选事件 $.fn.ySelectAll = function () { $(this).prev().find(".fs-selectAll").addClass('selected'); $(this).prev().find(".fs-options>div").each(function () { $(this).addClass('selected'); }); var count = $(this).prev().find(".fs-options>div").length; $(this).parent().find(".fs-label").text("已选中 " + count + "项"); //$(this).prev().find('.fs-option:eq(0)').click(); //$(this).prev().find('.fs-option:eq(0)').click(); } // 默认选中前n个选中 $.fn.ySelectDefault = function (num) { var $options = $(this).prev().find(".fs-options>div"); // 选中前n个选项 $options.slice(0, num).each(function () { $(this).addClass('selected'); }); // 更新已选中项的数量 var count = $options.filter('.selected').length; $(this).parent().find(".fs-label").text("已选中 " + count + "项"); }
复制
调用方法如下,注意,使用了yselect后再使用 $(".demo1").val()是获取不到已选中值的,需要使用
$(".demo1").ySelectVal(),结果为['1','2']
// 默认选中前两个选项的方法 $(".demo1").ySelectDefault(2); //默认全部选中 $(".demo1").ySelectAll(); //获取选择项 $(".demo1").ySelectVal(); //重新加载 $(".demo1").ySelectReload(); //获取到已选中的值并且以逗号分隔 console.log($("#m2").ySelectedValues(",")); //获取到已选中的text并且以逗号分隔 console.log($("#m2").ySelectedTexts(","));
复制
2.限制选中个数(其他禁用)
2.1效果
注意,限制选中就不要加上全选的按钮。容易冲突
2.1代码实现
页面加载的时候也需要判断长度是否为2,如果为2就设置禁用
$("#m1").change(function () { var $select = $(this); var selectedValues = $select.ySelectVal(); // 获取当前选中的值 var $options = $select.prev().find(".fs-options>div"); // 获取所有的选项 if (selectedValues.length == 2) { console.log("如果元素的长度为2,那么其他项就不让点击"); // 禁用其他未选中的选项 $options.each(function () { var $option = $(this); if (!$option.hasClass("selected")) { $option.addClass("disabled"); // 添加禁用类 $option.off("click"); // 移除点击事件 } }); } else { console.log("选中的值小于2,允许点击其他项"); // 启用所有选项的点击事件 $options.each(function () { var $option = $(this); if ($option.hasClass("disabled")) { $option.removeClass("disabled"); // 移除禁用类 $option.on("click", function () { // 你可以在这里添加选项点击事件的逻辑 console.log("选中项:", $option.text()); }); } }); } });
复制
需要配合css实现
.fs-options .disabled { pointer-events: none; /* 禁用鼠标点击 */ opacity: 0.5; /* 改变样式使禁用项看起来不活跃 */ }
复制
3.完整代码
<!-- * @Description: * @Author:请叫我欧皇i * @Date: 2024-11-25 15:09:26 * @FilePath: \ySelect-master\index.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>jQuery ySelect Plugin Demo</title> <script src="js/jquery-1.11.3.min.js"></script> <link href="css/bootstrap.min.css" rel="stylesheet" type="text/css" /> <link href="css/font-awesome.min.css" rel="stylesheet" type="text/css" /> <link href="css/ySelect.css" rel="stylesheet" type="text/css" /> <script src="js/bootstrap.min.js"></script> <script src="js/ySelect.js"></script> <style> .container { margin-top: 50px; } .fs-options .disabled { pointer-events: none; /* 禁用鼠标点击 */ opacity: 0.5; /* 改变样式使禁用项看起来不活跃 */ } </style> </head> <body> <div class="container"> <select id="m1" class="demo" multiple="multiple"> <option value="1">加多宝冰凉茶</option> <option value="2">饮料</option> <option value="3">太太乐鸡精</option> <option value="4">嘉士伯啤酒</option> <option value="5">成功图像类型</option> <option value="6">门店类型</option> <option value="7">终端类型</option> </select> <select id="m2" class="demo1" multiple="multiple"> <option value="1">加多宝凉茶</option> <option value="2">饮料</option> <option value="3">太太乐鸡精</option> <option value="4">嘉士伯啤酒</option> <option value="5">成功图像类型</option> <option value="6">门店类型</option> </select> <br /> <!-- <button id="current" class="btn btn-primary btn-sm">查看值</button> --> </div> <script> $(function () { $("#m1").ySelect({ placeholder: "请先选择一些项目", //提示信息 searchText: "搜索", //下拉搜索占位符 showSearch: false, //显示搜索 (true/false) numDisplayed: 2, //超出数量整合 overflowText: "已选中 {n}项", //超出数量整合文本提示,{n}为数量 }); $(".demo1").ySelect({ placeholder: "请先选择一些项目", //提示信息 searchText: "搜索", //下拉搜索占位符 showSearch: true, //显示搜索 (true/false) numDisplayed: 4, //超出数量整合 overflowText: "已选中 {n}项", //超出数量整合文本提示,{n}为数量 }); // 调用限制最多选择两个选项的方法 $(".demo1").ySelectDefault(2); $("#m1").ySelectDefault(2); var selectedValues = $("#m1").ySelectVal(); // 获取当前选中的值 console.log(selectedValues.length, "测斜的长度000000"); var $options = $("#m1").prev().find(".fs-options>div"); // 获取所有的选项 if (selectedValues.length == 2) { console.log("如果元素的长度为2,那么其他项就不让点击"); // 禁用其他未选中的选项 $options.each(function () { var $option = $(this); if (!$option.hasClass("selected")) { $option.addClass("disabled"); // 添加禁用类 $option.off("click"); // 移除点击事件 } }); } // 恢复之前选中的数据点 $("#current").click(function () { console.log($("#m2").ySelectedValues(",")); console.log($("#m2").ySelectedTexts(",")); }); }); $("#m1").change(function () { var $select = $(this); var selectedValues = $select.ySelectVal(); // 获取当前选中的值 var $options = $select.prev().find(".fs-options>div"); // 获取所有的选项 if (selectedValues.length == 2) { console.log("如果元素的长度为2,那么其他项就不让点击"); // 禁用其他未选中的选项 $options.each(function () { var $option = $(this); if (!$option.hasClass("selected")) { $option.addClass("disabled"); // 添加禁用类 $option.off("click"); // 移除点击事件 } }); } else { console.log("选中的值小于2,允许点击其他项"); // 启用所有选项的点击事件 $options.each(function () { var $option = $(this); if ($option.hasClass("disabled")) { $option.removeClass("disabled"); // 移除禁用类 $option.on("click", function () { // 你可以在这里添加选项点击事件的逻辑 console.log("选中项:", $option.text()); }); } }); } }); </script> </body> </html>
复制
3.1源码示例地址
yselect操作select: 实现select多选,复选,全选,限制选中个数