一、效果截图

二、文件引用

多选下拉框扩展自handsontable的BaseEditor。

多选下拉框组件由两个文件构成,

  • 一个下拉框样式表MultiSelect.css
  • 一个组件实现脚本MultiSelect.js

使用时引用这两个文件即可,当然,要先在你的页面中引用handsontable的核心js文件,再引用我这两个文件。

其中js以模块类型引用(type="module")

<link href="../../Scripts/MultiSelect.css" rel="stylesheet" />
<script type="module" src="../../Scripts/MultiSelect.js"></script>

三、使用范例

var columns = [
{ data: 'field1', title: 'title1', width: 120, className: 'htCenter htMiddle', editor: 'MultiSelect', selectOptions: ['1#', '2#', '3#', '4#', '5#', '6#', '7#', '8#'] }
];

四、源代码

1.MultiSelect.js

/// <reference path="handsontable.full.min.js" />

//封闭在IIFE中
(Handsontable => {
class MultiSelectEditor extends Handsontable.editors.BaseEditor { // ...rest of the editor code
/**
* Initializes editor instance, DOM Element and mount hooks.
* 初始化编辑器实例,dom元素和挂载钩子函数
*/
init() {
// Create detached node, add CSS class and make sure its not visible
//增加触发节点,增加class类并确保节点隐藏隐藏
var MultiSelectDomContainer = document.createElement("div");
MultiSelectDomContainer.style.paddingTop = "30px";
this.select = MultiSelectDomContainer;
Handsontable.dom.addClass(this.select, 'htMultiSelectEditor');
this.select.style.display = 'none'; // Attach node to DOM, by appending it to the container holding the table
this.hot.rootElement.appendChild(this.select);
} //编辑器的值加载到单元格
getValue() {
var selects = "";
var childs = this.select.children;
for (var i = 0; i < childs.length; i++) {
if (childs[i].children[0].checked == true) {
selects += "["+childs[i].children[1].innerHTML+"]";
}
}
return selects;
return this.select;
return "";
} //单元格的值加载到编辑器
setValue(value) {
var selectCount = 0;
var selects = value.split("][");
var childs = this.select.children;
for (var i = 0; i < selects.length; i++) {
selects[i]=selects[i].replace("[", "").replace("]", ""); for (var j = 0; j < childs.length; j++) {
if (childs[j].value == selects[i]) {
selectCount += 1;
childs[j].children[0].checked = true;
}
}
}
//this.select.style.paddingTop = selectCount*30+"px";
} //打开编辑器
open() {
this._opened = true;
this.refreshDimensions();
this.select.style.display = '';
} //编辑器样式计算
refreshDimensions() {
this.TD = this.getEditedCell(); // TD is outside of the viewport.
if (!this.TD) {
this.close(); return;
}
//调整弹出框位置,使其位于单元格下面
this.select.style.paddingTop = this.TD.clientHeight + "px"; const { wtOverlays } = this.hot.view.wt;
const currentOffset = Handsontable.dom.offset(this.TD);
const containerOffset = Handsontable.dom.offset(this.hot.rootElement);
const scrollableContainer = wtOverlays.scrollableElement;
const editorSection = this.checkEditorSection();
let width = Handsontable.dom.outerWidth(this.TD) + 1;
let height = Handsontable.dom.outerHeight(this.TD) + 1;
let editTop = currentOffset.top - containerOffset.top - 1 - (scrollableContainer.scrollTop || 0);
let editLeft = currentOffset.left - containerOffset.left - 1 - (scrollableContainer.scrollLeft || 0);
let cssTransformOffset; switch (editorSection) {
case 'top':
cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.topOverlay.clone.wtTable.holder.parentNode);
break;
case 'left':
cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.leftOverlay.clone.wtTable.holder.parentNode);
break;
case 'top-left-corner':
cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.topLeftCornerOverlay.clone.wtTable.holder.parentNode);
break;
case 'bottom-left-corner':
cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.bottomLeftCornerOverlay.clone.wtTable.holder.parentNode);
break;
case 'bottom':
cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.bottomOverlay.clone.wtTable.holder.parentNode);
break;
default:
break;
} if (this.hot.getSelectedLast()[0] === 0) {
editTop += 1;
}
if (this.hot.getSelectedLast()[1] === 0) {
editLeft += 1;
} const selectStyle = this.select.style; if (cssTransformOffset && cssTransformOffset !== -1) {
selectStyle[cssTransformOffset[0]] = cssTransformOffset[1];
} else {
Handsontable.dom.resetCssTransform(this.select);
} const cellComputedStyle = Handsontable.dom.getComputedStyle(this.TD, this.hot.rootWindow); if (parseInt(cellComputedStyle.borderTopWidth, 10) > 0) {
height -= 1;
}
if (parseInt(cellComputedStyle.borderLeftWidth, 10) > 0) {
width -= 1;
} selectStyle.height = `${height}px`;
selectStyle.minWidth = `${width}px`;
selectStyle.top = `${editTop}px`;
selectStyle.left = `${editLeft}px`;
selectStyle.margin = '0px';
} //获取当前单元格
getEditedCell() {
const { wtOverlays } = this.hot.view.wt;
const editorSection = this.checkEditorSection();
let editedCell; switch (editorSection) {
case 'top':
editedCell = wtOverlays.topOverlay.clone.wtTable.getCell({
row: this.row,
col: this.col
});
this.select.style.zIndex = 101;
break;
case 'corner':
editedCell = wtOverlays.topLeftCornerOverlay.clone.wtTable.getCell({
row: this.row,
col: this.col
});
this.select.style.zIndex = 103;
break;
case 'left':
editedCell = wtOverlays.leftOverlay.clone.wtTable.getCell({
row: this.row,
col: this.col
});
this.select.style.zIndex = 102;
break;
default:
editedCell = this.hot.getCell(this.row, this.col);
this.select.style.zIndex = '';
break;
} return editedCell < 0 ? void 0 : editedCell;
} focus() {
this.select.focus();
} close() {
this._opened = false;
this.select.style.display = 'none';
} //读取选项配置到编辑器
prepare(row, col, prop, td, originalValue, cellProperties) {
// Remember to invoke parent's method
super.prepare(row, col, prop, td, originalValue, cellProperties);
const selectOptions = this.cellProperties.selectOptions;
let options; if (typeof selectOptions === 'function') {
options = this.prepareOptions(selectOptions(this.row, this.col, this.prop));
} else {
options = this.prepareOptions(selectOptions);
} Handsontable.dom.empty(this.select); Handsontable.helper.objectEach(options, (value, key) => {
const optionElement = this.hot.rootDocument.createElement('div'); const checkbox = this.hot.rootDocument.createElement('input');
checkbox.type = "checkbox";
optionElement.appendChild(checkbox); var textSpan = this.hot.rootDocument.createElement('span');
textSpan.innerHTML = value;
optionElement.appendChild(textSpan); optionElement.value = key; Handsontable.dom.addClass(optionElement, 'MultiSelectOption'); //Handsontable.dom.fastInnerHTML(optionElement, value);
this.select.appendChild(optionElement);
});
}
prepareOptions(optionsToPrepare) {
let preparedOptions = {}; if (Array.isArray(optionsToPrepare)) {
for (let i = 0, len = optionsToPrepare.length; i < len; i++) {
preparedOptions[optionsToPrepare[i]] = optionsToPrepare[i];
} } else if (typeof optionsToPrepare === 'object') {
preparedOptions = optionsToPrepare;
} return preparedOptions;
}
} // Put editor in dedicated namespace
//将编辑器添加到专用命名空间
Handsontable.editors.MultiSelectEditor = MultiSelectEditor;
// Register alias
//编辑器注册别名
Handsontable.editors.registerEditor('MultiSelect', MultiSelectEditor); })(Handsontable);

