赞
踩
表格合并基础篇
本篇是在上一章的基础上实现,实现了的功能有添加行、删除行、逆向选区、取消合并功能。
添加行分为在上面添加和在下面追加行。
利用 insertAdjacentElement 方法实现,该方法可以实现从前插入元素和从后插入元素。
删除当前行就是利用元素remove()方法,从dom树种删除元素。
逆向选区是指选区从下往上选。
解决思路:记录当前选区时鼠标移动方向,往左上移动则为负,往右下移动则为正。负时在首位插入选中节点,正时从尾部追加选中节点,这样合并只需取第一个选中节点即可。
获取当前元素的rowspan和colspan属性值,然后遍历后面的和下面包含行节点,删除节点的hide类,然后删除当前元素rowspan和colspan属性即可。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>表格合并</title> <style> .zsk-table { border-collapse: collapse; border: 1px solid; font-family: inherit; user-select: none; } .zsk-table tr { height: 32px; } .zsk-table td { border: 1px solid; height: 32px; padding: 16px; } .amount { width: 100px; } .show-box { position: absolute; top: -200px; left: -200px; width: 200px; background-color: #eee; } .show-box>div { width: 200px; height: 50px; line-height: 50px; border-bottom: 1px solid #000; } .show-box>div:hover { background-color: #ccc; cursor: pointer; } .select { color: #fff; background-color: #3987cf; } .hide { display: none; } </style> </head> <body> <h1>表格合并</h1> <table tabindex="1" class="zsk-table"> <tr> <td>1-1</td> <td>1-2</td> <td>1-3</td> <td>1-4</td> <td>1-5</td> </tr> <tr> <td>2-1</td> <td>2-2</td> <td>2-3</td> <td>2-4</td> <td>2-5</td> </tr> <tr> <td>3-1</td> <td>3-2</td> <td>3-3</td> <td>3-4</td> <td>3-5</td> </tr> <tr> <td>4-1</td> <td>4-2</td> <td>4-3</td> <td>4-4</td> <td>4-5</td> </tr> <tr> <td>5-1</td> <td>5-2</td> <td>5-3</td> <td>5-4</td> <td>5-5</td> </tr> </table> <!-- 表格右键 --> <div class="show-box"> <div class="add-down">向下添加一行</div> <div class="add-up">向上添加一行</div> <div class="delete-cell">删除当前行行</div> <div class="merge-cell">合并</div> <div class="split-cell">取消合并</div> </div> <script> const table = document.querySelector('.zsk-table') const showBox = document.querySelector('.show-box') const mergeDiv = document.querySelector('.merge-cell') const addDownDiv = document.querySelector('.add-down') const addUpDiv = document.querySelector('.add-up') const deleteCellDiv = document.querySelector('.delete-cell') const cancelDiv = document.querySelector('.split-cell') const select = { // 选中单元格 value: [[]], range: [[], []] // [start,end]范围 } // 向下添加一行 addDownDiv.addEventListener('click', (e) => { let node = select.value[0][0].parentElement let newNode = node.cloneNode(true) node.insertAdjacentElement('afterend', newNode) clearSelect() }) // 向上添加一行 addUpDiv.addEventListener('click', (e) => { let node = select.value[0][0].parentElement let newNode = node.cloneNode(true) node.insertAdjacentElement('beforebegin', newNode) clearSelect() }) // 删除当前行 deleteCellDiv.addEventListener('click', () => { let node = select.value[0][0].parentElement node.remove() clearSelect() }) // 取消合并 cancelDiv.addEventListener('click', () => { let node = select.value[0][0] let rowspan = node.getAttribute('rowspan') let colspan = node.getAttribute('colspan') if (!colspan || !rowspan) return colspan = Number.parseInt(colspan) rowspan = Number.parseInt(rowspan) let index = getChildIndex(node) let nextNode = node for (let i = 0; i < rowspan; i++) { let col = colspan let temp = nextNode while (col--) { temp.classList.remove('hide') temp = temp.nextElementSibling } nextNode = getRowXElement(nextNode, 1) } node.removeAttribute('colspan') node.removeAttribute('rowspan') }) // 合并命令 mergeDiv.addEventListener('click', () => { if (select.value.length === 0) return // 默认是正向选中,即结尾点比开始点的x和y都大 select.value.forEach((item, i) => { item.forEach((v, k) => { if (i === 0 && k === 0) { v.setAttribute('colspan', item.length || '1') v.setAttribute('rowspan', select.value.length || '1') } else { v.classList.add('hide') } }) }) clearSelect() }) // 右键 table.addEventListener('click', (e) => { e.target.focus() }) table.addEventListener("contextmenu", (e) => { e.preventDefault() showBox.style.left = e.clientX + 'px' showBox.style.top = e.clientY + 'px' if (!select.value[0][0]) { select.value[0][0] = e.target } }) table.addEventListener('blur', (e) => { setTimeout(() => { showBox.style.left = -1000 + 'px' showBox.style.top = -1000 + 'px' }, 150) }) /** * 选中逻辑 * **/ selectLogic(table, select) function selectLogic(table, select) { let lastEnd = [0, 0] // 最后选中的单元格位置 let lastInfo = [0, 0] // 最后选中单元格的宽高 let endUp = [0, 0] let startRange = [0.0] let endRange = [0, 0] let run = false // 按下 let timer = 0 table.addEventListener('mousedown', (e) => { if (timer !== 0) { clearTimeout(timer) timer = 0 } timer = setTimeout(() => { // 先清空 clearSelect() run = true startRange = [e.clientX - e.offsetX, e.clientY - e.offsetY] lastEnd = [startRange[0], startRange[1]] lastInfo = [e.target.offsetWidth, e.target.offsetHeight] e.target.classList.add('select') if (e.target.tagName === 'TD') { select.value[0].push(e.target) select.range[0] = startRange select.range[1] = [startRange[0] + e.target.offsetWidth, startRange[1] + e.target.offsetHeight] } }, 200) }) // 移动 table.addEventListener('mousemove', (e) => { if (run) { end = [e.clientX, e.clientY] // 计算范围 然后 判断是否修改选中dom数组 let x = end[0] - lastEnd[0] let y = end[1] - lastEnd[1] let xDirection = end[0] - startRange[0] > 0 ? 1 : -1 // x方向 let yDirection = end[1] - startRange[1] > 0 ? 1 : -1 // y方向 console.log(`x: ${x} y: ${y} 方向x:${xDirection} 方向y:${yDirection}`); if ((xDirection === 1 && x >= lastInfo[0]) || (xDirection === -1 && x <= 0)) { console.log('横向超出,x扩展'); // 更新选取范围 x if (xDirection === 1) { lastEnd = [select.range[1][0], lastEnd[1]] lastInfo = [e.target.offsetWidth, lastInfo[1]] select.range[1] = [select.range[1][0] + e.target.offsetWidth, select.range[1][1]] } else { select.range[0] = [select.range[0][0] - e.target.offsetWidth, select.range[0][1]] lastEnd = [select.range[0][0], lastEnd[1]] lastInfo = [e.target.offsetWidth, lastInfo[1]] } // 每行横向添加一行 for (let i = 0; i < select.value.length; i++) { // 查找最后一个节点元相邻td元素 console.log(select.value[i]); if (xDirection === 1) { let el = getElement(select.value[i][select.value[i].length - 1], xDirection) select.value[i].push(el) } else { let el = getElement(select.value[i][0], xDirection) select.value[i].unshift(el) } } } else if ((xDirection === 1 && x <= 0) || (xDirection === -1 && x >= lastInfo[0])) { if (select.value[0].length <= 1) return console.log(select.value[0].length, '当前个数'); if (xDirection === 1) { select.range[1] = [lastEnd[0], select.range[1][1]] lastEnd = [lastEnd[0] - e.target.offsetWidth, lastEnd[1]] lastInfo = [e.target.offsetWidth, lastInfo[1]] } else { select.range[0] = [lastEnd[0] + e.target.offsetWidth, select.range[0][1]] lastEnd = [select.range[0][0], lastEnd[1]] lastInfo = [e.target.offsetWidth, lastInfo[1]] } // 减去每行的最后一个 for (let i = 0; i < select.value.length; i++) { if (select.value[i].length > 0) { if (xDirection === 1) { select.value[i][select.value[i].length - 1].classList.remove('select') select.value[i].pop() } else { select.value[i][0].classList.remove('select') select.value[i].shift() } } } } if ((yDirection === 1 && y > lastInfo[1]) || (yDirection === -1 && y <= 0)) { console.log('纵向超出,y扩展', select.value[0].length); if (yDirection === 1) { lastEnd = [lastEnd[0], select.range[1][1]] lastInfo = [lastInfo[0], e.target.offsetHeight] select.range[1] = [select.range[1][0], select.range[1][1] + e.target.offsetHeight] } else { select.range[0] = [select.range[0][0], select.range[0][1] - e.target.offsetHeight] lastEnd = [lastEnd[0], select.range[0][1]] lastInfo = [lastInfo[0], e.target.offsetHeight] } const lastRow = [] for (let k = 0; k < select.value[0].length; k++) { let el = yDirection === 1 ? select.value[select.value.length - 1][k] : select.value[0][k] lastRow.push(getRowXElement(el, yDirection)) } if (yDirection === 1) { select.value.push(lastRow) } else { select.value.unshift(lastRow) } // 更新选区范围 } else if ((yDirection === 1 && y <= 0) || (yDirection === -1 && y >= lastInfo[1])) { if (select.value.length < 1) return if (yDirection === 1) { select.range[1] = [select.range[1][0], lastEnd[1]] lastEnd = [lastEnd[0], lastEnd[1] - e.target.offsetHeight] lastInfo = [lastInfo[0], e.target.offsetHeight] } else { select.range[0] = [select.range[0][0], lastEnd[1] + e.target.offsetHeight] lastEnd = [lastEnd[0], lastEnd[1] + e.target.offsetHeight] lastInfo = [lastInfo[0], e.target.offsetHeight] } // 去掉最后一行的class if (yDirection === 1) { select.value[select.value.length - 1].forEach(el => { el.classList.remove('select') }) select.value.pop() } else { select.value[0].forEach(el => { el.classList.remove('select') }) select.value.shift() } } // 选中元素添加class for (let i = 0; i < select.value.length; i++) { for (let k = 0; k < select.value[i].length; k++) { select.value[i][k] && select.value[i][k].classList.add('select') } } } }) // 抬起 table.addEventListener('mouseup', (e) => { run = false if (timer !== 0) { clearTimeout(timer) timer = 0 } console.log(select.value); }) } /* 获取下一行当前横坐标相同位置元素 */ function getRowXElement(currentElement, direction = 1) { if (!currentElement.parentElement.nextElementSibling && direction == 1 || !currentElement.parentElement.previousElementSibling && direction !== 1) return null let nextElement = direction === 1 ? currentElement.parentElement.nextElementSibling.firstElementChild : currentElement.parentElement.previousElementSibling.firstElementChild; let childIndex = getChildIndex(currentElement) // 获取当前元素在父元素中的索引 if (childIndex !== -1) { return nextElement.parentElement.children[childIndex] } else { let currentLeft = currentElement.offsetLeft; let nextElementLeft = nextElement.offsetLeft; while (nextElement !== null && nextElementLeft !== currentLeft) { nextElement = getElement(nextElement, 1); nextElementLeft = nextElement.offsetLeft; } return nextElement; } } /** * 获取下一个兄弟元素 * direction === 1时默认查找下一个兄弟元素,否则上一个 **/ function getElement(element, direction = 1) { if (direction === 1) { if (element.nextElementSibling) { return element.nextElementSibling; } else { let parent = element.parentElement; while (parent && parent.nextElementSibling === null) { parent = parent.parentElement; } return parent ? parent.nextElementSibling.firstElementChild : null; } } else { if (element.previousElementSibling) { return element.previousElementSibling; } else { let parent = element.parentElement; while (parent && parent.previousElementSibling === null) { parent = parent.parentElement; } return parent ? parent.previousElementSibling.firstElementChild : null; } } } function clearSelect() { select.value.forEach((item, index) => { item.forEach(v => { v.classList.remove('select') }) }) Object.assign(select, { value: [[]], range: [[], []] // [start,end]范围 }) } /** * 获取子元素的索引 */ function getChildIndex(child) { var parent = child.parentNode; for (var i = 0; i < parent.children.length; i++) { if (parent.children[i] === child) { return i; } } return -1; // 如果找不到子元素,则返回-1 } </script> </body> </html>
合并没有做多次合并处理。
添加列没有实现。
边界情况未处理。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。