handsontable多选下拉框编辑器扩展
一、效果截图
二、文件引用
多选下拉框扩展自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多选下拉框编辑器扩展的更多相关文章
- query多选下拉框插件 jquery-multiselect(修改)
其实网上关于该控件的使用教程已经很多了,其中 query多选下拉框插件 jquery-multiselect Jquery多选下拉列表插件jquery multiselect功能介绍及使用 这2个的介 ...
- Easyui-Combobox多选下拉框
因为工作需要,引入combobox多选下拉框,并且获取选择的值并以","分开. 效果如下: 代码如下: <html> <head> <title> ...
- Extjs4.2 多选下拉框
//多选下拉框 Ext.define('MDM.view.custom.MultiComboBox', { extend: 'Ext.form.ComboBox', alias: 'widget.mu ...
- js:jquery multiSelect 多选下拉框实例
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- DropDownList单选与多选下拉框
一.单选DropDownList传值 1.添加界面的DropDownList显示值问题 (1)在方法内添加ViewData的方法: var ad = new UnitsRepository(); Vi ...
- pentaho cde 自定义复选下拉框 checkbox select
pentaho 自带的component 虽多,但是当用户需要在一个表格中查看多个组别的数据时,pentaho自带的单选框就不能实现了,所以复选下拉框势在必行,实现效果如下: 实现原理是借用了jqu ...
- Bootstrap3级联多选下拉框
<!DOCTYPE html> <html> <head> <title>Bootstrap3级联多选下拉框</title> <met ...
- js怎么能取得多选下拉框选中的多个值?
方法:获取多选下拉框对象数组→循环判断option选项的selected属性(true为选中,false为未选中)→使用value属性取出选中项的值.实例演示如下: 1.HTML结构 1 2 3 4 ...
- js多选下拉框
1.js原生实现 1.1:引用JS文件 /*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */ !function(a,b ...
- 多选下拉框带搜索(aps.net)
自己写了一个带搜索功能的多选下拉框,为了要获取值,就没有封装插件,实现思路 1.一个文本框 做搜索 2.一个文本框显示选中文本,一个隐藏控件存值 3.一个div里面绑定CheckBoxList控件(这 ...
随机推荐
- java深入理解浅拷贝和深拷贝
目录 简介 拷贝接口 使用clone导致的浅拷贝 使用clone的深拷贝 不要overridden clone 总结 简介 拷贝对象是java中经常会遇到的问题.java中存在两种类型,基础类型和引用 ...
- 成长计划校园极客秀|基于OpenHarmony的智能阳台
前言 本文由OpenAtom OpenHarmony(以下简称"OpenHarmony")开源开发者成长计划活动的参与者李建涛提供,详细阐述了由搭载OpenHarmony系统的拓维 ...
- 本周二晚19:00战码先锋第8期直播丨如何多方位参与OpenHarmony开源贡献
OpenAtom OpenHarmony(以下简称"OpenHarmony")工作委员会首度发起「OpenHarmony开源贡献者计划」,旨在鼓励开发者参与OpenHarmony开 ...
- HDC2021技术分论坛:鸿蒙智联设备开发,这五大法宝你应该拥有
作者:zhaowenguang,dinglu, 华为高级工程师 Huawei LiteOS是轻量级的开源物联网操作系统.智能硬件使能平台,可广泛应用于智能家居.穿戴式.车联网.制造业等领域,使物联网终 ...
- 【直播预告】HarmonyOS 极客松赋能直播第六期:产品创新从哪里来?
- 重新点亮linux 命令树————守护进程[二十三]
前言 简单整理一下守护进程. 正文 守护进程一般是开机启动的. 使用nohup 与 & 符号配合运行一个命令 nohup命令使进程忽略hangup(挂起)信号 使用tail 查看log文件. ...
- 用fpga实现1G Eth TCP&UDP硬件协议栈
用fpga实现的1G 以太网硬件协议栈,属于轻的协议栈
- Git 各指令的本质
前言 作为当前世界上最强大的代码管理工具Git相信大家都很熟悉,但据我所知有很大一批人停留在clone.commit.pull.push...的阶段,是不是对rebase心里没底只敢用merge? 碰 ...
- 阿里云2020上云采购季,你的ECS买好了吗?
阿里云2020上云采购季,超级品类日,天天有爆款. 今日爆款推荐:云服务器. 重磅推荐两款,限时抢购. 新品共享型s6: 企业级共享型n4: 想看更多云产品,来阿里云采购季: https://www. ...
- 阿里云RemoteShuffleService新功能:AQE和流控
简介:阿里云EMR自2020年推出Remote Shuffle Service(RSS)以来,帮助了诸多客户解决Spark作业的性能.稳定性问题,并使得存算分离架构得以实施.为了更方便大家使用和扩展 ...