Recently due to company demand

You need to write Excel to merge cells, drag and drop to select cells, drag and drop to change cell size and other functions


Because the framework used by the company was written by one of the company’s bigwigs

Therefore, in order to be able to have good adhesion with the company’s project, it needs to use JS or jquery development


And on the net basically can not find the big guy to share the code, more is to ask others to use the plug-in

Although there is a user to share the code, but because the user’s code in many TD table will be stuck, poor performance, so not adopted

Then he decided to develop his own…


The sample


Making the address


Technical difficulties

  • tabletdThe problem with merging
  • Drag to selecttdIf you encounter the mergedtdYou need to decide if there are other optionstd


Implementation approach

  1. The firsttableTo an array, the merged cells need to be inserted as follows
// Merge represents the tag "< TD colSpan="3" rowSpan="3">123
      "

let table_arr = [
    [td, td, td, td, td, td, ....],
    [td, td, td, td, td, td, ....],
    [td, td, merge, merge, merge, td, ....],
    [td, td, merge, merge, merge, td, ....],
    [td, td, merge, merge, merge, td, ....],
    [td, td, td, td, td, td, ....],
    ....
]
Copy the code
  1. For eachtdTraversal sets the unique identifier, which I used heredatasetTo set the
// Set td unique identifier
function setTdSpan (table_arr) {
    setTimeout((a)= > {
        table_arr.forEach((row, row_i) = > {
            row.forEach((col, col_i) = > {
                let row_num = row_i
                let col_num = col_i
                if (col.rowSpan > 1) row_num = row_i - col.rowSpan + 1
                if (col.colSpan > 1) col_num = col_i - col.colSpan + 1
                $(col).attr('data-tdspan'.`${row_num}-${col_num}`)
                $(col).attr('data-endtdspan'.`${row_i}-${col_i}`)})})})}Copy the code
  1. Then according to the mouse press, move, bounce position, to obtain the celltdThe subscript
// Get the TD of the mouse position
function switchTd (params, fn) {
    let table = $('table') [0]
    let { x, y } = params

    table_arr.forEach((tr, tr_ind) = > {
        tr.forEach((td, td_ind) = > {
            if (tr_ind === 0 || td_ind === 0) return

            let td_start_left = td.offsetLeft
            let td_start_top = td.offsetTop

            let td_end_left = td_start_left + td.offsetWidth
            let td_end_top = td_start_top + td.offsetHeight

            if ((x >= td_start_left && x <= td_end_left) && (y >= td_start_top && y <= td_end_top)) {
                typeof fn === 'function' && fn({
                    tr_ind, td_ind, td,
                    end_tr_ind: subStrTdSpan(td.dataset.endtdspan, 'tr'),
                    end_td_ind: subStrTdSpan(td.dataset.endtdspan, 'td'),
                    left: table.offsetLeft + td_start_left,
                    top: table.offsetTop + td_start_top
                })
            }
        })
    })
}
Copy the code
// Drag and select
$(document).on('mousedown', (e) => {
    $('.select_area').css({'cursor': 'default'})
    let start_left = e.clientX + $(document).scrollLeft()
    let start_top = e.clientY + $(document).scrollTop()
    // It is used to determine whether the td is the same
    let tr_td = ' '
    // start/end TD subscript
    let tr_num = 0
    let td_num = 0

    switchTd({
        x: start_left - $('table').offset().left,
        y: start_top - $('table').offset().top
    }, (ind_data) => {
        tr_num = ind_data.tr_ind
        td_num = ind_data.td_ind
        if (tr_num === 0 || td_num === 0) return
        collisionCell({
            tr_num, td_num,
            tr_ind: ind_data.end_tr_ind,
            td_ind: ind_data.end_td_ind
        })
        tr_td = ind_data.td.dataset.tdspan
    })
    $(document).on('mousemove', (e1) => {
        e1.preventDefault()

        let move_left = e1.clientX + $(document).scrollLeft()
        let move_top = e1.clientY + $(document).scrollTop()

        switchTd({
            x: move_left - $('table').offset().left,
            y: move_top - $('table').offset().top
        }, (ind_data) => {
            if (tr_num === 0 || td_num === 0) return
            if (tr_td === ind_data.td.dataset.tdspan) return
            collisionCell({
                tr_num, td_num,
                tr_ind: ind_data.end_tr_ind,
                td_ind: ind_data.end_td_ind
            })
            tr_td = ind_data.td.dataset.tdspan
        })
    })
})
$(document).on('mouseup', () => {$()document).unbind('mousemove')
    setSelectTdArr()
    console.log(start_end_data)
    console.log(select_td_arr)
})
Copy the code
  1. And then you compute some subscripttdTo some subscripttdTo calculate the size and position of the selection box
