Reputation: 96
Here is a table that you can click and drag down to rowspan. I added the function removeCellSpan() to revert the spanned cell back to its default. It can successfully set the rowspan back to 0 for $thisCell and append td elements back to the appropriate rows that were spanned. The problem is that these newly appended elements are not recognized by the rest of the table and cannot be highlighted. It seems that all the columns become offset by 1. What am I missing?
EDIT: The snippet is long, but it illustrates the problem perfectly. Click the "+" button to add an appointment in the schedule, right-click to delete the appointment. The problem arises when you try to schedule the same appointment again in the same section.
function addAppt() {
$('td').css('cursor', 'cell');
$('textarea').css('cursor', 'cell');
var $table = $("#myTable tbody");
var $rows = $table.children("tr");
var $cells = $table.find("td");
var numCells = $cells.length;
var numRows = $rows.length;
var numCols = numCells / numRows; // skip row headings
// track which columns have rowspans by setting to 1
var matrix = new Array(numRows).fill(new Array(numCols).fill(0));
var matrix = new Array(numRows);
// init 2d matrix
for (var i = 0; i < 5; i++) {
matrix[i] = new Array(numCols);
}
//matrix[1][3] = 1; // test blocking cell
var startCol, startRow, endRow, lastValidCell;
var cellDown, cellOver, cellUp;
var mouseDown = false;
// used mouse event code from http://stackoverflow.com/a/19164149/1544886
$cells.on('mousedown touchstart', function(event) {
var cellPos;
cellDown = this;
event.preventDefault();
mouseDown = true;
cellPos = findCell(cellDown);
if (cellPos) {
startCol = cellPos.col;
startRow = endRow = cellPos.row;
highlightCells();
} else {
clearHighlights();
}
});
$cells.on('mousemove touchmove', function(event) {
event.preventDefault();
if (mouseDown && cellOver != this) {
var cellPos;
cellOver = this;
cellPos = findCell(cellOver);
if (cellPos) {
// limit to starting column only
if (cellPos.col === startCol) {
endRow = cellPos.row;
highlightCells();
}
}
}
});
$cells.on('mouseup touchend', function(event) {
var cellPos;
event.preventDefault();
cellUp = this;
cellPos = findCell(cellUp);
if (cellPos && cellUp === lastValidCell) {
createCellSpan();
}
});
$(window.document).on('mouseup touchend', function(event) {
mouseDown = false;
//cellDown = cellOver = null;
clearHighlights();
});
function findCell(cell) {
var col, row;
$cells.each(function(idx, el) {
if (cell === el) {
col = idx % numCols;
row = Math.floor(idx / numCols);
if (matrix[row][col] === 1) { // a rowspan already exists for this cell
//console.log('found', row, col);
col = null;
}
return false;
}
});
return (col != null) ? {
col: col,
row: row
} : null;
}
function highlightCells() {
clearHighlights();
if (endRow >= startRow) {
for (var row = startRow; row <= endRow; row++) {
if (matrix[row][startCol] !== 1) { // rowspan doesn't already exists for this cell
var $thisCell = $cells.eq(row * numCols + startCol);
$thisCell.addClass('highlight');
lastValidCell = $thisCell[0];
} else {
endRow = row - 1; // found a blocking cell
return false;
}
}
} else {
for (var row = startRow; row >= endRow; row--) {
if (matrix[row][startCol] !== 1) { // rowspan doesn't already exists for this cell
var $thisCell = $cells.eq(row * numCols + startCol);
$thisCell.addClass('highlight');
lastValidCell = $thisCell[0];
} else {
endRow = row + 1; // found a blocking cell
return false;
}
}
}
}
function clearHighlights() {
$cells.removeClass('highlight');
}
function createCellSpan() {
var sRow = Math.min(startRow, endRow);
var eRow = Math.max(startRow, endRow);
var rowSpan = eRow - sRow + 1;
for (var row = eRow; row >= sRow; row--) {
var $thisCell = $cells.eq(row * numCols + startCol);
if (row === sRow)
$thisCell.attr('rowspan', rowSpan).addClass('spanned');
else
$thisCell.remove();
matrix[row][startCol] = 1; // mark these cells as blocked
}
/*for (var row = sRow; row <= eRow; row++) {
var $thisCell = $cells.eq(row * numCols + startCol);
$thisCell.addClass('spanned');
matrix[row][startCol] = 1; // mark these cells as blocked
} */
}
function removeCellSpan() {
var sRow = Math.min(startRow, endRow);
var eRow = Math.max(startRow, endRow);
var rowSpan = eRow - sRow + 1;
for (var row = eRow; row >= sRow; row--) {
var $thisCell = $cells.eq(row * numCols + startCol);
var $thisRow = $('tbody tr').eq(row);
if (row === sRow) {
$thisCell.attr('rowspan', 0).removeClass('spanned');
console.log("Span Removed.");
matrix[row][startCol] = 0; // mark this cell as empty
} else {
$thisRow.append('<td><textarea cols="20" rows="5" class="appt-text"></textarea></td>');
matrix[row][startCol] = 0; // mark these cells as empty
}
}
}
$(document).on("contextmenu", "td", function(e) {
e.preventDefault();
if ($(this).hasClass('spanned') === false) {
console.log("This cell is not spanned.");
return false;
} else {
removeCellSpan();
}
});
}
#control-panel p {
display: inline;
font-size: 14px;
}
table {
font-family: Arvo, Arial, sans-serif;
border-collapse: separate !important;
border-top: 2px solid #4DC7E8;
border-left: 2px solid #4DC7E8;
border-radius: 6px;
}
.days-of-the-week th {
width: 160px;
padding-top: 10px;
padding-bottom: 10px;
text-align: center;
border-right: 2px solid #4DC7E8;
border-bottom: 2px solid #4DC7E8;
border-radius: 6px;
}
tr td, th {
border-right: 2px solid #4DC7E8;
border-bottom: 2px solid #4DC7E8;
border-radius: 6px;
}
td {
height: 50px;
}
.days-of-the-week .time-col {
width: 80px;
}
tbody th {
text-align: right;
padding: 5px 3px;
}
.highlight {
background-color: rgba(255, 108, 78, 0.3);
}
.spanned {
background-color: green;
}
.appt-text {
background-color: inherit;
border: 0px;
width: 100%;
height: 100%;
outline: none;
cursor: default;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row" id="control-panel">
<button onclick="addAppt()" id="add-appt" title="Add Appointment">+</button>
<p>= Add Appointment</p>
</div>
<div class="row text-center">
<table id="myTable">
<thead>
<tr class="days-of-the-week">
<th scope="col" class="time-col"></th>
<th scope="col">Sunday</th>
<th scope="col">Monday</th>
<th scope="col">Tuesday</th>
<th scope="col">Wednesday</th>
<th scope="col">Thursday</th>
<th scope="col">Friday</th>
<th scope="col">Saturday</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" class="rowHdr">10:00am</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">10:30am</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">11:00am</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">11:30am</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">12:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">12:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">1:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">1:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">2:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">2:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">3:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">3:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">4:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">4:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">5:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">5:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">6:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">6:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">7:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">7:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">8:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">8:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">9:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">9:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">10:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
</tbody>
</table>
</div>
I had to include all of my code for the snippet to work. But it illustrates the problem perfectly.
Upvotes: 1
Views: 1424
Reputation: 16540
I've made a number of changes. Too many to detail, but I expect you'll compare the before with the after to see what they are.
One key point, I've chosen to hide the cells using display:none
, rather than delete them. This makes it easier to bring them back when removing the rowspan.
function addAppt() {
$('td').css('cursor', 'cell');
$('textarea').css('cursor', 'cell');
var $table = $("#myTable tbody");
var $rows = $table.children("tr");
var $cells = $table.find("td");
var numCells = $cells.length;
var numRows = $rows.length;
var numCols = numCells / numRows; // skip row headings
// track which columns have rowspans by setting to 1
var matrix = new Array(numRows).fill(new Array(numCols).fill(0));
var matrix = new Array(numRows);
// init 2d matrix
for (var i = 0; i < numRows; i++) {
matrix[i] = new Array(numCols);
}
//matrix[1][3] = 1; // test blocking cell
var startCol, startRow, endRow, lastValidCell;
var cellDown, cellOver, cellUp;
var mouseDown = false;
// used mouse event code from http://stackoverflow.com/a/19164149/1544886
$cells.on('mousedown touchstart', function(event) {
var cellPos;
if (event.which !== 1) return; // only continue if left mouse button
cellDown = this;
event.preventDefault();
mouseDown = true;
cellPos = findCell(cellDown);
if (cellPos) {
startCol = cellPos.col;
startRow = endRow = cellPos.row;
highlightCells();
} else {
clearHighlights();
}
});
$cells.on('mousemove touchmove', function(event) {
event.preventDefault();
if (mouseDown && cellOver != this) {
var cellPos;
cellOver = this;
cellPos = findCell(cellOver);
if (cellPos) {
// limit to starting column only
if (cellPos.col === startCol) {
endRow = cellPos.row;
highlightCells();
}
}
}
});
$cells.on('mouseup touchend', function(event) {
var cellPos;
if (event.which !== 1) return; // only continue if left mouse button
event.preventDefault();
cellUp = this;
cellPos = findCell(cellUp);
if (cellPos && cellUp === lastValidCell) {
createCellSpan();
}
});
$(window.document).on('mouseup touchend', function(event) {
mouseDown = false;
//cellDown = cellOver = null;
clearHighlights();
});
function findCell(cell, ignore) {
var col, row;
ignore = ignore | true; // set ignore to true if we haven't provided this optional param
$cells.each(function(idx, el) {
if (cell === el) {
col = idx % numCols;
row = Math.floor(idx / numCols);
if (!ignore && matrix[row][col] === 1) { // a rowspan already exists for this cell
//console.log('found', row, col);
col = null;
}
return false;
}
});
return (col != null) ? {
col: col,
row: row
} : null;
}
function highlightCells() {
clearHighlights();
if (endRow >= startRow) {
for (var row = startRow; row <= endRow; row++) {
if (matrix[row][startCol] !== 1) { // rowspan doesn't already exists for this cell
var $thisCell = $cells.eq(row * numCols + startCol);
$thisCell.addClass('highlight');
lastValidCell = $thisCell[0];
} else {
endRow = row - 1; // found a blocking cell
return false;
}
}
} else {
for (var row = startRow; row >= endRow; row--) {
if (matrix[row][startCol] !== 1) { // rowspan doesn't already exists for this cell
var $thisCell = $cells.eq(row * numCols + startCol);
$thisCell.addClass('highlight');
lastValidCell = $thisCell[0];
} else {
endRow = row + 1; // found a blocking cell
return false;
}
}
}
}
function clearHighlights() {
$cells.removeClass('highlight');
}
function createCellSpan() {
var sRow = Math.min(startRow, endRow);
var eRow = Math.max(startRow, endRow);
var rowSpan = eRow - sRow + 1;
for (var row = eRow; row >= sRow; row--) {
var $thisCell = $cells.eq(row * numCols + startCol);
if (row === sRow)
$thisCell.attr('rowspan', rowSpan).addClass('spanned');
else
$thisCell.css('display','none'); // hide instead of removing
//$thisCell.remove();
matrix[row][startCol] = 1; // mark these cells as blocked
}
/*for (var row = sRow; row <= eRow; row++) {
var $thisCell = $cells.eq(row * numCols + startCol);
$thisCell.addClass('spanned');
matrix[row][startCol] = 1; // mark these cells as blocked
} */
}
function removeCellSpan(cell) {
var sRow, eRow;
var $thisCell = $(cell);
var rowSpan = $thisCell.attr('rowspan') - 1;
var cellPos = findCell(cell, true);
if (cellPos) {
startCol = cellPos.col;
sRow = cellPos.row;
eRow = cellPos.row + rowSpan;
var $currentCells = $table.find("td");
// add back missing cells
for (var row = sRow; row <= eRow; row++) {
if (row === sRow) {
var $thisCell = $cells.eq(row * numCols + startCol);
$thisCell.attr('rowspan', 1).removeClass('spanned');
} else {
var $thisCell = $currentCells.eq(row * numCols + startCol);
$thisCell.css('display','table-cell');
}
matrix[row][startCol] = 0; // mark this cell as empty
}
}
}
$(document).on("contextmenu", "td", function(e) {
e.preventDefault();
if ($(this).hasClass('spanned') === false) {
console.log("This cell is not spanned.");
return false;
} else {
removeCellSpan(this);
}
});
}
#control-panel p {
display: inline;
font-size: 14px;
}
table {
font-family: Arvo, Arial, sans-serif;
border-collapse: separate !important;
border-top: 2px solid #4DC7E8;
border-left: 2px solid #4DC7E8;
border-radius: 6px;
}
.days-of-the-week th {
width: 160px;
padding-top: 10px;
padding-bottom: 10px;
text-align: center;
border-right: 2px solid #4DC7E8;
border-bottom: 2px solid #4DC7E8;
border-radius: 6px;
}
tr td,
th {
border-right: 2px solid #4DC7E8;
border-bottom: 2px solid #4DC7E8;
border-radius: 6px;
}
td {
height: 50px;
}
.days-of-the-week .time-col {
width: 80px;
}
tbody th {
text-align: right;
padding: 5px 3px;
}
.highlight {
background-color: rgba(255, 108, 78, 0.3);
}
.spanned {
background-color: green;
}
.appt-text {
background-color: inherit;
border: 0px;
width: 100%;
height: 100%;
outline: none;
cursor: default;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row" id="control-panel">
<button onclick="addAppt()" id="add-appt" title="Add Appointment">+</button>
<p>= Add Appointment</p>
</div>
<div class="row text-center">
<table id="myTable">
<thead>
<tr class="days-of-the-week">
<th scope="col" class="time-col"></th>
<th scope="col">Sunday</th>
<th scope="col">Monday</th>
<th scope="col">Tuesday</th>
<th scope="col">Wednesday</th>
<th scope="col">Thursday</th>
<th scope="col">Friday</th>
<th scope="col">Saturday</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" class="rowHdr">10:00am</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">10:30am</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">11:00am</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">11:30am</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">12:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">12:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">1:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">1:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">2:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">2:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">3:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">3:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">4:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">4:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">5:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">5:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">6:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">6:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">7:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">7:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">8:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">8:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">9:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">9:30pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
<tr>
<th scope="row" class="rowHdr">10:00pm</th>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
<td rowspan=""><textarea cols="20" rows="5" class="appt-text"></textarea></td>
</tr>
</tbody>
</table>
</div>
Upvotes: 1