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");//清除多選

相關文章
相關標籤/搜索