// Collision mechanism 1, calculate the start and end positions
function collisionCell (params) {
    let { tr_num, td_num, tr_ind, td_ind } = params

    // Determine the start position and end position
    let start_tr_ind = tr_num <= tr_ind? tr_num: tr_ind
    let end_tr_ind = start_tr_ind === tr_num? tr_ind: tr_num
    let start_td_ind = td_num <= td_ind? td_num: td_ind
    let end_td_ind = start_td_ind === td_num? td_ind: td_num

    start_end_data = {
        s_tr: start_tr_ind, s_td: start_td_ind,
        e_tr: end_tr_ind, e_td: end_td_ind
    }

    comElementTdSpan({
        s_tr: start_tr_ind, s_td: start_td_ind,
        e_tr: end_tr_ind, e_td: end_td_ind
    })
}
Copy the code
// Collision mechanism 2, calculate the start and end index after collision mechanism
let select_td_arr2 = [] // Use to judge when selecting
function comElementTdSpan (params) {
    let { s_tr, s_td, e_tr, e_td } = params
    let std = table_arr[s_tr][s_td]
    let etd = table_arr[e_tr][e_td]
    let start_tr_ind = subStrTdSpan(std.dataset.tdspan, 'tr')
    let start_td_ind = subStrTdSpan(std.dataset.tdspan, 'td')
    let end_tr_ind = subStrTdSpan(etd.dataset.endtdspan, 'tr')
    let end_td_ind = subStrTdSpan(etd.dataset.endtdspan, 'td')
    select_td_arr2 = []
    
    let left = null, top = null, right = null, bottom = null
    for (let tr_ind = start_tr_ind; tr_ind <= end_tr_ind; tr_ind++) {
        for (let td_ind = start_td_ind; td_ind <= end_td_ind; td_ind++) {
            let td = table_arr[tr_ind][td_ind]
            select_td_arr2.push(td)
            
            let left_td = td.getBoundingClientRect().left
            let top_td = td.getBoundingClientRect().top
            let right_td = td.getBoundingClientRect().right
            letbottom_td = td.getBoundingClientRect().bottom left = left ! = =null? (left_td < left ? left_td : left) : left_td top = top ! = =null? (top_td < top ? top_td : top) : top_td right = right ! = =null? (right_td > right ? right_td : right) : right_td bottom = bottom ! = =null ? (bottom_td > bottom ? bottom_td : bottom) : bottom_td
        }
    }
    left = left + $(document).scrollLeft()
    top = top + $(document).scrollTop()
    right = right + $(document).scrollLeft()
    bottom = bottom + $(document).scrollTop()

    let s_tr2 = null, s_td2 = null, e_tr2 = null, e_td2 = null
    select_td_arr2.forEach((td) = > {
        let s_tr_ind2 = subStrTdSpan(td.dataset.tdspan, 'tr')
        let s_td_ind2 = subStrTdSpan(td.dataset.tdspan, 'td')
        let e_tr_ind2 = subStrTdSpan(td.dataset.endtdspan, 'tr')
        let e_td_ind2 = subStrTdSpan(td.dataset.endtdspan, 'td') s_tr2 = s_tr2 ! = =null? (s_tr2 < s_tr_ind2 ? s_tr2 : s_tr_ind2) : s_tr_ind2 s_td2 = s_td2 ! = =null? (s_td2 < s_td_ind2 ? s_td2 : s_td_ind2) : s_td_ind2 e_tr2 = e_tr2 ! = =null? (e_tr2 > e_tr_ind2 ? e_tr2 : e_tr_ind2) : e_tr_ind2 e_td2 = e_td2 ! = =null ? (e_td2 > e_td_ind2 ? e_td2 : e_td_ind2) : e_td_ind2
    })
    setSelectCss({
        left: left,
        top: top,
        width: right - left,
        height: bottom - top
    })
    if(s_tr ! == s_tr2 || s_td ! == s_td2 || e_tr ! == e_tr2 || e_td ! == e_td2) { start_end_data = {s_tr: s_tr2, s_td: s_td2,
            e_tr: e_tr2, e_td: e_td2
        }
        comElementTdSpan({
            s_tr: s_tr2, s_td: s_td2,
            e_tr: e_tr2, e_td: e_td2
        })
    }
}
Copy the code
  1. Then set the selection boxcssattribute
// Styles the marquee
let sel_html = `<div class="select_area"></div>`
$('body').append($(sel_html))
function setSelectCss(params) {
    let { width, height, left, top, css_obj } = params
    $('.select_area').css(Object.assign({
        'width': width + 'px'.'height': height + 'px'.'left': left + 'px'.'top': top + 'px'.'border': '2px solid rgb(47, 137, 220)'.'box-sizing': 'border-box'.'position': 'absolute'.'background-color': 'rgba (23, 133, 231, 0.1)'
    }, css_obj))
}
Copy the code
  1. Finally, the selected box is selectedtdIn the array
// Insert the selected TD into select_td_arr array
function setSelectTdArr () {
    let select_area = $('.select_area')
    select_td_arr = []
    $('table').find('td').each(function () {
        let t1 = $(this).offset().top
        let l1 = $(this).offset().left
        let r1 = $(this).offset().left + $(this).innerWidth()
        let b1 = $(this).offset().top + $(this).innerHeight()

        let t2 = select_area.offset().top
        let l2 = select_area.offset().left
        let r2 = select_area.offset().left + select_area.innerWidth()
        let b2 = select_area.offset().top + select_area.innerHeight()

        if (t2 < b1 && l2 < r1 && r2 > l1 && b2 > t1) {
            select_td_arr.push($(this) [0])}})}Copy the code


The full code can be viewed on Github


The end of the

Finally, you are welcome to point out the problem and learn together

Feel good can also give me a star, love you brother ^▽^