Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #11 (permalink)  
Старый 29.10.2018, 23:44
Аватар для MC-XOBAHCK
Профессор
Отправить личное сообщение для MC-XOBAHCK Посмотреть профиль Найти все сообщения от MC-XOBAHCK
 
Регистрация: 06.08.2017
Сообщений: 473

Я понял.
Сообщение от рони
4. не знаю, выбор за вами, смотря какой нужен результат.
Сейчас получается есть варианты выделения ячеек лесенками вверх, вниз, влево, вправо, когда в таблице уже есть объединённая ячейка/и. По идее нужно чтоб ячейки выделяло выравнивая с объединёнными ячейками, по фигуре прямоугольника без вариантов лесенок.
Не думаю что у меня получится с этим разобраться. Буду что то думать.

Спасибо что помогли.
Ответить с цитированием
  #12 (permalink)  
Старый 30.10.2018, 14:06
Аватар для MC-XOBAHCK
Профессор
Отправить личное сообщение для MC-XOBAHCK Посмотреть профиль Найти все сообщения от MC-XOBAHCK
 
Регистрация: 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", то есть можно ячейки редактировать.
Можно ли как то определить в какой ячейке установлен курсор? Может добавляется к ячейке фокус или ещё что?
Ответить с цитированием
  #13 (permalink)  
Старый 02.11.2018, 11:37
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 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%
Ответить с цитированием
  #14 (permalink)  
Старый 02.11.2018, 12:19
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,112

Malleys,
выделение странно работает, захват ячеек больше чем нужно, особенно если начать с 2 или 3 колонки.
Ответить с цитированием
  #15 (permalink)  
Старый 02.11.2018, 12:28
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Сообщение от рони
выделение странно работает, захват ячеек больше чем нужно, особенно если начать с 2 или 3 колонки
Ещё раз, поскольку ячейка таблицы прямоугольная, то и выделенные ячейки (из которых можно получить объединённую ячейку) представляют из себя прямоугольную область. Ячейка не может быть не прямоугольной! Или вы нашли какой-то баг, который я не смог обнаружить! До сих пор не могу обнаружить!

UPD Пусть исходная таблица выглядит так. Вы хотите объединить ячейки A и B. Вы выделяете мышью область, которая затрагивает эти ячейки(отмечено красным). Вы могли решить, что объединённая ячейка должна занимать область, отмеченную жёлтым цветом, но это не так. Если предположить, что область, отмеченная жёлтым цветом и есть наименьшая область, которая может быть заменена одной ячейкой, то ячейка С удаляется, что приводит к смещению ячейки следующей за ней в той же колонке. Но это противоречит утверждению «ячейка таблицы прямоугольная, ведь выделенные ячейки (из которых можно получить объединённую ячейку) представляют из себя прямоугольную область», а значит выбранные (A, B и C) ячейки на самом деле не составляли прямоугольную область. Синяя область, равным образом, как и область всей таблицы, соответствует этому утверждению, но только она является наименьшей областью включающей в себя исходное выделение и граница которой проходить по рёбрам ячеек.
Изображения:
Тип файла: jpg 127.0.0.1_37415_select.html (2).jpg (15.7 Кб, 3 просмотров)

Последний раз редактировалось Malleys, 02.11.2018 в 13:11.
Ответить с цитированием
  #16 (permalink)  
Старый 02.11.2018, 13:28
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,112

Malleys,
ещё нет обьединённых ячеек, таблица изначальна,
я не могу выделить одну ячейку!!! или 2!!! выделяется вместо одной две, вместо двух четыре.
Ответить с цитированием
  #17 (permalink)  
Старый 02.11.2018, 13:47
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,112

Malleys,
2 и 3 колонка для примера с глюком, 4 нормально

Последний раз редактировалось рони, 02.11.2018 в 13:49.
Ответить с цитированием
  #18 (permalink)  
Старый 02.11.2018, 13:51
Профессор
Отправить личное сообщение для Nexus Посмотреть профиль Найти все сообщения от Nexus
 
Регистрация: 04.12.2012
Сообщений: 3,791

рони, а какой у вас браузер?
У меня, к слову, в Chrome@70.0.3538.77 подобного поведения нет, все корректно работает.

Upd. баг есть, если zoom страницы в браузере отличен от 100%.

Последний раз редактировалось Nexus, 02.11.2018 в 13:55.
Ответить с цитированием
  #19 (permalink)  
Старый 02.11.2018, 14:12
Аватар для рони
Профессор
Отправить личное сообщение для рони Посмотреть профиль Найти все сообщения от рони
 
Регистрация: 27.05.2010
Сообщений: 33,112

Сообщение от Nexus
zoom страницы в браузере отличен от 100%.
да хром последний и zoom
и ещё в Firefox таблица странно выглядит.
Ответить с цитированием
  #20 (permalink)  
Старый 02.11.2018, 16:03
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Сообщение от Nexus
баг есть, если zoom страницы в браузере отличен от 100%
Сообщение от рони
и zoom
Уменьшил область (см. исправленный пост №13) ячейки при вычислении пересечения, берётся 99.8% от реального размера. Дело в том, что метод Element.prototype.getBoundingClientRect при масштабировании (> 100%)может возвращать величины, которые отличаются на...
// помасштабируй и запускай
alert(1 - 1 / devicePixelRatio)


Или как исправить этот костыль? (99.8% от реального размера)

Сообщение от рони
в Firefox таблица странно выглядит.
contenteditable действует так
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Динамически обновленная таблица HTML работает неправильно mishapod AJAX и COMET 1 11.04.2018 01:50
Простой онлайн редактор HTML dima_riabets Элементы интерфейса 0 14.02.2015 03:25
Редактор HTML в режиме real-time Demath Сайт Javascript.ru 4 04.07.2012 16:09
редактор html или bb кодов на jquery gaserge Общие вопросы Javascript 15 28.08.2011 01:39
WYSIWYG редактор текста HTML страницы на javascript Дмитри Чижиков Ваши сайты и скрипты 4 14.09.2009 17:05