Ох эти таблицы
Доброго времени суток, форумчане. Встал вопрос о том, как удалить колонку в таблице, притом с colspan ами и rowspan ами. Подате хотя бы идею как это сделать.
Надеюсь на вашу помощь. Прошу проверять вот на этой табличке: <table border="1"> <tr> <td rowspan="3">1111</td> <td colspan="2">22222</td> <td colspan="2">33333</td> </tr> <tr> <td>44444</td> <td colspan="2" rowspan="2">55555</td> <td>66666</td> </tr> <tr> <td>77777</td> <td>88888</td> </tr> <tr> <td colspan="5">99999</td> </tr> </table> |
Интересная задачка. :)
На оптимальность решения не претендую, безупречность не гарантирую. Но всё же, вот что у меня получилось... <script type="text/javascript"> // table: элемент DOM Table, THead, TBody или TFoot // col: позиция колонки function deleteTableCol (table, col) { var rows = table.rows, cells, cell, prevCell; var cx, cy, cw, ch, coff; var map = [], mx, my, mw = 0, mh = 0; for (cy=0; cy<rows.length; cy++) { cells = rows[cy].cells; for (cx=0, coff=0; cx<cells.length; cx++, coff+=cw) { cell = cells[cx]; cw = cell.colSpan; ch = cell.rowSpan; if (cw == 0 || ch == 0) { return false; // динамическую растяжку ячеек по <colgroup> (при colspan=0) // и по <thead> / <tbody> / <tfoot> (при rowspan=0) // сложно обработать; поэтому, чтобы не напортачить, выходим } while (mh < cy+ch) { map[mh++] = []; } while (map[cy][coff] !== undefined) { coff++; } for (my=cy; my<cy+ch; my++) { for (mx=coff; mx<coff+cw; mx++) { if (map[my][mx] !== undefined) { return false; // при подсчете размеров и позиций у нас получилось, // что ячейки якобы "накладываются" друг на друга; // такую ситуацию тоже сложно обработать, выходим } map[my][mx] = cell; } } if (coff+cw > mw) { mw = coff+cw; } } } if (col >= mw) { return true; // ячеек в указанной позиции нет, // поэтому ничего делать не надо, выходим } for (my=0; my<mh; my++) { cell = map[my][col]; if (cell !== prevCell && cell !== undefined) { cw = cell.colSpan; if (cw > 1) { cell.colSpan = cw-1; } else { rows[my].removeChild(cell); } prevCell = cell; } } return true; } onload = function () { var table = document.getElementById("table1"); var tableCols = 5; var i, button; for (i=0; i<tableCols; i++) { button = document.createElement("input"); button.type = "button"; button.value = i; button.id = "delete" + i; button.onclick = (function (i) { return function () { deleteTableCol(table, i); document.body.removeChild(document.getElementById("delete" + (--tableCols))); }})(i); document.body.appendChild(button); } } </script> <table id="table1" border="1"> <tr> <td rowspan="3">11111</td> <td colspan="2">22222</td> <td colspan="2">33333</td> </tr> <tr> <td>44444</td> <td colspan="2" rowspan="2">55555</td> <td>66666</td> </tr> <tr> <td>77777</td> <td>88888</td> </tr> <tr> <td colspan="5">99999</td> </tr> </table><br> Некоторые соображения: 1. Условно-пустые строки (все <tr> , в которых не осталось ни одного <td> ) - не удаляются, а остаются как есть, чтобы не нарушать структуру таблицы с учетом ячеек, растянутых по вертикали.2. Полностью пустые строки (те, с которыми не пересекается ни одна вертикально растянутая ячейка) - тоже пока не удаляются. Но это можно допилить. 3. Все colspan'ы в таблице должны быть указаны арифметически корректно, то есть так, чтобы, например, не пытаться втиснуть двойную ячейку туда, где есть место только для одинарной: <table> <tr> <td>1111</td> <td rowspan="2">2222</td> </tr> <tr> *!* <td colspan="2">3333</td> */!* </tr> </table>С такой таблицей ничего не произойдет и функция просто вернет false .4. Поведение ячеек с colspan="0" и rowspan="0" обрабатывать слишком муторно, поэтому, наткнувшись на таковые, функция опять-таки ничего не сделает и вернет false .5. Относительно <table> строки извлекаются сразу изо всех имеющихся секций (<thead> , <tbody> и <tfoot> ), то есть будут обработаны ВСЕ <tr> насквозь, как будто они имеют одного общего родителя. Поэтому, если секции указаны явно, то, на всякий случай, функцию лучше вызывать для каждой из них по отдельности.Только прошу сперва тщательно всё потестировать. :) |
Цитата:
Произвольную ? (Или только первую) С произвольной - вряд ли универсально - посколь для каждого случая нужен алгоритм - что делать с ячейками с соlspan более этой колонки |
Я бы предложил добить таблицу недостающими ячейками c display: none, а потом решать какие ячейки будете удалять, где-то тут есть тема про нахождение соседних ячеек в таблице
http://javascript.ru/forum/events/29...i-rowspan.html |
Дзен-трансгуманист,
благодарю. Это везде пашет, то есть на любых таблицах? Deff, как раз то универсал нужен. Эту таблицу дал якобы со всеми случаями жизни. bes, не вариант. Пока буду разбирать код первого поста. Очень хорош... |
Цитата:
- я сталкивался с данной задачей: - седни бухгалтера говорят - алгоритм удаления такой - завтра иначе - Итоговый Вариант: - все ячейки в строках,( вплоть для идентичных повторяющих друг друга строк) - Бух задаёт явно явно кликая по ним в исходнике, и составляя граф удаления - в строках с идентичной структурой - алгоритм задается единожды |
А что если просканировать всю таблицу и записать в массив colspan ы, если rowspan>1?? и записать еще в следующие ячейки до rowspan значения? Тогда вероятно получится получить реальные значения ячеек, где длина их в строке меньше максимальной в данной таблице...
|
Apollo_440,
Да структуировать и восстановить пол-дела - но при удалении колонки - не ячейки - встают вопросы что делать с нижестоящей ячейкой у которой colspan более чем 1 она она по центру (к примеру) под удаляемой колонкой и забита текстом без переноса(цифры) - Переносить цифры ? |
Поэтому кажый раз придумывать реализацию под новые требования легче, когда все ячейки на руках. ;)
|
Deff,
colspan-1. Вроде как бы еще действует. Получили индекста удаляемых ячеек - а затем при удалении обновляем colspan на единицу меньше. Думаю работать будет. Конечно массив шаблона не самое лучшее решение, так как придется сканировать всю таблицу (размеры таблицы играют большую роль). Самое важное сейчас для меня - получить реальный индекс в строке. |
Apollo_440,
bes, Дал Вам ссылку на тему - там есть эти решения - см пост 4 |
тогда я неправильно назвал тему - нужно: как получить реальный индекс ячеки в таблице.
|
cellIndex
|
Apollo_440,
Воть смотрите = > http://javascript.ru/forum/events/29...tml#post188172 пост 25 Кликните по любой ячейке (хотя мон и не кликать) - прост так сделано - у bes тоже своё решение по теме Собственно - можно брать индекс не прилегающих а текущих - прост запрос был такой - пронумерованы то все |
bes,
cellIndex работает правильно без rowspan. Тут как бы продолжение к таблице первого поста. Все остальное я понял как сделать... |
Цитата:
Например, если есть td rowspan=2, то в следующей строке ячейку не следует удалять (но это в простейшем случае, когда нет colspan-в). Попробуйте добавить в таблицу недостающие ячейки и просто удаляйте все ячейки с нужным cellIndex (самое очевидное решение на мой взгляд). |
Цитата:
|
Apollo_440,
У меня и так вся таблица сканируется, ссылки на ячейки забиваются в нормализованный массив. Таблица из вашего примера у меня преобразуется вот в такой массив: Только вместо цифр там ссылки. А потом идет цикл по нужной колонке. Если colspan очередной ячейки больше единицы - делаем декремент, если единица - удаляем. UPD: внес в функцию пару мелких изменений, алгоритм не трогал. |
Цитата:
|
Цитата:
|
Дзен-трансгуманист, идея примерно та же (хотя в пример не вникал), только не нужно сканировать каждый раз всю таблицу (один раз добавить недостающие ячейки), код добавления ячеек к таблице есть по указанной ссылке, код удаления (прогнать в цикле по ячейкам с нужным cellIndex)
|
Цитата:
:blink: А чем по ссылке не нравицо -? там создаётся дубль таблы - выполненный в одноячейковом варианте - и ячейкам обозначающим реальную ячейку исходной таблицы(с Colspan и Rowspan) - присваиваецо тот жа класс - так шо с дублем можно вытворить - все что понравицо - потом ток считать идентичные классы (Принцип идентичный Вашему Вариант с первичным скрытием - был от bes |
Цитата:
|
Дзен-трансгуманист,
как описано здесь http://javascript.ru/forum/events/29...tml#post187221 этот способ очень медленный. Хотя идея очень хороша. |
bes, Deff,
Окей, понял. Таблица не будет сканироваться долго, ну если там конечно не миллион ячеек... Можно сделать и однократное сканирование - тогда последующие удаления будут намного быстрее, но тогда нельзя будет работать с DOM таблицы извне чтобы потом не испортить ее. Просто если делать дубль, то он в памяти может много места занять. Но у скрытия есть свой плюс - оно обратимо. |
Цитата:
ниже - табла(там, по ссылке) на 1000 ячеек (примерно реальный бухгалтерский размер - создавалась и обрабатывалась с цветом 0.6 сек - без цвета 0.2 |
Цитата:
|
Цитата:
Да, и еще добавил сохранение карты ячеек в объект таблицы (хотя не уверен, что это правильный подход), то есть обход всей таблицы происходит только при первом вызове функции, а затем ссылки просто достаются из кэша по индексам. Только тут надо учесть, что после добавления новых <tr> и <td> и любого другого изменения структуры таблицы, обязательно нужно будет строить карту ячеек заново. Для этого при вызове надо указать 3-й аргумент true , либо самостоятельно удалить свойство _cellMap из объекта таблицы.________________________________________ Update 07.08.2012
Update 08.08.2012
<style type="text/css"> table { border: 0px; border-spacing: 2px; } tr { height: 24px; } td { padding: 0px; } </style> <script type="text/javascript"> // ------------------------------ Here it starts ------------------------------ var deleteTableCol; var deleteTableRow; ( function () { function makeCellMap (table) { var rows = table.rows, cells, cell; var cx, cy, cw, ch, coff; var marr = [], mx, my, mw = 0, mh = 0; for (cy=0; cy<rows.length; cy++) { cells = rows[cy].cells; for (cx=0, coff=0; cx<cells.length; cx++, coff+=cw) { cell = cells[cx]; cw = cell.colSpan; ch = cell.rowSpan; if (cw == 0 || ch == 0) { return false; } while (mh < cy+ch) { marr[mh++] = []; } while (marr[cy][coff] !== undefined) { coff++; } for (my=cy; my<cy+ch; my++) { for (mx=coff; mx<coff+cw; mx++) { if (marr[my][mx] !== undefined) { return false; } marr[my][mx] = cell; } } if (coff+cw > mw) { mw = coff+cw; } } } return { marr: marr, mw: mw, mh: mh }; } var cacheProp = "_cellMap"; function getCellMap (table, invalidateCache) { var cellMap; if (invalidateCache && table[cacheProp] !== undefined) { delete table[cacheProp]; } if (table[cacheProp] === undefined) { cellMap = makeCellMap(table); if (cellMap === false) { return false; } table[cacheProp] = cellMap; } return table[cacheProp]; } function assertOrdinal (value, caller) { if (!isFinite(value) || value < 0 || value != Math.floor(value)) { throw new Error("Bad ordinal argument in " + caller); } } deleteTableCol = function (table, col, invalidateCache) { assertOrdinal(col, "deleteTableCol"); var cellMap = getCellMap(table, invalidateCache); if (cellMap === false) { return false; } if (col >= cellMap.mw) { return true; } var rows = table.rows, cell, prevCell; var marr = cellMap.marr, mw = cellMap.mw, mh = cellMap.mh, my, cw; for (my=0; my<mh; my++) { cell = marr[my][col]; if (cell !== prevCell && cell !== undefined) { cw = cell.colSpan; if (cw > 1) { cell.colSpan = cw-1; } else { rows[my].removeChild(cell); } prevCell = cell; } marr[my].splice(col, 1); } cellMap.mw--; return true; } deleteTableRow = function (table, row, invalidateCache) { assertOrdinal(row, "deleteTableRow"); var cellMap = getCellMap(table, invalidateCache); if (cellMap === false) { return false; } if (row >= cellMap.mh) { return true; } var trow = table.rows[row], trowNext = trow.nextSibling; var mrow = cellMap.marr[row], mrowNext = cellMap.marr[row+1]; var mw = cellMap.mw, mh = cellMap.mh, mx, mxNext, ch; var cell, prevCell, nextCell; for (mx=0; mx<mw; mx++) { cell = mrow[mx]; if (cell !== prevCell && cell !== undefined) { ch = cell.rowSpan; if (ch > 1) { cell.rowSpan = ch-1; if (cell.parentNode === trow) { trow.removeChild(cell); for (mxNext=mx+1; ; mxNext++) { nextCell = mrowNext[mxNext]; if (nextCell !== cell && (nextCell === undefined || nextCell.parentNode === trowNext)) { break; } } if (nextCell !== undefined) { trowNext.insertBefore(cell, nextCell); } else { trowNext.appendChild(cell); } } } prevCell = cell; } } trow.parentNode.removeChild(trow); cellMap.marr.splice(row, 1); cellMap.mh--; return true; } })(); // ------------------------------ Here it ends ------------------------------ /* * Fix for IE 8 and below * Required for following test only * */ if (!Array.prototype.hasOwnProperty("indexOf")) { Array.prototype.indexOf = function (value) { var i; for (i=0; i<this.length; i++) { if (this[i] == value) { return i; } } return -1; } } function appendTestTable (cols, rows, maxCellW, maxCellH) { var body = document.body, table, tbody, tr, td, killer; var map = [], mx, my; var cx, cy, cw, ch; var hIndex = [], vIndex = []; function rand (x) { return Math.floor(Math.random() * x); } function randColor () { return "#" + rand(16).toString(16) + rand(16).toString(16) + rand(16).toString(16); } function onDelete (fn, value, index1, index1Extra, index2, index2Extra) { return function () { var off = index1.indexOf(value); if (fn(table, off)) { index1.splice(off, 1); if (index1.length == index1Extra) { if (table.getElementsByTagName("td").length == index2.length - index2Extra + 1) { body.removeChild(table); body.removeChild(killer); } else { killer.value = "Oh, shit, there's a bug somewhere!"; } } } else { alert("Something gone wrong..."); } }} function createButton (caption, onclick) { var button = document.createElement("input"); button.style.width = "24px"; button.type = "button"; button.value = caption; button.onclick = onclick; return button; } table = document.createElement("table"); tbody = document.createElement("tbody"); killer = document.createElement("input"); killer.style.margin = "12px 0px 0px 2px"; killer.type = "button"; killer.value = "Kill this table"; killer.onclick = function () { body.removeChild(table); body.removeChild(killer); } body.appendChild(killer); for (my=0; my<rows; my++) { map[my] = []; vIndex[my] = my; } for (my=0; my<rows; my++) { tr = document.createElement("tr"); td = document.createElement("td"); td.appendChild(createButton(my, onDelete(deleteTableRow, my, vIndex, 0, hIndex, 1))); tr.appendChild(td); for (mx=0; mx<cols; mx+=cw) { while (map[my][mx]) { mx++; } if (mx == cols) { break; } for (cw=1; cw<maxCellW && mx+cw<cols; cw++) { if (map[my][mx+cw]) { break; } } cw = rand(cw) + 1; ch = rand(rows-my > maxCellH? maxCellH: rows-my) + 1; for (cy=0; cy<ch; cy++) { for (cx=0; cx<cw; cx++) { map[my+cy][mx+cx] = true; } } td = document.createElement("td"); td.style.backgroundColor = randColor(); td.colSpan = cw; td.rowSpan = ch; td.innerHTML = " "; tr.appendChild(td); } tbody.appendChild(tr); } tr = document.createElement("tr"); td = document.createElement("td"); tr.appendChild(td); for (mx=0; mx<cols; hIndex[mx+1]=mx++) { td = document.createElement("td"); td.appendChild(createButton(mx, onDelete(deleteTableCol, mx, hIndex, 1, vIndex, 0))); tr.appendChild(td); } tbody.appendChild(tr); table.appendChild(tbody); body.appendChild(table); } function newTestTable () { var i; var args = document.getElementById("args").value.match(/[0-9]+/g); if (args.length < 4) { return; } for (i=0; i<4; i++) { args[i] = parseInt(args[i], 10); if (!isFinite(args[i]) || args[i] == 0 || args[i] > 1000) { return; } } if (args[0] * args[1] > 10000) { return; } args.length = 4; appendTestTable.apply(window, args); } </script> <span>table cols, table rows, max colspan, max rowspan:</span> <input id="args" type="text" value="20, 20, 6, 3"> <input type="button" value="Append table" onclick="newTestTable()"> <br> NOTE: Хоть номера кнопок в процессе не меняются (они равны исходным индексам), но вызовы deleteTableCol и deleteTableRow всякий раз происходят именно по текущему смещению колонки/строки с нажатой кнопкой относительно начала не-кнопочных ячеек. |
Дзен-трансгуманист,
:blink: КЛАССС!!! Матёрый Человечище!!! Эт Нун Точно Запостить В наши скрипты - чобы потом не искать (По сравнению с Ламповыми Часами +400очков!! |
:lol: Для полноты картины - Еще и по строкам удаление (Ток отдельным постом (Посколь кого-то устроит именно данная версия
(*6-й раз балуюсь -создаю- стираю - балдю! |
Дзен-трансгуманист,
Ксать попробуйте добавить <tbody> - должно сработать и в старых Ие |
Deff,
:) Удаление по строкам теперь добавить будет не сложно, завтра сделаю ради забавы. Но в новый пост, пожалуй, писать не буду, а добавлю в тот, а то другим напряг будет еще две сотни строк перечитывать. Цитата:
indexOf в прототипе массива. Тоже завтра уже править буду.А ламповые часы приятно смотрятся. :) Хотя напомнили мне ЖК-часы, которые пару месяцев назад строгал я, но так и не дострогал. :D |
Дзен-трансгуманист,
Цитата:
|
Дзен-трансгуманист,
ты явно Человечище!!! Спасибо тебе большое за все!!! Думаю я смогу разгадать тайну твоего ума :) Думаю вопрос закрыт. Всем спасибо |
Добавил функцию
deleteTableRow для удаления строк и обновил тест в соответствии с ней.Оказалось сложнее, чем ожидалось на первый взгляд: в некоторых случаях ячейкам приходится менять предка, а с учетом colspan'ов и rowspan'ов - вставлять их потом в новое место нужно предельно аккуратно. В общем, без доли гемора и говнокода не обошлось... Ну и опять-таки, тестируйте. Цитата:
|
Дзен-трансгуманист,
Абалдеть... плусы кончились... В Ие6-8 всеж не работает ( по-моему tbody не вставили |
Цитата:
|
Цитата:
Заодно исправил баг в deleteTableRow , который проявился только после добавления <tbody> - а связан он с тем, что родителем строки не обязательно является непосредственно table . (лол, стыдно, что я проглядел такую банальную ошибку :D ) |
:write: Поздравляю - В Ие работает - просто замечательно - натур продукт!
|
Дзен-трансгуманист,
думаю лучше шоколадки ничего еще не создано :) Надо вынести данный скрипт на видное место, чтобы не тих здесь. |
Часовой пояс GMT +3, время: 15:12. |