MultiSelect.js

2.MultiSelect.css

.htMultiSelectEditor {
/*
* This hack enables to change <select> dimensions in WebKit browsers
*/
-webkit-appearance: menulist-button !important;
position: absolute;
width: auto;
}
.MultiSelectOption:hover{
background-color:antiquewhite;
border:1px solid gray;
}

MultiSelect.css

五、更新

日期:2022-01-10

问题

  • 修改样式
  • 解决在表格底部,弹出的下拉框被遮挡的问题

1.MutiSelect.js

/// <reference path="handsontable.full.min.js" />

//封闭在IIFE中
(Handsontable => {
class MultiSelectEditor extends Handsontable.editors.BaseEditor { // ...rest of the editor code
/**
* Initializes editor instance, DOM Element and mount hooks.
* 初始化编辑器实例,dom元素和挂载钩子函数
*/
init() {
// Create detached node, add CSS class and make sure its not visible
//增加触发节点,增加class类并确保节点隐藏隐藏
var MultiSelectDomContainer = document.createElement("div");
MultiSelectDomContainer.style.paddingTop = "30px";
this.select = MultiSelectDomContainer;
Handsontable.dom.addClass(this.select, 'htMultiSelectEditor');
this.select.style.display = 'none'; // Attach node to DOM, by appending it to the container holding the table
//this.hot.rootElement.appendChild(this.select);//当前单元格的下拉框子节点(会被遮挡)
this.hot.rootDocument.body.appendChild(this.select);//文档的下拉框子节点(不会被遮挡)
this.select.addEventListener("mousedown", (
function (e) {
return e.stopPropagation();
}));//阻止单光获得值在下拉框元素上面,editermanager的点击事件执行(关闭下拉框),让单元格能获得值
//document.body.appendChild(this.select);
} //编辑器的值加载到单元格
getValue() {
var selects = "";
var childs = this.select.children;
for (var i = 0; i < childs.length; i++) {
if (childs[i].children[0].checked == true) {
selects += "["+childs[i].children[1].innerHTML+"]";
}
}
return selects;
return this.select;
return "";
} //单元格的值加载到编辑器
setValue(value) {
var selectCount = 0;
var selects = value.split("][");
var childs = this.select.children;
for (var i = 0; i < selects.length; i++) {
selects[i]=selects[i].replace("[", "").replace("]", ""); for (var j = 0; j < childs.length; j++) {
if (childs[j].value == selects[i]) {
selectCount += 1;
childs[j].children[0].checked = true;
}
}
}
//this.select.style.paddingTop = selectCount*30+"px";
} //打开编辑器
open() {
this._opened = true;
this.refreshDimensions();
this.select.style.display = '';
} //编辑器样式计算
refreshDimensions() {
this.TD = this.getEditedCell(); // TD is outside of the viewport.
if (!this.TD) {
this.close(); return;
}
//调整弹出框位置,使其位于单元格下面
this.select.style.paddingTop = this.TD.clientHeight + "px"; const { wtOverlays } = this.hot.view.wt;
const currentOffset = Handsontable.dom.offset(this.TD);
const containerOffset = Handsontable.dom.offset(this.hot.rootElement);
const scrollableContainer = wtOverlays.scrollableElement;
const editorSection = this.checkEditorSection();
let width = Handsontable.dom.outerWidth(this.TD) + 1;
let height = Handsontable.dom.outerHeight(this.TD) + 1;
let editTop = currentOffset.top - containerOffset.top - 1 - (scrollableContainer.scrollTop || 0);
let editLeft = currentOffset.left - containerOffset.left - 0 - (scrollableContainer.scrollLeft || 0);
let cssTransformOffset; switch (editorSection) {
case 'top':
cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.topOverlay.clone.wtTable.holder.parentNode);
break;
case 'left':
cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.leftOverlay.clone.wtTable.holder.parentNode);
break;
case 'top-left-corner':
cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.topLeftCornerOverlay.clone.wtTable.holder.parentNode);
break;
case 'bottom-left-corner':
cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.bottomLeftCornerOverlay.clone.wtTable.holder.parentNode);
break;
case 'bottom':
cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.bottomOverlay.clone.wtTable.holder.parentNode);
break;
default:
break;
} if (this.hot.getSelectedLast()[0] === 0) {
editTop += 1;
}
if (this.hot.getSelectedLast()[1] === 0) {
editLeft += 1;
} const selectStyle = this.select.style; if (cssTransformOffset && cssTransformOffset !== -1) {
selectStyle[cssTransformOffset[0]] = cssTransformOffset[1];
} else {
Handsontable.dom.resetCssTransform(this.select);
} const cellComputedStyle = Handsontable.dom.getComputedStyle(this.TD, this.hot.rootWindow); if (parseInt(cellComputedStyle.borderTopWidth, 10) > 0) {
height -= 1;
}
if (parseInt(cellComputedStyle.borderLeftWidth, 10) > 0) {
width -= 1;
} //selectStyle.height = `${height}px`;
selectStyle.height = "auto";
selectStyle.minWidth = `${width}px`;
selectStyle.top = `${editTop}px`;
selectStyle.left = `${editLeft}px`;
selectStyle.margin = '0px';
} //获取当前单元格
getEditedCell() {
const { wtOverlays } = this.hot.view.wt;
const editorSection = this.checkEditorSection();
let editedCell; switch (editorSection) {
case 'top':
editedCell = wtOverlays.topOverlay.clone.wtTable.getCell({
row: this.row,
col: this.col
});
this.select.style.zIndex = 101;
break;
case 'corner':
editedCell = wtOverlays.topLeftCornerOverlay.clone.wtTable.getCell({
row: this.row,
col: this.col
});
this.select.style.zIndex = 103;
break;
case 'left':
editedCell = wtOverlays.leftOverlay.clone.wtTable.getCell({
row: this.row,
col: this.col
});
this.select.style.zIndex = 102;
break;
default:
editedCell = this.hot.getCell(this.row, this.col);
this.select.style.zIndex = '';
break;
} return editedCell < 0 ? void 0 : editedCell;
} focus() {
this.select.focus();
} close() {
this._opened = false;
this.select.style.display = 'none';
} //读取选项配置到编辑器
prepare(row, col, prop, td, originalValue, cellProperties) {
// Remember to invoke parent's method
super.prepare(row, col, prop, td, originalValue, cellProperties);
const selectOptions = this.cellProperties.selectOptions;
let options; if (typeof selectOptions === 'function') {
options = this.prepareOptions(selectOptions(this.row, this.col, this.prop));
} else {
options = this.prepareOptions(selectOptions);
} Handsontable.dom.empty(this.select); Handsontable.helper.objectEach(options, (value, key) => {
const optionElement = this.hot.rootDocument.createElement('div'); const checkbox = this.hot.rootDocument.createElement('input');
checkbox.type = "checkbox";
optionElement.appendChild(checkbox); var textSpan = this.hot.rootDocument.createElement('span');
textSpan.innerHTML = value;
optionElement.appendChild(textSpan); optionElement.value = key; Handsontable.dom.addClass(optionElement, 'MultiSelectOption'); //Handsontable.dom.fastInnerHTML(optionElement, value);
this.select.appendChild(optionElement);
});
}
prepareOptions(optionsToPrepare) {
let preparedOptions = {}; if (Array.isArray(optionsToPrepare)) {
for (let i = 0, len = optionsToPrepare.length; i < len; i++) {
preparedOptions[optionsToPrepare[i]] = optionsToPrepare[i];
} } else if (typeof optionsToPrepare === 'object') {
preparedOptions = optionsToPrepare;
} return preparedOptions;
} } // Put editor in dedicated namespace
//将编辑器添加到专用命名空间
Handsontable.editors.MultiSelectEditor = MultiSelectEditor;
// Register alias
//编辑器注册别名
Handsontable.editors.registerEditor('MultiSelect', MultiSelectEditor); })(Handsontable);

