js 自定义表格的合并单元格、插入行列、删除行列

第一步,给表格添加事件

    export var finalCollsArr = null // 用来存放单元格的id数组
    let flag = false// 当鼠标被按下时,为true,放开是为true
    let indexs = []// 用来存放鼠标经过的单元格在整个表格的位置,鼠标按下时被初始化,
    table.addEventListener('mousedown', (e) => {
        getTbList(table) // 获取表格的单元格id
        if (e.button == 0) {
            flag = true
            indexs = []
        }


    }, false)

    tableNode.addEventListener('mousemove', (e) => {
        if (flag) { // 只有鼠标被按下时,才会执行复合代码
            search(e.target, indexs)
        }
    }, false)

    tableNode.addEventListener('mouseup', () => {
        flag = false
        finalCollsArr = collsDistribute(indexs)

    }, false)

第二步:获取单元格在表格中id分布,获取鼠标选中的单元格id分布

// 整个单元格的id分布,在删除行列,插入行列中有使用到
export var list = new Array()

/**
 * 获取单元格id
 * @param {表格节点} curTable 
 */
export const getTbList = (curTable) => {
    if (!curTable) {
        return
    }
    const rowLength = curTable.rows.length 
    const colLength = getCellLength(curTable)
    // let arr = new Array(colLength).fill('undefined');
    // list = new Array(rowLength).fill(arr);
    list = new Array()
    for (let i = 0; i < rowLength; i++) {
        let arr = []
        for (let j = 0; j < colLength; j++) {
            arr.push('undefined')
        }
        list.push(arr)
    }

    let rowNum = 0;
    let colNum = 0

    for (let i = 0; i < rowLength; i++) {
        for (let j = 0; j < colLength; j++) {
            // 获取单元格
            let tdNode = curTable.rows[rowNum].cells[colNum]
            if (!tdNode) {
                continue
            }
            for (let k = j; k < colLength; k++) {
                if (list[i][k] != 'undefined' ) {
                    continue
                }
                list[i][k] = tdNode.getAttribute('id')
                j = k;
                break
            }

            let rowspan = tdNode.rowSpan
            let colspan = tdNode.colSpan


            if (rowspan>1 && colspan>1) {
                for (let k = 0; k < rowspan; k++) {
                    for (let z = 0; z < colspan; z++) {
                        list[i+k][j+z] = tdNode.getAttribute('id')
                    }
                }
            }else if(rowspan>1 || colspan>1){
                for (let k = 0; k < rowspan; k++) {
                    list[i+k][j] = tdNode.getAttribute('id')
                }
                for (let k = 0; k < colspan; k++) {
                    list[i][j+k] = tdNode.getAttribute('id')
                }
            }
            colNum++;
        }
        rowNum++;
        colNum = 0;
    }
    console.log("list: ", list);
    // 第一行 id
    oneRowId = list[0];
    let newArray = list[0].map(function (col, i) {
        return list.map(function (row) {
            return row[i]
        })
    })
    // 第一列 id
    oneColId = newArray[0]
}


/**
 * 将鼠标经过的单元格添加到数组
 * @param {单元格} tdNode 
 * @param {数组} indexs 
 * @returns 
 */
 export const search = (tdNode, indexs) => {
    if (tdNode.getAttribute('tagname') != 'entry') {
        return
    }
    let id = tdNode.getAttribute('id')
    let ind = indexs.indexOf(id)
    if (ind == -1) {
        // 不存在
        indexs.push(id)
    } else if (ind != -1 && ind != indexs.length - 1) {
        indexs.splice(ind, 1)
    }
}

/**
 * 获取鼠标选中的单元格分布
 * @param {数组} indexs 
 */
export const collsDistribute = (indexs) => {
    let collsArr = new Array()
    let firstId = indexs[0]
    let endId = indexs[indexs.length-1]
    let arrRow = new Array();
    let arrCol = new Array();
    // 获取id在list数组的第几行第几列
    let row = 0
    let col = 0
    for (let i = 0; i < list.length; i++) {

        list[i].forEach((id,index)=>{
            if(id === firstId){
                arrRow[row++] = i
                arrCol[col++] = index
            }
        });

        list[i].forEach((id,index)=>{
            if(id === endId){
                arrRow[row++] = i
                arrCol[col++] = index
            }
        });
    }
    arrRow.sort()
    arrCol.sort()

    let minCols = Math.min(arrCol[0], arrCol[arrCol.length-1]) 
    let maxCols = Math.max(arrCol[0], arrCol[arrCol.length-1]) 
    let minRows = Math.min(arrRow[0], arrRow[arrRow.length-1])
    let maxRows = Math.max(arrRow[0], arrRow[arrRow.length-1])
    let x = maxRows-minRows+1
    let y = maxCols-minCols+1

    for (let i = 0; i < x; i++) {
        let arr = []
        for (let j = 0; j < y; j++) {
            arr.push('false')
        }
        collsArr.push(arr)
    }
    // let arr = new Array(y).fill('false')
    // let collsArr = new Array(x).fill(arr)

    for (let i = 0; i < x; i++) {
        console.log("minRows: " + minRows);
        for (let j = 0; j < y; j++) {
            console.log("minCols: " + minCols);
            collsArr[i][j] = list[minRows][minCols++]
            if (minCols>maxCols) {
                break
            }
        }
        minCols = maxCols + 1 - y
        minRows++
        if (minRows>maxRows) {
            break
        }
    }

    // console.log("===================");
    // console.log(collsArr);
    
    return collsArr;

}

