jQuery仿excel表格实现单元格拆分合并功能

用html的table来布局,定制html模板,调用pd4ml生成要打印的pdf,为了方便添加了一个合并拆分单元格的方法,合并单元格来源于网络,可是有问题本身进行了修改。javascript

网上合并单元格源码以下:html

<table border="1">
	<script>
        var s = '';
        for (var i = 0; i < 10; i++) {
            s += '<tr>';
			for (var j = 0; j < 10; j++) {
				s += '<td>' + i + '-' + j + '</td>';
			}
			s += '</tr>';
        }
        document.write(s);
    </script>
</table>

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.min.js"></script>
<script>
    //须要的样式
    document.write('<style>.cannotselect{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;-khtml-user-select:none;user-select:none;}td.selected{background:#0094ff;color:#fff}</style>');
    //jQuery表格单元格合并插件,功能和excel单元格合并功能同样,而且能够保留合并后的全部单元格内容到第一个单元格中
    $.fn.tableMergeCells = function () {
        //***请保留原做者相关信息
        //***power by showbo,http://www.w3dev.cn
        return this.each(function () {
            var tb = $(this), startTD, endTD, MMRC = { startRowIndex: -1, endRowIndex: -1, startCellIndex: -1, endCellIndex: -1 };
            //初始化全部单元格的行列下标内容并存储到dom对象中
            tb.find('tr').each(function (r) { 
				$('td', this).each(function (c) { 
					$(this).data('rc', { r: r, c: c }); 
				});
			});
            //添加表格禁止选择样式和事件
            tb.addClass('cannotselect').bind('selectstart', function () { return false });
            //选中单元格处理函数
            function addSelectedClass() {
                var selected = false,  rc,t;
                tb.find('td').each(function () {
                    rc = $(this).data('rc');
                    //判断单元格左上坐标是否在鼠标按下和移动到的单元格行列区间内
                    selected = rc.r >= MMRC.startRowIndex && rc.r <= MMRC.endRowIndex && rc.c >= MMRC.startCellIndex && rc.c <= MMRC.endCellIndex;
                    if (!selected && rc.maxc) {//合并过的单元格,判断另外3(左下,右上,右下)个角的行列是否在区域内             
                        selected =
                            (rc.maxr >= MMRC.startRowIndex && rc.maxr <= MMRC.endRowIndex && rc.c >= MMRC.startCellIndex && rc.c <= MMRC.endCellIndex) ||//左下
                            (rc.r >= MMRC.startRowIndex && rc.r <= MMRC.endRowIndex && rc.maxc >= MMRC.startCellIndex && rc.maxc <= MMRC.endCellIndex) ||//右上
                            (rc.maxr >= MMRC.startRowIndex && rc.maxr <= MMRC.endRowIndex && rc.maxc >= MMRC.startCellIndex && rc.maxc <= MMRC.endCellIndex);//右下

                    }
                    if (selected)  this.className = 'selected';
                });
                var rangeChange = false;
                tb.find('td.selected').each(function () { //从已选中单元格中更新行列的开始结束下标
                    rc = $(this).data('rc');
                    t = MMRC.startRowIndex;
                    MMRC.startRowIndex = Math.min(MMRC.startRowIndex, rc.r);
                    rangeChange = rangeChange || MMRC.startRowIndex != t;

                    t = MMRC.endRowIndex;
                    MMRC.endRowIndex = Math.max(MMRC.endRowIndex, rc.maxr || rc.r);
                    rangeChange = rangeChange || MMRC.endRowIndex != t;

                    t = MMRC.startCellIndex;
                    MMRC.startCellIndex = Math.min(MMRC.startCellIndex, rc.c);
                    rangeChange = rangeChange || MMRC.startCellIndex != t;

                    t = MMRC.endCellIndex;
                    MMRC.endCellIndex = Math.max(MMRC.endCellIndex, rc.maxc || rc.c);
                    rangeChange = rangeChange || MMRC.endCellIndex != t;
                });
                //注意这里若是用代码选中过合并的单元格须要从新执行选中操做
                if (rangeChange) addSelectedClass();
            }
            function onMousemove(e) {//鼠标在表格单元格内移动事件
                e = e || window.event;
                var o = e.srcElement || e.target;
                if (o.tagName == 'TD') {
                    endTD = o;
                    var sRC = $(startTD).data('rc'), eRC = $(endTD).data('rc'), rc;
                    MMRC.startRowIndex = Math.min(sRC.r, eRC.r);
                    MMRC.startCellIndex = Math.min(sRC.c, eRC.c);
                    MMRC.endRowIndex = Math.max(sRC.r, eRC.r);
                    MMRC.endCellIndex = Math.max(sRC.c, eRC.c);
                    tb.find('td').removeClass('selected');
                    addSelectedClass();
                }
            }
            function onMouseup(e) {//鼠标弹起事件
                tb.unbind({ mouseup: onMouseup, mousemove: onMousemove });
                if (startTD && endTD && startTD != endTD && confirm('确认合并?!')) {//开始结束td不相同确认合并
                    var tds = tb.find('td.selected'), firstTD = tds.eq(0), index = -1, t, addBR
                        , html = tds.filter(':gt(0)').map(function () {
                            t = this.parentNode.rowIndex;
                            addBR = index != -1 && index != t;
                            index = t;
                            return (addBR ? '<br>' : '') + this.innerHTML
                        }).get().join(',');
                    tds.filter(':gt(0)').remove(); firstTD.append(',' + html.replace(/,(<br>)/g, '$1'));

                    //更新合并的第一个单元格的缓存rc数据为所跨列和行
                    var rc = firstTD.attr({ colspan: MMRC.endCellIndex - MMRC.startCellIndex + 1, rowspan: MMRC.endRowIndex - MMRC.startRowIndex + 1 }).data('rc');
                    rc.maxc = rc.c + MMRC.endCellIndex - MMRC.startCellIndex; rc.maxr = rc.r + MMRC.endRowIndex - MMRC.startRowIndex;
					console.info(rc.maxc);
					console.info(rc.maxr);
                    firstTD.data('rc', rc);

                }
                tb.find('td').removeClass('selected');
                startTD = endTD = null;
            }
            function onMousedown(e) {
                var o = e.target;
                if (o.tagName == 'TD') {
                    startTD = o;
                    tb.bind({ mouseup: onMouseup, mousemove: onMousemove });
                }
            }
            tb.mousedown(onMousedown);
        });
    };

    $('table').tableMergeCells();
