Я понял.
Цитата:
Не думаю что у меня получится с этим разобраться. Буду что то думать. Спасибо что помогли. |
Доработал расчёт размеров объединённой ячейки:
<style>table{min-height: 250px;border-collapse: separate}th, td{min-width:50px;min-height:15px}td{border: 1px #0000CD solid;background-color: #800080} td.sel{opacity:.6} #line{position:absolute;width:0px;height:0px;background:rgba(0,90,255,0.25);border:1px solid rgba(0,114,255,0.5);box-sizing:border-box}</style> <script> window.addEventListener("DOMContentLoaded", function() { var target = document.createElement("div"), x, y; target.id = "line"; document.addEventListener("mousedown", function(e) { if (e.which > 1) return; x = e.pageX; y = e.pageY; document.body.appendChild(target); selectBlock(); document.addEventListener("mousemove", move, false); return false }, false); function move(e) { e.preventDefault(); target.style.width = Math.abs(e.pageX - x) + "px"; target.style.height = Math.abs(e.pageY - y) + "px"; target.style.left = (e.pageX < x ? e.pageX : x) + "px"; target.style.top = (e.pageY < y ? e.pageY : y) + "px"; selectBlock(true) } document.addEventListener("mouseup", function() { target.style.width = "0px"; target.style.height = "0px"; document.removeEventListener("mousemove", move); target.parentNode && document.body.removeChild(target) }); function collisionDetection(x1, y1, w1, h1, x2, y2, w2, h2) { return x1 < x2 + w2 && y1 < y2 + h2 && x1 + w1 > x2 && y1 + h1 > y2 } function coordinate(el) { var pos = el.getBoundingClientRect(), top = pos.top, left = pos.left, right = pos.right, bottom = pos.bottom, height = bottom - top, width = right - left; return [left, top, width, height] } function selectBlock(add) { [].forEach.call(document.querySelectorAll("td"), function(el) { var data = coordinate(el).concat(coordinate(target)), modify = add && collisionDetection.apply(null, data) ? "add" : "remove"; el.classList[modify]("sel") }) } function mergeTd() { var tds = document.querySelectorAll('td.sel'), len = tds.length, parent; let col = [[]]; let row = 0; let startArr = 0; if (len) { [].forEach.call(tds, function (el, i) { var elParent = el.parentNode; if (i == 0) { parent = el.parentNode; el.hasAttribute('rowspan') ? row = row + +el.getAttribute('rowspan') : row++; } else if (parent == elParent) { } else { parent = el.parentNode; startArr++; col[startArr] = []; el.hasAttribute('rowspan') ? row = row + +el.getAttribute('rowspan') : row++; } el.hasAttribute('colspan') ? col[startArr].push(+el.getAttribute('colspan')) : col[startArr].push(1); }); // console.log(col); // массив размеров colspan let col1 = (function() { let n = []; for (let i = 0; i < col.length; i++) { n[i] = col[i].reduce((a, b) => a + b); } return Math.max.apply(null, n); })(); for (let i = 0; i < tds.length; i++) { if (i == 0) { col1 > 1 ? tds[i].setAttribute('colspan', col1, 0) : 0; row > 1 ? tds[i].setAttribute('rowspan', row, 0) : 0; } else tds[i].remove(); } } }; document.querySelector(".merge").addEventListener("mousedown", mergeTd) }); </script> <table> <tbody> <tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr> <tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr> <tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr> <tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr> </tbody> </table> <input type="button" value="go" class="merge"> Расчёт colspan можно делать гораздо проще и короче. Знаю как, но пока оставил так чтобы можно было вывести в консоль и посмотреть размеры colspan выделенных ячеек в виде многомерного массива. Есть баг при выделении ячеек "лесенкой". Как доработать этот момент, пока не знаю. Есть мысля увеличивать размер создаваемого прямоугольника выделения, до крайних координат уже выделенных ячеек min top, min left, max reigth, max bottom. При этом нужно и про уменьшение области выделения не забывать. Какая то длинная портянка в условии нарисовуется... осталось "вырисовать" куда и о чём написать это условие : ) Если можно - ВОПРОС по таблице У меня у таблицы стоит атрибут contenteditable="true", то есть можно ячейки редактировать. Можно ли как то определить в какой ячейке установлен курсор? Может добавляется к ячейке фокус или ещё что? |
Вложений: 2
Цитата:
1. Пусть дана таблица 5 на 5 ячеек. Некоторые ячейки уже объединены: (Это не сложно) Вложение 4022 Если теперь объединить ячейки A и B, то таблица должна по прежнему остаться прямоугольной таблицей! 2. Дана таблица: Вложение 4023 Объединив любую ячейку с соседней, у вас по прежнему должна остаться прямоугольная таблица! Цитата:
Цитата:
Вот мой вариант. Я ввёл вспомогательный класс Box, который представляет прямоугольные области исходного или вычисленного выделения. После того, как на основе области исходного выделения была найдена область вычисленного выделения, все ячейки кроме одной удаляются, а у оставшейся устанавливаются соответствующие атрибуты colspan и rowspan таким образом, что она целиком заполняет область вычисленного выделения. <!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <style type="text/css"> table { height: 500px; width: 500px; border-collapse: collapse; position: relative; user-select: none; cursor: crosshair; table-layout: fixed; font: 1em Helvetica Neue, Segoe UI, Roboto, Ubuntu, sans-serif; text-align: center; } td { border: 1px gray solid; } </style> <script> class Box { constructor(x = 0, y = 0, width = 0, height = 0) { this.x = x; this.y = y; this.width = width; this.height = height; } contains({ x, y, width, height }) { return ( x > this.x && y > this.y && (x + width) < (this.x + this.width) && (y + height) < (this.y + this.height) ); } overlaps({ x, y, width, height }) { return ( 1.001 * Math.max(x, this.x) < 0.998 * Math.min(x + width, this.x + this.width) && 1.001 * Math.max(y, this.y) < 0.998 * Math.min(y + height, this.y + this.height) ); } equals({ x, y, width, height }) { return ( x === this.x && y === this.y && width === this.width && height === this.height ); } add({x, y, width, height}) { return new Box( Math.min(x, this.x), Math.min(y, this.y), Math.max(x + width, this.x + this.width) - Math.min(x, this.x), Math.max(y + height, this.y + this.height) - Math.min(y, this.y) ); } relativeTo({ x, y }) { return new Box( this.x - x, this.y - y, this.width, this.height ); } relativeToElement(element) { return this.relativeTo(element.getBoundingClientRect()); } static from({ x = 0, y = 0, width = 0, height = 0 } = {}) { return new Box(x, y, width, height); } } class EditableTable extends HTMLTableElement { constructor() { super(); const document = this.ownerDocument; this.selectionElement = document.createElement("div"); this.selectionElement2 = document.createElement("div"); this.selectionElement.style.cssText = ` position: absolute; background: rgba(111, 168, 220, 0.66); pointer-events: none; `; this.selectionElement2.style.cssText = ` position: absolute; outline: 1px dashed rgba(0, 0, 0, 0.5); pointer-events: none; `; this.mergeButton = document.createElement("button"); this.mergeButton.textContent = "объединить"; this.mergeButton.addEventListener("click", this.merge.bind(this)); this.addEventListener("mousedown", this.startSelect.bind(this)); document.addEventListener("mousemove", this.moveSelect.bind(this)); document.addEventListener("mouseup", this.endSelect.bind(this)); } connectedCallback() { this.parentNode.insertBefore(this.mergeButton, this); } startSelect(event) { this._x = event.pageX; this._y = event.pageY; this._pointerIsMoving = true; this.appendChild(this.selectionElement); this.appendChild(this.selectionElement2); this.cellBoxes = []; for(const cell of this.querySelectorAll("td")) { const box = Box.from(cell.getBoundingClientRect()) .relativeToElement(this); this.cellBoxes.push(box); } this.moveSelect(event); } moveSelect(event) { if(!this._pointerIsMoving) return; event.preventDefault(); const { pageX: x, pageY: y } = event; const selectionBox = new Box( Math.min(x, this._x), Math.min(y, this._y), Math.abs(x - this._x), Math.abs(y - this._y) ).relativeToElement(this); let actualSelectionBox = this.constructor.getActualSelection(selectionBox, this.cellBoxes); this.constructor.transferBoxToElement(actualSelectionBox, this.selectionElement); this.constructor.transferBoxToElement(selectionBox, this.selectionElement2); } static transferBoxToElement({x, y, width, height }, { style }) { style.left = `${x}px`; style.top = `${y}px`; style.width = `${width}px`; style.height = `${height}px`; } static getActualSelection(selectionBox, cellBoxes) { const box = getActualSelectionHelper(selectionBox, cellBoxes); return box.equals(getActualSelectionHelper(box, cellBoxes)) ? box : this.getActualSelection(box, cellBoxes); function getActualSelectionHelper(selectionBox, cellBoxes) { let actualSelectionBox = Box.from(selectionBox) for(const box of cellBoxes) { if(selectionBox.overlaps(box)) { actualSelectionBox = actualSelectionBox.add(box); } } return actualSelectionBox; } } merge() { let actualSelectionBox = this.constructor.getActualSelection(this._selectionBox, this.cellBoxes); this.constructor.transferBoxToElement(actualSelectionBox, this.selectionElement); this.constructor.transferBoxToElement(this._selectionBox, this.selectionElement2); const cellsMap = []; function addToMap(x, y, r = 0) { loop: for(var j = r; true; j++) { if(!cellsMap[j]) cellsMap[j] = []; for(var i = 0; true; i++) { if(!cellsMap[j][i]) { for(var v = 0; v < y; v++) { for(var u = 0; u < x; u++) { if(!cellsMap[j + v]) cellsMap[j + v] = []; cellsMap[j + v][i + u] = 1; } } break loop; } } } } let selectedCells = []; let index; let isFirstSelectedRowProcessed = false; for(const row of this.rows) { let isRowAffectedBySelection = false; for(const cell of row.cells) { const cellBox = Box.from(cell.getBoundingClientRect()).relativeToElement(this); if(actualSelectionBox.overlaps(cellBox)) { if(!isRowAffectedBySelection) { isRowAffectedBySelection = true; } if(!isFirstSelectedRowProcessed) { isFirstSelectedRowProcessed = true; index = 0; } selectedCells.push(cell); addToMap(cell.colSpan, cell.rowSpan, index); } } index++; } const span = { colSpan: "0" in cellsMap ? cellsMap[0].length : 0, rowSpan: cellsMap.length }; const selectedCell = selectedCells.shift(); Object.assign(selectedCell || {}, span); selectedCells.forEach(cell => { selectedCell.append(...cell.childNodes); cell.remove() }); this.selectionElement.remove(); } split() { // TODO если нужно } endSelect(event) { if(!this._pointerIsMoving) return; this._pointerIsMoving = false; const { pageX: x, pageY: y } = event; this._selectionBox = new Box( Math.min(x, this._x), Math.min(y, this._y), Math.abs(x - this._x), Math.abs(y - this._y) ).relativeToElement(this); this.selectionElement2.remove(); } } customElements.define("editable-table", EditableTable, { extends: "table" }); // firefox + contenteditable + table !== ❤ //document.addEventListener("DOMContentLoaded", event => { // for(const cell of document.querySelectorAll("td")) { // cell.contentEditable = true; // } //}); </script> </head> <body> <table is="editable-table"> <tbody> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> </tbody> </table> </body> </html> Цитата:
for(const cell of document.querySelectorAll("td")) { cell.contentEditable = true; } |
Malleys,
выделение странно работает, захват ячеек больше чем нужно, особенно если начать с 2 или 3 колонки. |
Вложений: 1
Цитата:
UPD Пусть исходная таблица выглядит так. Вы хотите объединить ячейки A и B. Вы выделяете мышью область, которая затрагивает эти ячейки(отмечено красным). Вы могли решить, что объединённая ячейка должна занимать область, отмеченную жёлтым цветом, но это не так. Если предположить, что область, отмеченная жёлтым цветом и есть наименьшая область, которая может быть заменена одной ячейкой, то ячейка С удаляется, что приводит к смещению ячейки следующей за ней в той же колонке. Но это противоречит утверждению «ячейка таблицы прямоугольная, ведь выделенные ячейки (из которых можно получить объединённую ячейку) представляют из себя прямоугольную область», а значит выбранные (A, B и C) ячейки на самом деле не составляли прямоугольную область. Синяя область, равным образом, как и область всей таблицы, соответствует этому утверждению, но только она является наименьшей областью включающей в себя исходное выделение и граница которой проходить по рёбрам ячеек. |
Malleys,
ещё нет обьединённых ячеек, таблица изначальна, я не могу выделить одну ячейку!!! или 2!!! выделяется вместо одной две, вместо двух четыре. |
|
рони, а какой у вас браузер?
У меня, к слову, в Chrome@70.0.3538.77 подобного поведения нет, все корректно работает. Upd. баг есть, если zoom страницы в браузере отличен от 100%. |
Цитата:
и ещё в Firefox таблица странно выглядит. |
Цитата:
Цитата:
// помасштабируй и запускай alert(1 - 1 / devicePixelRatio) Или как исправить этот костыль? (99.8% от реального размера) Цитата:
|
Часовой пояс GMT +3, время: 20:25. |