MutiSelect.js

2.MutiSelect.css

.htMultiSelectEditor {
/*
* This hack enables to change <select> dimensions in WebKit browsers
*/
-webkit-appearance: menulist-button !important;
position: absolute;
width: auto;
height:100px;
z-index:9999;
background-color:white;
border:1px solid gray;
}
.MultiSelectOption:hover{
background-color:antiquewhite;
}

MultiSelect.css

handsontable多选下拉框编辑器扩展的更多相关文章

  1. query多选下拉框插件 jquery-multiselect(修改)

    其实网上关于该控件的使用教程已经很多了,其中 query多选下拉框插件 jquery-multiselect Jquery多选下拉列表插件jquery multiselect功能介绍及使用 这2个的介 ...

  2. Easyui-Combobox多选下拉框

    因为工作需要,引入combobox多选下拉框,并且获取选择的值并以","分开. 效果如下: 代码如下: <html> <head> <title> ...

  3. Extjs4.2 多选下拉框

    //多选下拉框 Ext.define('MDM.view.custom.MultiComboBox', { extend: 'Ext.form.ComboBox', alias: 'widget.mu ...

  4. js:jquery multiSelect 多选下拉框实例

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. DropDownList单选与多选下拉框

    一.单选DropDownList传值 1.添加界面的DropDownList显示值问题 (1)在方法内添加ViewData的方法: var ad = new UnitsRepository(); Vi ...

  6. pentaho cde 自定义复选下拉框 checkbox select

    pentaho  自带的component 虽多,但是当用户需要在一个表格中查看多个组别的数据时,pentaho自带的单选框就不能实现了,所以复选下拉框势在必行,实现效果如下: 实现原理是借用了jqu ...

  7. Bootstrap3级联多选下拉框

    <!DOCTYPE html> <html> <head> <title>Bootstrap3级联多选下拉框</title> <met ...

  8. js怎么能取得多选下拉框选中的多个值?

    方法:获取多选下拉框对象数组→循环判断option选项的selected属性(true为选中,false为未选中)→使用value属性取出选中项的值.实例演示如下: 1.HTML结构 1 2 3 4 ...

  9. js多选下拉框

    1.js原生实现 1.1:引用JS文件 /*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */ !function(a,b ...

  10. 多选下拉框带搜索(aps.net)

    自己写了一个带搜索功能的多选下拉框,为了要获取值,就没有封装插件,实现思路 1.一个文本框 做搜索 2.一个文本框显示选中文本,一个隐藏控件存值 3.一个div里面绑定CheckBoxList控件(这 ...

随机推荐

  1. sumo简单安装及运行实例

    下载解压并添加环境变量 记录一下今天SUMO的安装及使用经验,写的可能比较潦草,没看懂的小伙伴在下方评论,我看到一定会解答. 第一步先打开网址下载sumo: https://sourceforge.n ...

  2. 他来了他来了,.net开源智能家居之苹果HomeKit的c#原生sdk【Homekit.Net】1.0.0发布,快来打造你的私人智能家居吧

    背景介绍 hi 大家好,我是三合,作为一个非著名懒人,每天上完班回到家,瘫在沙发上一动都不想动,去开个灯我都嫌累,此时,智能家居拯救了我,只需要在手机点点点,开关灯,空调,窗帘就都搞定了,一开始我用的 ...

  3. 使用ISS服务器方式跑C#程序

    使用ISS服务器方式跑C#程序 VS2010,临时接了一个C#系统的小系统,需要本地调试跑一下 但是老是在conn.open提示06413,简单来说就是连接不上数据库 尝试了很多方法,最后还是决定配置 ...

  4. 登录chatgpt的时候出现429的解决方法,亲测有效

    登录chatgpt的时候出现429的解决方法 PS:在2023年3月14日晚还是可以用的,亲测有效 登录chatgpt的时候出现429的解决方法 很多时候在国内用代理进入chatgpt的时候会出现42 ...

  5. 【hibernate】使用HQL对页面进行时间校验操作(预约)

    [hibernate]使用HQL对页面进行时间校验操作(预约) 预约系统中的时间校验 正好接了一个预约的需求,还需要用java 7和hibernate 1.时间冲突,时间段不能重复,在保存前对数据库进 ...

  6. 力扣240(java&python)-搜索二维矩阵 II(中等)

    题目: 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target .该矩阵具有以下特性: 每行的元素从左到右升序排列.每列的元素从上到下升序排列. 示例 1: 输入:ma ...

  7. 技术门槛高?来看 Intel 机密计算技术在龙蜥社区的实践 | 龙蜥技术

    简介: 数据可用不可见是怎么做到的? 编者按:龙蜥社区云原生机密计算 SIG 定位于云原生机密计算底层基础设施,专注于机密计算底层技术.在阿里巴巴开源开放周中, 龙蜥社区机密计算 SIG Mainta ...

  8. 真正的HTAP对用户和开发者意味着什么?

    简介: Gartner 2016 年首次提出 HTAP(Hybrid Transaction / Analytical Processing,混合事务分析处理)并给出明确的定义:即同时支持 OLTP ...

  9. ModelScope初探:一行代码调用成熟AI模型。

    简介: 如何用一行代码调用成熟AI模型?试试ModelScope,让AI开发者解放生产力! ModelScope是阿里推出的下一代开源的模型即服务共享平台,为泛AI开发者提供灵活.易用.低成本的一站式 ...

  10. 【Serverless实战】B站每日自动签到&&传统单节点网站的Serverless上云

    简介: Serverless好哇!这里将针对个人与生产两个应用方向的测评 使用Serverless实现自动获取每日B站的经验值,让你更快冲到LV6! 你的业务站点还是一台服务器All in One吗? ...