</script>

他在初始化全部单元格的行列下标内容并存储到dom对象中是有问题的,他没有考虑到表格已存在跨行跨列的问题。java

修改初始化表格下标的方法以下:jquery

//给表格单元格标记索引
	function setTdIndex($table) {
		var $trs = $table.find("tr");
		//总行数
		var all_row = $trs.length;
		//总列数
		var all_col = 0;
		$trs.eq(0).children().each(function() {
			all_col += parseInt($(this).attr("colspan")) || 1;
		});
	
		//单元格索引数组,用于标记单元格对应的索引是否被占用
		var tdsIndex = [];
		for (var i = 0; i < all_row; i++) {
			tdsIndex[i] = new Array();
			for (var j = 0; j < all_col; j++) {
				tdsIndex[i][j] = 0;
			}
		}
		
		//单元格索引站位,为了获取当前行下一个单元格索引位置
		function tdsIndex_zw(i, j, colspan, rowspan) {
			for (var a = i; a < i + colspan; a++) {
				for (var b = j; b < j + rowspan; b++) {
					tdsIndex[b][a] = 1;
				}
			}
		}
	
		//获取第n行下一个单元格的索引
		function getTdIndex(n) {
			for (var i = 0; i < all_col; i++) {
				if (tdsIndex[n][i] == 0) {
					return i;
					continue;
				}
			}
		}
	
		$trs.each(function(i) {
			$(this).children().each(function(j) {
				//td的索引,即td的x坐标
				var x = getTdIndex(i); 
				var rc = {r: i, c: x };
				$(this).data('rc', rc);
				var td_colspan = parseInt($(this).attr("colspan") || 1);
				var td_rowspan = parseInt($(this).attr("rowspan") || 1);
				//在对象位置数组中站位
				tdsIndex_zw(x, i, td_colspan, td_rowspan); 
				//设置跨行跨列信息(单元格合并信息)
				if(td_rowspan >1){
					rc.maxr = i + td_rowspan -1;
				}
				if(td_colspan >1){
					rc.maxc = x + td_colspan -1;
				}
			});
		});
	}

修改后拆分单元格代码以下:web