/**
 * 获取表格的列数
 * @param {表格节点} curTable 
 * @returns 
 */
 export const getCellLength = (curTable) => {
    let rowNode = curTable.rows[0]
    let tdNodeList = rowNode.getElementsByTagName('td')
    let length = 0
    for (let i = 0; i < tdNodeList.length; i++) {
        let rowspan = tdNodeList[i].getAttribute('colspan')
        if (rowspan) {
            length += Number(rowspan)
        } else {
            length++
        }
    }
    return length
}

/**
 * 获取单元格所在的列数
 * @param {单元格} td 
 * @param {列数组} arr 
 */
export const getCellsTrain = (td, arr) => {

    let id = td.getAttribute('id')

    // 列位置
    let col = -1
    for (let i = 0; i < arr.length; i++) {
        if(arr[i].indexOf(id) != -1){
            col = i
            break
        }
    }

    return col
}

第三步:合并单元格

/**
 * 合并单元格
 * @param {单元格id数组,第一步中的finalCollsArr} arr 
 */
export const mergeCells = (arr) => {

    // 获取行列值最小的单元格
    let id = arr[0][0]
    let td = document.getElementById(id)
    // 行和列
    let colspan = arr[0].length
    let rowspan = arr.length
    td.setAttribute('colspan', colspan)
    td.setAttribute('rowspan', rowspan)

    for (let i = 0; i < rowspan; i++) {
        for (let j = 0; j <colspan; j++) {

            // 相同的id跳过
            if (arr[i][j] == id) {
                continue
            }
            let delColls = document.getElementById(arr[i][j])
            // 不存在的单元格跳过
            if (!delColls) {
                continue
            }
            // 删除单元格
            delColls.parentNode.removeChild(delColls)
        }
    }
}

第四步:插入行

/**
 * 插入行
 * @param {单元格} colls 
 * @param {表格} curTable 
 * @param {true:向上插入, false:向下插入} isUp 
 */
export const addRow = (colls, curTable, isUp) => {

    // 获取单元格所在行数
    let row = colls.parentNode.rowIndex; // 行位置
    let rowNode = colls.parentNode // 行节点
    // 获取总列数
    let cellLength = getCellLength(curTable)

    let arrId = list[row]

    // 判断上(下)一行有没有这一行的元素
    // 在第一行上方插入和在最后一行下方插入就不用判断

    if (!((isUp && row == 0) || (!isUp && row == list.length-1))) {
        let arr = isUp ? list[row-1] : list[row+1]
        let redoArr = []
        for (let i = 0; i < arrId.length; i++) {
            if (arrId[i] == arr[i]) {
                cellLength--
                if (redoArr.indexOf(arrId[i]) == -1) {
                    redoArr.push(arrId[i])
                }
            } 
        }

        for (let i = 0; i < redoArr.length; i++) {
            let td = document.getElementById(redoArr[i])
            let rowspan = td.rowSpan
            td.rowSpan = rowspan + 1
        }
    }

    const trNode = document.createElement('tr')
    trNode.setAttribute("tagname", "row");   // 设置属性
    trNode.setAttribute("id", `tag_${global.id}`);
    global.id = global.id + 1

    let tdHtml = ''
    for (let j = 0; j < cellLength; j++) {
        tdHtml += `<td tagname='entry' id='tag_${global.id}' style="padding: 8px; line-height: 1.42857; vertical-align: top; border: 1px solid rgb(221, 221, 221);">&nbsp;</td>`
        global.id = global.id + 1
    }
    trNode.innerHTML = tdHtml
    if (isUp) {
        rowNode.parentNode.insertBefore(trNode, rowNode)
    }else{
        rowNode.parentNode.insertBefore(trNode, rowNode.nextSibling)
    }
}

第五步:插入列

/**
 * 插入列
 * @param {单元格} colls 
 * @param {true:向左插入, false:向右插入} isLeft 
 * @returns 
 */
