29.10.2018, 23:44
|
|
Профессор
|
|
Регистрация: 06.08.2017
Сообщений: 473
|
|
Я понял.
Сообщение от рони
|
4. не знаю, выбор за вами, смотря какой нужен результат.
|
Сейчас получается есть варианты выделения ячеек лесенками вверх, вниз, влево, вправо, когда в таблице уже есть объединённая ячейка/и. По идее нужно чтоб ячейки выделяло выравнивая с объединёнными ячейками, по фигуре прямоугольника без вариантов лесенок.
Не думаю что у меня получится с этим разобраться. Буду что то думать.
Спасибо что помогли.
|
|
30.10.2018, 14:06
|
|
Профессор
|
|
Регистрация: 06.08.2017
Сообщений: 473
|
|
Доработал расчёт размеров объединённой ячейки:
<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", то есть можно ячейки редактировать.
Можно ли как то определить в какой ячейке установлен курсор? Может добавляется к ячейке фокус или ещё что?
|
|
02.11.2018, 11:37
|
|
Профессор
|
|
Регистрация: 20.12.2009
Сообщений: 1,714
|
|
Сообщение от MC-XOBAHCK
|
Идея как считать высоту и ширину для объединённой ячейки у меня появилась. Буду пробовать.
|
MC-XOBAHCK, рони, возможно вы захотите проверить, как работает объединение ячеек при следующих условиях.
1. Пусть дана таблица 5 на 5 ячеек. Некоторые ячейки уже объединены: (Это не сложно)
127.0.0.1_37415_select.html.jpg
Если теперь объединить ячейки A и B, то таблица должна по прежнему остаться прямоугольной таблицей!
2. Дана таблица:
127.0.0.1_37415_select.html (1).jpg
Объединив любую ячейку с соседней, у вас по прежнему должна остаться прямоугольная таблица!
Сообщение от MC-XOBAHCK
|
сделать правильное выделение
|
По сути у вас может быть две области выделения. Одна показывает область выделенную при помощи мыши (исходное выделение), другая вычисляется на основе первой: она показывает какие ячейки таблицы будут объединены (вычисленное выделение). Поскольку ячейка таблицы прямоугольная, то и выделенные ячейки представляют из себя прямоугольную область. Отсюда следует, что вам нужно найти такую область, которая больше, чем исходное выделение и равна наименьшей области среди всевозможных выделении, которые целиком вмещают в себя любые ячейки таблицы (т. е. граница вычисленного выделения не проходит по грани какой-либо ячейки, но только по ребрам ячеек, и содержит в себе исходное выделение)
Сообщение от рони
|
смотря какой нужен результат.
|
Поскольку ячейка таблицы прямоугольная, то и выделенные ячейки (из которых можно получить объединённую ячейку) представляют из себя прямоугольную область.
Вот мой вариант. Я ввёл вспомогательный класс 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>
Сообщение от MC-XOBAHCK
|
У меня у таблицы стоит атрибут contenteditable="true", то есть можно ячейки редактировать.
Можно ли как то определить в какой ячейке установлен курсор? Может добавляется к ячейке фокус или ещё что?
|
Когда у таблицы стоит атрибут contenteditable, то всё редактируется, что внутри элемента table. Например, вы можете вписать текст в две соседние ячейки, затем обе выделить, скопировать и вставить в третью ячейку. Если нужно редактировать только содержимое ячеек, то нужно прописать к каждой ячейке contenteditable
for(const cell of document.querySelectorAll("td")) {
cell.contentEditable = true;
}
Последний раз редактировалось Malleys, 02.11.2018 в 16:06.
Причина: Ура!!! Оказывается можно уменьшить коробку для сравнения на 0.2%
|
|
02.11.2018, 12:19
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,112
|
|
Malleys,
выделение странно работает, захват ячеек больше чем нужно, особенно если начать с 2 или 3 колонки.
|
|
02.11.2018, 12:28
|
|
Профессор
|
|
Регистрация: 20.12.2009
Сообщений: 1,714
|
|
Сообщение от рони
|
выделение странно работает, захват ячеек больше чем нужно, особенно если начать с 2 или 3 колонки
|
Ещё раз, поскольку ячейка таблицы прямоугольная, то и выделенные ячейки (из которых можно получить объединённую ячейку) представляют из себя прямоугольную область. Ячейка не может быть не прямоугольной! Или вы нашли какой-то баг, который я не смог обнаружить! До сих пор не могу обнаружить!
UPD Пусть исходная таблица выглядит так. Вы хотите объединить ячейки A и B. Вы выделяете мышью область, которая затрагивает эти ячейки(отмечено красным). Вы могли решить, что объединённая ячейка должна занимать область, отмеченную жёлтым цветом, но это не так. Если предположить, что область, отмеченная жёлтым цветом и есть наименьшая область, которая может быть заменена одной ячейкой, то ячейка С удаляется, что приводит к смещению ячейки следующей за ней в той же колонке. Но это противоречит утверждению «ячейка таблицы прямоугольная, ведь выделенные ячейки (из которых можно получить объединённую ячейку) представляют из себя прямоугольную область», а значит выбранные (A, B и C) ячейки на самом деле не составляли прямоугольную область. Синяя область, равным образом, как и область всей таблицы, соответствует этому утверждению, но только она является наименьшей областью включающей в себя исходное выделение и граница которой проходить по рёбрам ячеек.
Последний раз редактировалось Malleys, 02.11.2018 в 13:11.
|
|
02.11.2018, 13:28
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,112
|
|
Malleys,
ещё нет обьединённых ячеек, таблица изначальна,
я не могу выделить одну ячейку!!! или 2!!! выделяется вместо одной две, вместо двух четыре.
|
|
02.11.2018, 13:47
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,112
|
|
Malleys,
2 и 3 колонка для примера с глюком, 4 нормально
Последний раз редактировалось рони, 02.11.2018 в 13:49.
|
|
02.11.2018, 13:51
|
Профессор
|
|
Регистрация: 04.12.2012
Сообщений: 3,791
|
|
рони, а какой у вас браузер?
У меня, к слову, в Chrome@70.0.3538.77 подобного поведения нет, все корректно работает.
Upd. баг есть, если zoom страницы в браузере отличен от 100%.
Последний раз редактировалось Nexus, 02.11.2018 в 13:55.
|
|
02.11.2018, 14:12
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,112
|
|
Сообщение от Nexus
|
zoom страницы в браузере отличен от 100%.
|
да хром последний и zoom
и ещё в Firefox таблица странно выглядит.
|
|
02.11.2018, 16:03
|
|
Профессор
|
|
Регистрация: 20.12.2009
Сообщений: 1,714
|
|
Сообщение от Nexus
|
баг есть, если zoom страницы в браузере отличен от 100%
|
Уменьшил область (см. исправленный пост №13) ячейки при вычислении пересечения, берётся 99.8% от реального размера. Дело в том, что метод Element.prototype.getBoundingClientRect при масштабировании (> 100%)может возвращать величины, которые отличаются на...
// помасштабируй и запускай
alert(1 - 1 / devicePixelRatio)
Или как исправить этот костыль? (99.8% от реального размера)
Сообщение от рони
|
в Firefox таблица странно выглядит.
|
contenteditable действует так
|
|
|
|