<script type="text/javascript">
	//给表格单元格标记索引
	function setTdIndex($table) {
		var $trs = $table.find("tr");
		//总行数
		var all_row = $trs.length;
		//总列数
		var all_col = 0;
		$trs.eq(0).children().each(function() {
			all_col += parseInt($(this).attr("colspan")) || 1;
		});
	
		//单元格索引数组,用于标记单元格对应的索引是否被占用
		var tdsIndex = [];
		for (var i = 0; i < all_row; i++) {
			tdsIndex[i] = new Array();
			for (var j = 0; j < all_col; j++) {
				tdsIndex[i][j] = 0;
			}
		}
		
		//单元格索引站位,为了获取当前行下一个单元格索引位置
		function tdsIndex_zw(i, j, colspan, rowspan) {
			for (var a = i; a < i + colspan; a++) {
				for (var b = j; b < j + rowspan; b++) {
					tdsIndex[b][a] = 1;
				}
			}
		}
	
		//获取第n行下一个单元格的索引
		function getTdIndex(n) {
			for (var i = 0; i < all_col; i++) {
				if (tdsIndex[n][i] == 0) {
					return i;
					continue;
				}
			}
		}
	
		$trs.each(function(i) {
			$(this).children().each(function(j) {
				//td的索引,即td的x坐标
				var x = getTdIndex(i); 
				var rc = {r: i, c: x };
				$(this).data('rc', rc);
				var td_colspan = parseInt($(this).attr("colspan") || 1);
				var td_rowspan = parseInt($(this).attr("rowspan") || 1);
				//在对象位置数组中站位
				tdsIndex_zw(x, i, td_colspan, td_rowspan); 
				//设置跨行跨列信息(单元格合并信息)
				if(td_rowspan >1){
					rc.maxr = i + td_rowspan -1;
				}
				if(td_colspan >1){
					rc.maxc = x + td_colspan -1;
				}
			});
		});
	}

    //须要的样式
    document.write('<style>.cannotselect{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;-khtml-user-select:none;user-select:none;}td.selected{background:#0094ff;color:#fff}</style>');
    //jQuery表格单元格合并插件,功能和excel单元格合并功能同样,而且能够保留合并后的全部单元格内容到第一个单元格中
    $.fn.tableMergeCells = function () {
        //***请保留原做者相关信息
        //***power by showbo,http://www.w3dev.cn
        return this.each(function () {
            var tb = $(this), startTD, endTD, MMRC = { startRowIndex: -1, endRowIndex: -1, startCellIndex: -1, endCellIndex: -1 };
            //初始化全部单元格的行列下标内容并存储到dom对象中
            /* tb.find('tr').each(function (r) { 
            	$('td', this).each(function (c) { 
            		$(this).data('rc', { r: r, c: c }); 
					console.info($(this).data('rc'));
            	});  
            }); */
            setTdIndex(tb);
            //添加表格禁止选择样式和事件
            tb.addClass('cannotselect').bind('selectstart', function () { return false });
            //选中单元格处理函数
            function addSelectedClass() {
                var selected = false,  rc,t;
                tb.find('td').each(function () {
                    rc = $(this).data('rc');
                    //判断单元格左上坐标是否在鼠标按下和移动到的单元格行列区间内
                    selected = rc.r >= MMRC.startRowIndex && rc.r <= MMRC.endRowIndex && rc.c >= MMRC.startCellIndex && rc.c <= MMRC.endCellIndex;
                    if (!selected && rc.maxc) {//合并过的单元格,判断另外3(左下,右上,右下)个角的行列是否在区域内             
                        selected =
                            (rc.maxr >= MMRC.startRowIndex && rc.maxr <= MMRC.endRowIndex && rc.c >= MMRC.startCellIndex && rc.c <= MMRC.endCellIndex) ||//左下
                            (rc.r >= MMRC.startRowIndex && rc.r <= MMRC.endRowIndex && rc.maxc >= MMRC.startCellIndex && rc.maxc <= MMRC.endCellIndex) ||//右上
                            (rc.maxr >= MMRC.startRowIndex && rc.maxr <= MMRC.endRowIndex && rc.maxc >= MMRC.startCellIndex && rc.maxc <= MMRC.endCellIndex);//右下

                    }
                    if (selected)  this.className = 'selected';
                });
                var rangeChange = false;
                tb.find('td.selected').each(function () { //从已选中单元格中更新行列的开始结束下标
                    rc = $(this).data('rc');
                    t = MMRC.startRowIndex;
                    MMRC.startRowIndex = Math.min(MMRC.startRowIndex, rc.r);
                    rangeChange = rangeChange || MMRC.startRowIndex != t;

                    t = MMRC.endRowIndex;
                    MMRC.endRowIndex = Math.max(MMRC.endRowIndex, rc.maxr || rc.r);
                    rangeChange = rangeChange || MMRC.endRowIndex != t;

                    t = MMRC.startCellIndex;
                    MMRC.startCellIndex = Math.min(MMRC.startCellIndex, rc.c);
                    rangeChange = rangeChange || MMRC.startCellIndex != t;

                    t = MMRC.endCellIndex;
                    MMRC.endCellIndex = Math.max(MMRC.endCellIndex, rc.maxc || rc.c);
                    rangeChange = rangeChange || MMRC.endCellIndex != t;
                });
                //注意这里若是用代码选中过合并的单元格须要从新执行选中操做
                if (rangeChange) addSelectedClass();
            }
            function onMousemove(e) {//鼠标在表格单元格内移动事件
                e = e || window.event;
                var o = e.srcElement || e.target;
                if (o.tagName == 'TD') {
                    endTD = o;
                    var sRC = $(startTD).data('rc'), eRC = $(endTD).data('rc'), rc;
                    MMRC.startRowIndex = Math.min(sRC.r, eRC.r);
                    MMRC.startCellIndex = Math.min(sRC.c, eRC.c);
                    MMRC.endRowIndex = Math.max(sRC.r, eRC.r);
                    MMRC.endCellIndex = Math.max(sRC.c, eRC.c);
                    tb.find('td').removeClass('selected');
                    addSelectedClass();
                }
            }
            function onMouseup(e) {//鼠标弹起事件
                tb.unbind({ mouseup: onMouseup, mousemove: onMousemove });
            }
            function hbdyg(){//合并单元格
            	if (startTD && endTD && startTD != endTD) {//开始结束td不相同确认合并
                    var tds = tb.find('td.selected'), firstTD = tds.eq(0), index = -1, t, addBR
                        , html = tds.filter(':gt(0)').map(function () {
                            t = this.parentNode.rowIndex;
                            addBR = index != -1 && index != t;
                            index = t;
                            return (addBR ? '<br>' : '') + this.innerHTML;
                        }).get().join(',');
                    tds.filter(':gt(0)').remove(); 
                  
                    //firstTD.append(',' + html.replace(/,(<br>)/g, '$1'));

                    //更新合并的第一个单元格的缓存rc数据为所跨列和行
                    //console.log(firstTD.data('rc'));
                    var rc = firstTD.attr({ colspan: MMRC.endCellIndex - MMRC.startCellIndex + 1, rowspan: MMRC.endRowIndex - MMRC.startRowIndex + 1 }).data('rc');
                    rc.maxc = rc.c + MMRC.endCellIndex - MMRC.startCellIndex; rc.maxr = rc.r + MMRC.endRowIndex - MMRC.startRowIndex;

                    firstTD.data('rc', rc);
					//alert("合并完成!");
                }
            	//清除多选
            	qcdx();
            }
            function qcdx(){//清除多选
            	tb.find('td').removeClass('selected');
                startTD = endTD = null;
            }
            tb.on("hbdyg",hbdyg);
            tb.on("qcdx",qcdx);
            function onMousedown(e) {//鼠标按下事件
                var o = e.target;
                if (o.tagName == 'TD') {
                    startTD = o;
                    tb.bind({ mouseup: onMouseup, mousemove: onMousemove });
                }
                return false;
            }
            tb.mousedown(onMousedown);
        });
    };