export const addCell = (colls, isLeft) => {
    
    // const rowLength = curTable.rows.length  // 行數量

    // 行转列
    let newArray = list[0].map(function (col, i) {
        return list.map(function (row) {
           return row[i]
        })
    })
    // 获取单元格所在的列
    let col = getCellsTrain(colls, newArray)
    if (col == -1) {
        return
    }
    // 这一列的id
    let arrId = newArray[col]
    let arr = []
    if (!((isLeft && col == 0) || (!isLeft && col == newArray.length-1))) {
        arr = isLeft ? newArray[col-1] : newArray[col+1]
    }

    let redoArr = []
 
    for (let i = 0; i < arrId.length; i++) {
        let td = document.getElementById(arrId[i])

        // 创建单元格节点
        let newCell = document.createElement('td')
        newCell.setAttribute("tagname", "entry")
        newCell.setAttribute("id", `tag_${global.id}`)
        global.id = global.id + 1
        newCell.setAttribute("style", "padding: 8px; line-height: 1.42857; vertical-align: top; border: 1px solid rgb(221, 221, 221);")
        newCell.innerHTML = '&nbsp;'

        if (arr.length>0) {
            if (arr[i] == arrId[i]) {
                if (redoArr.indexOf(arrId[i]) == -1) {
                    let colspan = td.colSpan
                    td.colSpan = colspan + 1
                    redoArr.push(arrId[i])
                }
                continue
            }
            let tdBro = document.getElementById(arr[i])
            if (Array.from(new Set(arrId)).length < arrId.length) {
                if (td.parentNode.rowIndex != tdBro.parentNode.rowIndex) {
                    if (isLeft) {
                        td = tdBro.nextSibling
                    }else{
                        td = tdBro.previousSibling
                    }
                }
            }
            
        }else {
            if (td.parentNode.rowIndex != i) {
                if (col == 0) {
                    td = document.getElementById(newArray[col + 1][i])
                    td.parentNode.insertBefore(newCell, td)
                } else {
                    td = document.getElementById(newArray[col - 1][i])
                    td.parentNode.appendChild(newCell)
                }
                continue
            }
        }

        if (isLeft) {
            !td ? curTable.rows[i].appendChild(newCell) : td.parentNode.insertBefore(newCell, td)
        } else {
            td.parentNode.insertBefore(newCell, td.nextSibling)
        }
    }

}

第六步:删除行

// colls: 单元格节点, curTable:表格节点
export const delRow = (colls, curTable) => {
    const rowLength = curTable.rows.length  // 行數量
    if (rowLength < 2) {
        alert('不能全部删除!')
        return
    }
    let row = colls.parentNode.rowIndex; // 行位置
    // let col = colls.cellIndex; // 列位置

    let rowNode = colls.parentNode // 行节点
    let tdNodeList = rowNode.getElementsByTagName('td')

    // 遍历这一行的单元格有没有跨行的,  如果跨行就将下一行的这一列的单元格跨行-1,相同跨列
    for (let i = 0; i < tdNodeList.length; i++) {
        let rowspan = tdNodeList[i].rowSpan
        if (rowspan>1) {
            let colspan = tdNodeList[i].colSpan
            let rowChild = curTable.rows[row+1]
            let colChild = rowChild.cells[i]
            let newCell = document.createElement('td')
            newCell.setAttribute("tagname", "entry")
            newCell.setAttribute("id", `tag_${global.id}`)
            global.id = global.id + 1
            newCell.setAttribute("rowspan", rowspan-1)
            newCell.setAttribute("colspan", colspan)
            newCell.setAttribute("style", "padding: 8px; line-height: 1.42857; vertical-align: top; border: 1px solid rgb(221, 221, 221);")
            newCell.innerHTML = '&nbsp;'
            rowChild.insertBefore(newCell, colChild)
        }
    }

    // 遍历有没有被跨行的,将被跨行的跨行数-1
    let arrId = list[row]
    for (let i = 0; i < arrId.length; i++) {
        let td = document.getElementById(arrId[i]) 
        let tdRow = td.parentNode.rowIndex;
        if (row == tdRow) {
            continue
        }
        let rowNum = td.rowSpan
        td.rowSpan = rowNum -1
    }

    rowNode.parentNode.removeChild(rowNode)

}

第七步:删除列

// colls: 单元格节点, curTable:表格节点
export const delCell = (colls, curTable) => {

    const cellLength = getCellLength(curTable)
    if (cellLength < 2) {
        alert('不能全部删除!')
        return
    }

    // 行转列
    let newArray = list[0].map(function (col, i) {
        return list.map(function (row) {
            return row[i]
        })
    })
    let col = getCellsTrain(colls, newArray)
    if (col == -1) {
        return
    }
    
    // 这一列的值
    let arrId = newArray[col]
    
    for (let i = 0; i < arrId.length; i++) {
        let td = document.getElementById(arrId[i]) 
        let tdCol = getCellsTrain(td, newArray)
        // 判断单元格有没有被跨列,有,将跨列的 跨列-1
        if (tdCol != col) {
            let colNum = td.colSpan
            td.colSpan = colNum -1
            continue
        }
        // 判断单元格有没有跨列,有跨列,在该单元格的后面添加一个单元格 跨列-1
        let colspan = td.colSpan
        if (colspan>1) {
            let tdNext = td.nextSibling
            let rowspan = td.rowSpan
            let newCell = document.createElement('td')
            newCell.setAttribute("tagname", "entry")
            newCell.setAttribute("id", `tag_${global.id}`)
            global.id = global.id + 1
            newCell.setAttribute("rowspan", rowspan)
            newCell.setAttribute("colspan", colspan-1)
            newCell.setAttribute("style", "padding: 8px; line-height: 1.42857; vertical-align: top; border: 1px solid rgb(221, 221, 221);")
            newCell.innerHTML = '&nbsp;'
            td.parentNode.insertBefore(newCell, tdNext)
        }

        td.parentNode.removeChild(td)
    }
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值