前言:前天刚写了篇JS组件Bootstrap Table表格行拖拽效果,今天接到新的需要,需要在之前表格行拖拽的基础上能够同时拖拽选中的多行。用了半天时间研究了下,效果是出来了,但是感觉不尽如人意。先把它分享出来,以后想到更好的办法再优化吧。 一、效果展示 1、拖动前 2、拖动中 3、拖动后 4、撤销回到拖动前状态 二、需求分析 通过上篇我们知道,如果要实现拖拽,必须要有一个可以拖拽的标签,或者叫容器,比如上篇里面的tr就是一个拖拽的容器,那么如果要实现选择行的拖拽,那么博主的第一反应是将选中的行放到一个容器里面,比如放到一个div中,然后注册这个div的可拖拽,可是实际情况是,tr是在table里面的标签,如果将tr用div包起来,势必将table里面的样式打乱,这个界面就真的是乱掉了。很显然,这条路走不通。然后通过谷歌浏览器审核元素知道,用Bootstrap table生成的表格tr的父级元素实际上是tbody,于是在想是否可以注册tbody的拖拽,实践证明,此法可行。于是就此开干。 三、代码示例 cshtm的代码就不再重复,和上篇相同。我们重点来看看js代码。 var i_statuindex = 0 var arrdata = [] var m_oTable = null $(function () { //1.初始化表格 m_oTable = new TableInit() m_oTable.Init()//2.初始化按钮事件 var oButtonInit = new ButtonInit() oButtonInit.Init()//3.日期控件的初始化 $(".datetimepicker").datetimepicker({ format: 'yyyy-mm-dd hh:ii', autoclose: true, todayBtn: true, }) }) //表格相关事件和方法 var TableInit = function () { var oTableInit = new Object()oTableInit.Init = function () { $('#tb_order_left').bootstrapTable({url: '/api/OrderApi/get',method: 'get',striped: true,cache: false,striped: true,pagination: true,height: 600,uniqueId:"TO_ORDER_ID",queryParams: oTableInit.queryParams,queryParamsType: "limit",sidePagination: "server",pageSize: 10,pageList: [10, 25, 50, 100],search: true,strictSearch: true,showColumns: true,showRefresh: true,minimumCountColumns: 2,clickToSelect: true,columns: [{checkbox: true},{field: 'ORDER_NO',title: '订单号'},{field: 'BODY_NO',title: '车身号'}, {field: 'VIN',title: 'VIN码'}, {field: 'TM_MODEL_MATERIAL_ID',title: '整车编码'},{field: 'ORDER_TYPE',title: '订单类型'},{field: 'ORDER_STATUS',title: '订单状态'},{field: 'CREATE_DATE',title: '订单导入时间'},{field: 'PLAN_DATE',title: '订单计划上线日期'},{field: 'VMS_NO',title: 'VMS号'},{field: 'ENGIN_CODE',title: '发动机号'},{field: 'TRANS_CODE',title: '变速箱号'},{field: 'OFFLINE_DATE_ACT',title: '实际下线日期'},{field: 'HOLD_RES',title: 'hold理由'},{field: 'SPC_FLAG',title: '特殊标记'},],onLoadSuccess: function (data) {oTableInit.InitDrag() if (data.total >0) { var iheight = $('#div_tableleft').find(".fixed-table-container").height()$('#div_tableleft').find(".fixed-table-container").height(iheight + 36) }},onCheckAll: function (rows) {$("#tb_order_left tbody tr").addClass("selected") },onUncheckAll: function (rows) {$("#tb_order_left tbody tr").removeClass("selected") } })$('#tb_order_right').bootstrapTable({url: '/api/OrderApi/get',method: 'get',toolbar: '#toolbar_right',striped: true,cache: false,striped: true,pagination: true,height: 600,queryParams: oTableInit.queryParamsRight,queryParamsType: "limit",//ajaxOptions: { departmentname: "", statu: "" },sidePagination: "server",pageSize: 10,pageList: [10, 25, 50, 100],search: true,strictSearch: true,showRefresh: true,minimumCountColumns: 2,columns: [{field: 'ORDER_NO',title: '订单号'},{field: 'BODY_NO',title: '车身号'}, {field: 'VIN',title: 'VIN码'}, {field: 'TM_MODEL_MATERIAL_ID',title: '整车编码'},{field: 'ORDER_TYPE',title: '订单类型'},{field: 'ORDER_STATUS',title: '订单状态'},{field: 'CREATE_DATE',title: '订单导入时间'},{field: 'PLAN_DATE',title: '订单计划上线日期'},{field: 'VMS_NO',title: 'VMS号'},{field: 'ENGIN_CODE',title: '发动机号'},{field: 'TRANS_CODE',title: '变速箱号'},{field: 'OFFLINE_DATE_ACT',title: '实际下线日期'},{field: 'HOLD_RES',title: 'hold理由'},{field: 'SPC_FLAG',title: '特殊标记'},],onLoadSuccess: function (data) {oTableInit.InitDrop() } }) }oTableInit.InitDrag = function () { $('#tb_order_left tbody').draggable({helper: "clone",start: function (event, ui) {var old_left_data = JSON.stringify($('#tb_order_left').bootstrapTable("getData")) var old_right_data = JSON.stringify($('#tb_order_right').bootstrapTable("getData")) var odata = { index: ++i_statuindex, left_data: old_left_data, right_data: old_right_data } arrdata.push(odata) },stop: function (event, ui) {} }) }oTableInit.InitDrop = function () { $("#div_tableright div[class=fixed-table-container]").droppable({drop: function (event, ui) {var arrtr = $(ui.helper[0]).find("tr[class=selected]") if (arrtr.length <= 0) { alert("请先选中要插单的行")return }var oTop = ui.helper[0].offsetTop var iRowHeadHeight = 40 var iRowHeight = 37 var rowIndex = 0 if (oTop <= iRowHeadHeight + iRowHeight / 2) { rowIndex = 0 }else { rowIndex = Math.ceil((oTop - iRowHeadHeight) / iRowHeight) }for (var i = 0i <arrtr.lengthi++) { var arrtd = $(arrtr[i]).find("td")var uniqueid = $(arrtr[i]).attr("data-uniqueid")var rowdata = { ORDER_NO: $(arrtd[1]).text(), BODY_NO: $(arrtd[2]).text(), VIN: $(arrtd[3]).text(), TM_MODEL_MATERIAL_ID: $(arrtd[4]).text(), ORDER_TYPE: $(arrtd[5]).text(), ORDER_STATUS: $(arrtd[6]).text(), CREATE_DATE: $(arrtd[7]).text() == "-" ? null : $(arrtd[7]).text(), PLAN_DATE: $(arrtd[8]).text() == "-" ? null : $(arrtd[8]).text(), VMS_NO: $(arrtd[9]).text(), ENGIN_CODE: $(arrtd[10]).text(), TRANS_CODE: $(arrtd[11]).text(), OFFLINE_DATE_ACT: $(arrtd[12]).text() == "-" ? null : $(arrtd[12]).text(), HOLD_RES: $(arrtd[13]).text(), SPC_FLAG: $(arrtd[14]).text(), TO_ORDER_ID: uniqueid }$("#tb_order_right").bootstrapTable("insertRow", { index: rowIndex++, row: rowdata })$('#tb_order_left').bootstrapTable("removeByUniqueId", uniqueid) }oTableInit.InitDrag() } }) }oTableInit.queryParams = function (params) { //配置参数 var temp = { //这里的键的名字和控制器的变量名必须一直,这边改动,控制器也需要改成一样的limit: params.limit, //页面大小offset: params.offset, //页码strBodyno: $("#txt_search_bodynumber").val(),strVin: $("#txt_search_vinnumber").val(),strOrderno: $("#txt_search_ordernumber").val(),strEngincode: $("#txt_search_engin_code").val(),strOrderstatus: 0,strTranscode: $("#txt_search_trans_code").val(),strVms: $("#txt_search_vms").val(),strCarcode: $("#txt_search_carcode").val(),strImportStartdate: $("#txt_search_import_startdate").val(),strImportEnddate: $("#txt_search_import_enddate").val(),strSendStartdate: $("#txt_search_send_startdate").val(),strSendEnddate: $("#txt_search_send_enddate").val(), } return temp }oTableInit.queryParamsRight = function (params) { //配置参数 var temp = { //这里的键的名字和控制器的变量名必须一直,这边改动,控制器也需要改成一样的limit: params.limit, //页面大小offset: params.offset, //页码strBodyno: "",strVin: "",strOrderno: "",strEngincode: "",strOrderstatus: 5,strTranscode: "",strVms: "",strCarcode: "",strImportStartdate: "",strImportEnddate: "",strSendStartdate: "",strSendEnddate: "", } return temp }return oTableInit } //页面按钮初始化事件 var ButtonInit = function () { var oInit = new Object() var postdata = {}oInit.Init = function () { //查询点击事件 $("#btn_query").click(function () {$("#tb_order_left").bootstrapTable('refresh') })//重置点击事件 $("#btn_reset").click(function () {$(".container-fluid").find(".form-control").val("") $("#tb_order_left").bootstrapTable('refresh') })//插单操作点击事件 $("#btn_insertorder").click(function () { })//撤销操作点击事件 $("#btn_cancel").click(function () {if (i_statuindex <= 0) {return }for (var i = 0i <arrdata.lengthi++) {if (arrdata[i].index != i_statuindex) { continue }var arr_left_data = eval(arrdata[i].left_data) var arr_right_data = eval(arrdata[i].right_data) $('#tb_order_left').bootstrapTable('removeAll') $('#tb_order_right').bootstrapTable('removeAll') $('#tb_order_left').bootstrapTable('append', arr_left_data) for (var x = 0x <arr_right_data.lengthx++) { $("#tb_order_right").bootstrapTable("insertRow", { index: x, row: arr_right_data[x] }) }//$('#tb_order_right').bootstrapTable('append', arr_right_data)//append之后不能dropbreak }i_statuindex-- //重新注册可拖拽m_oTable.InitDrag() //m_oTable.InitDrop() }) }return oInit } 还是重点看看部分代码 1、注册左边可拖拽 $('#tb_order_left tbody').draggable({helper: "clone",start: function (event, ui) {var old_left_data = JSON.stringify($('#tb_order_left').bootstrapTable("getData")) var old_right_data = JSON.stringify($('#tb_order_right').bootstrapTable("getData")) var odata = { index: ++i_statuindex, left_data: old_left_data, right_data: old_right_data } arrdata.push(odata) },stop: function (event, ui) {} }) 这里代码很简单,主要做了两件事: (1)注册tbody的可拖拽,同样适用的JQuery UI的draggable事件。 (2)在开始拖拽前,保存两边表格的数据,用于还原的操作。 2、注册右边drop $("#div_tableright div[class=fixed-table-container]").droppable({drop: function (event, ui) {var arrtr = $(ui.helper[0]).find("tr[class=selected]") if (arrtr.length <= 0) { alert("请先选中要插单的行")return }var oTop = ui.helper[0].offsetTop var iRowHeadHeight = 40 var iRowHeight = 37 var rowIndex = 0 if (oTop <= iRowHeadHeight + iRowHeight / 2) { rowIndex = 0 }else { rowIndex = Math.ceil((oTop - iRowHeadHeight) / iRowHeight) }for (var i = 0i <arrtr.lengthi++) { var arrtd = $(arrtr[i]).find("td")var uniqueid = $(arrtr[i]).attr("data-uniqueid")var rowdata = { ORDER_NO: $(arrtd[1]).text(), BODY_NO: $(arrtd[2]).text(), VIN: $(arrtd[3]).text(), TM_MODEL_MATERIAL_ID: $(arrtd[4]).text(), ORDER_TYPE: $(arrtd[5]).text(), ORDER_STATUS: $(arrtd[6]).text(), CREATE_DATE: $(arrtd[7]).text() == "-" ? null : $(arrtd[7]).text(), PLAN_DATE: $(arrtd[8]).text() == "-" ? null : $(arrtd[8]).text(), VMS_NO: $(arrtd[9]).text(), ENGIN_CODE: $(arrtd[10]).text(), TRANS_CODE: $(arrtd[11]).text(), OFFLINE_DATE_ACT: $(arrtd[12]).text() == "-" ? null : $(arrtd[12]).text(), HOLD_RES: $(arrtd[13]).text(), SPC_FLAG: $(arrtd[14]).text(), TO_ORDER_ID: uniqueid }$("#tb_order_right").bootstrapTable("insertRow", { index: rowIndex++, row: rowdata })$('#tb_order_left').bootstrapTable("removeByUniqueId", uniqueid) }oTableInit.InitDrag() } }) 这里代码和之前有点变化 (1)注册#div_tableright div[class=fixed-table-container]标签的droppable,这个标签是Bootstrap Table表格初始化后自动生成的,为什么不直接注册表格#tb_order_right的droppable,是因为这个标签作用域太小,会导致拖过来的tbody捕捉不到drop事件。而注册表格外部的#div_tableright div[class=fixed-table-container]这个div标签可以解决问题。 (2)通过var arrtr = $(ui.helper[0]).find("tr[class=selected]")找到拖过来tbody里面选中的行,然后将数据取出依次插入右边表格,左边表格则依次删除行数据。 (3)这里有点麻烦的是找drop的位置,我们知道,要想将左边选中的行放到右边指定的位置,那么就得得到当前鼠标drop的位置,这里通过ui.helper[0].offsetTop属性来获得鼠标的Y轴位置,通过计算得到要插入的位置。 3、撤销操作 $("#btn_cancel").click(function () {if (i_statuindex <= 0) {return }for (var i = 0i <arrdata.lengthi++) {if (arrdata[i].index != i_statuindex) { continue }var arr_left_data = eval(arrdata[i].left_data) var arr_right_data = eval(arrdata[i].right_data) $('#tb_order_left').bootstrapTable('removeAll') $('#tb_order_right').bootstrapTable('removeAll') $('#tb_order_left').bootstrapTable('append', arr_left_data) for (var x = 0x <arr_right_data.lengthx++) { $("#tb_order_right").bootstrapTable("insertRow", { index: x, row: arr_right_data[x] }) }//$('#tb_order_right').bootstrapTable('append', arr_right_data)//append之后不能dropbreak }i_statuindex-- //重写注册可拖拽m_oTable.InitDrag() //m_oTable.InitDrop() }) 撤销操作和之前也基本相同。 四、总结 效果是完成了,美中不足的是每次拖过去的都是整个tbody,而不是选中的行,奈何多个选中的行无法用某一个容器包起来。暂时没找到合适的解决方案。先这样吧,等以后想到好的方案了再优化吧。 五、优化方案 1、注册drap的方法 oTableInit.InitDrag = function () { $('#tb_order_left tbody').draggable({helper: "clone",start: function (event, ui) {var old_left_data = JSON.stringify($('#tb_order_left').bootstrapTable("getData")) var old_right_data = JSON.stringify($('#tb_order_right').bootstrapTable("getData")) var odata = { index: ++i_statuindex, left_data: old_left_data, right_data: old_right_data } arrdata.push(odata) $(ui.helper[0]).find("tr[class!=selected]").remove() },stop: function (event, ui) {} }) } 增加了这一句$(ui.helper[0]).find("tr[class!=selected]").remove()这样在拖动的时候就看不到未选中的行了。 2、精准定位到右边表格指定位置: oTableInit.InitDrop = function () { $("#div_tableright div[class=fixed-table-container]").droppable({drop: function (event, ui) {var arrtr = $(ui.helper[0]).find("tr[class=selected]") if (arrtr.length <= 0) { toastr.warning('请先选中要插单的行')return }var oTop = ui.helper[0].offsetTop //因为表格每行的高度可能不一致,所以这里取插入行位置的办法是:取右边表格的行高依次累加,计算行索引。var rowIndex = 0 var bIsBottom = true var iRowHeadHeight = 40 var addHeight = iRowHeadHeight var trs = $("#tb_order_right tbody tr") for (var i = 0i <trs.lengthi++) { addHeight += $(trs[i]).height()if (addHeight >oTop) { rowIndex = ibIsBottom = false//这里参数用来定义拖动到右边表格最下面的情况,这时没有进入到此条件判断里面。 break}}if (bIsBottom) { rowIndex = trs.length } for (var i = 0i <arrtr.lengthi++) { var arrtd = $(arrtr[i]).find("td")var uniqueid = $(arrtr[i]).attr("data-uniqueid")var rowdata = { ORDER_NO: $(arrtd[1]).text(), BODY_NO: $(arrtd[2]).text(), VIN: $(arrtd[3]).text(), TM_MODEL_MATERIAL_ID: $(arrtd[4]).text(), ORDER_TYPE: $(arrtd[5]).text(), ORDER_STATUS_NAME: $(arrtd[6]).text(), CREATE_DATE: $(arrtd[7]).text() == "-" ? null : $(arrtd[7]).text(), PLAN_DATE: $(arrtd[8]).text() == "-" ? null : $(arrtd[8]).text(), VMS_NO: $(arrtd[9]).text(), ENGIN_CODE: $(arrtd[10]).text(), TRANS_CODE: $(arrtd[11]).text(), OFFLINE_DATE_ACT: $(arrtd[12]).text() == "-" ? null : $(arrtd[12]).text(), HOLD_RES: $(arrtd[13]).text(), SPC_FLAG: $(arrtd[14]).text(), TO_ORDER_ID: uniqueid, ORDER_STATUS:0 }$("#tb_order_right").bootstrapTable("insertRow", { index: rowIndex++, row: rowdata })$('#tb_order_left').bootstrapTable("removeByUniqueId", uniqueid) } oTableInit.InitDrag() } }) } 因为每一行的行高不确定,是由行里面的数据动态撑出来的,所以这里也动态计算drop的位置。 至此,这个小的功能基本告一段落,基本的效果和待优化点也完成了。 源码下载:Bootstrap Table表格多行拖拽效果 以上就是本文的全部内容,希望本文所述对大家学习javascript程序设计有所帮助。如果要直接实现这种效果的话,
一个程序比较复杂,
第二个可能效果不会很理想,会出现很有拖拽或者运动中的bug!
你可以参考一下jquery ui里面的拖拽。
利用jquery ui里面的,即方便,有能有效的去除各种意料意外的情况
给你一个地址,你可以看一下参考一下
http://jqueryui.com/demos/droppable/#revert
Web前端实现像excel那样的按列拖选表格的方法:
1.通过flash,flex实现FLEX 功能强大的datagrid
2.通过jquery插件,js实现
JS实现可编辑的表格,双击可编辑,可以删除行和列,增加行和列,重置,导出表格,也可以上下移动元素
Flexigrid – Web 2.0 Javscript Grid for jQuery - 可变列宽,自动适应表头宽度,可通过 Ajax 连接 XML 数据源,类似 Ext Grid,但基于 jQuery 因此更轻量小巧。
Chromatable JQuery Plugin - 固定表头,可滚动内容区,内容区滚动的时候表头位置保持不变。
Ingrid, the jQuery Datagrid - 在 HTML 表格上加入列宽调整,分页,排序,行列式样等功能(演示)。
JQTreeTable - 在表格中加入树形结构
Scrollable HTML table - 将普通 HTML 表格变为可滚动状态。将表头部分放入 THEAD 区,内容部分放入 TBODY 区,脚注部分放入 TFOOT 区域,引用 webtoolkit.scrollabletable.js 文件,然后在每个表格后面创建 ScrollableTable() 对象即可(演示)。
KeyTable - 象 Excel 那样,在单元格之间巡游,可以现场编辑。
graphTable - 借助 flot 将 HTML 表格中的内容变成图形(演示)。
DataTables - 非常强大的 jQuery 表格插件,可变宽页码浏览,现场过滤。多列排序,自动探测数据类型,智能列宽,可从几乎任何数据源获取数据。