</script>

调用合并的方法以下:ajax

var selectTdTables = $('#dm_view table').tableMergeCells();

//合并单元格
function mm_hbdyg(){
	selectTdTables.each(function () {
		$(this).trigger("hbdyg");
	});
}

拆分单元格代码以下:数组

//拆分单元格
function mm_cfdyg(select_dom_id){
	var $td = $("#"+select_dom_id);//当前选中的td
	var $tb = $td.parents('table:first');//td所在的table
	var $trs = $tb.find("tr");//table下全部的行
	var rc = $td.data("rc");//单元格下标信息
	//console.info(rc);
	var rowIndex = rc.r;
	var colIndex = rc.c;
	var td_rowspan = parseInt($td.attr("rowspan") || 1);
	var td_colspan = parseInt($td.attr("colspan") || 1);
	if(td_rowspan == 1 && td_colspan == 1){
		return;
	}
	for(var i=rowIndex;i<rowIndex + td_rowspan;i++){//循环行
		$trs.eq(i).children().each(function(n){//循环单元格
			var td_rc = $(this).data("rc");//单元格下标
			if(td_rc.c >= colIndex){//当前td索引(下标)大于或等于合并单元格的索引时,取前一个td,取不到前一个直接用当前的
                //上一个单元格
                var $sygdyg = $(this).prev().length == 0 ? $(this) : $(this).prev();
				for(var j=colIndex;j<colIndex + td_colspan;j++){
					var $newTd = $("<td></td>");
                    $sygdyg .after($newTd);
				} 
				return false;
			}
		});
	}
	$td.remove();
	setTdIndex($tb);//删除后须要从新设置td下标
}

点击单元格时取消多选方法:缓存

$("#"+dom_id).parents('table:first').trigger("qcdx");//清除多选

相关文章
相关标签/搜索