HTML Table Range Selection/Deselection in JavaScript - javascript

I need the functionality of selecting and deselecting a table cell range. The attached code works perfectly but it uses JQuery while the rest of my project uses plain JavaScript. onmousedown, onmouseover, and onmouseup are the three mouse events used in this code.
I tried to convert this JQuery code to plain JavaScript but could not succeed. I would very much appreciate if you can help me in this regard.
Thank you!
var table = $("#table");
var isMouseDown = false;
var startRowIndex = null;
var startCellIndex = null;
function selectTo(cell) {
var row = cell.parent();
var cellIndex = cell.index();
var rowIndex = row.index();
var rowStart, rowEnd, cellStart, cellEnd;
if (rowIndex < startRowIndex) {
rowStart = rowIndex;
rowEnd = startRowIndex;
} else {
rowStart = startRowIndex;
rowEnd = rowIndex;
}
if (cellIndex < startCellIndex) {
cellStart = cellIndex;
cellEnd = startCellIndex;
} else {
cellStart = startCellIndex;
cellEnd = cellIndex;
}
for (var i = rowStart; i <= rowEnd; i++) {
var rowCells = table.find("tr").eq(i).find("td");
for (var j = cellStart; j <= cellEnd; j++) {
rowCells.eq(j).addClass("selected");
}
}
}
table.find("td").mousedown(function (e) {
isMouseDown = true;
var cell = $(this);
table.find(".selected").removeClass("selected"); // deselect everything
if (e.shiftKey) {
selectTo(cell);
} else {
cell.addClass("selected");
startCellIndex = cell.index();
startRowIndex = cell.parent().index();
}
return false; // prevent text selection
})
.mouseover(function () {
if (!isMouseDown) return;
table.find(".selected").removeClass("selected");
selectTo($(this));
})
.bind("selectstart", function () {
return false;
});
$(document).mouseup(function () {
isMouseDown = false;
});
table td {
border: 1px solid #999;
width: 40px;
height: 15px;
margin: 10px;
}
td.selected {
background-color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
Click and drag mouse or use shift key to select cells.
<table id="table">
<tr>
<td></td><td></td><td></td><td></td>
</tr>
<tr>
<td></td><td></td><td></td><td></td>
</tr>
<tr>
<td></td><td></td><td></td><td></td>
</tr>
<tr>
<td></td><td></td><td></td><td></td>
</tr>
</table>
</body>

Use querySelectorAll and play with the index or use foreach depending on the use.
window.onload = function() {
var table = document.getElementById('table');
var isMouseDown = false;
var startRowIndex = null;
var startColumnIndex = null;
table.querySelectorAll('tr').forEach(function(tr, rowIndex) {
tr.querySelectorAll('td').forEach(
function(td, columnIndex) {
td.addEventListener('mousedown', function() {
isMouseDown = true;
startRowIndex = rowIndex;
startColumnIndex = columnIndex;
table.querySelectorAll('td').forEach(function(td) {
td.removeAttribute('class')
})
setSelectedInRange(startRowIndex, startColumnIndex, rowIndex, columnIndex)
})
td.addEventListener('mouseover', function() {
if(!isMouseDown) return;
setSelectedInRange(startRowIndex, startColumnIndex, rowIndex, columnIndex)
})
}
)
})
document.addEventListener('mouseup', function() {
isMouseDown = false;
})
}
function setSelectedInRange(rowStart, columnStart, rowEnd, columnEnd) {
var table = document.getElementById('table');
table.querySelectorAll('td').forEach(function(td) {
td.removeAttribute('class')
})
if(rowStart > rowEnd) {
var temp = rowStart;
rowStart = rowEnd;
rowEnd = temp;
}
if(columnStart > columnEnd) {
var temp = columnStart;
columnStart = columnEnd;
columnEnd = temp;
}
var rows = table.querySelectorAll('tr');
for(var i = rowStart; i <= rowEnd; i++) {
var currentRow = rows[i];
var columns = currentRow.querySelectorAll('td');
for(var j = columnStart; j <= columnEnd; j++) {
columns[j].setAttribute('class', 'selected')
}
}
}

Related

How do i get the cell id of a table

This is the board drawing function
this.drawBoard=function(){
document.write('<table id="newGame">');
for(let i=0;i<boxes;i++){
document.write('<tr>');
for(let j=0;j<boxes;j++){
document.write(' <td id=" '+ i +' , '+ j + ' " ');
document.write('</td>');
}
document.write('</tr>');
}
document.write('</table>');
b.onClickEvent();
}
While this is the function to get the id
i found that by changing the 0 index in line clicked = cell.getElementsByTagName("td")[0].id; I can get to the column I want but don't know how to manipulate it to get the exact cell id
this.onClickEvent=function() {
let table = document.getElementById("newGame");
let rows = table.getElementsByTagName("tr");
for (i = 0; i < rows.length; i++) {
let currentRow = table.rows[i];
var createClickHandler =
function(cell) {
return function() {
clicked = cell.getElementsByTagName("td")[0].id;
console.log(clicked);
let numbers=[];
clicked.replace(/(\d[\d\.]*)/g, function( x ) { var n = Number(x); if (x == n) { numbers.push(x); } })
b.spliceGrid(numbers[0],numbers[1]);
numbers=[];
};
};
currentRow.onclick = createClickHandler(currentRow);
}
}
tables elements have properties like rowIndex or cellIndex, you don't need to use id for each cell.
Then, you even have access to any cell you want to get or set anything on it, like change it's background ( to pink ):
myTable.rows[2].cells[1].classList.add('BgPink');
this way:
const myTable = newTable(4,5);
myTable.rows[2].cells[1].classList.add('BgPink');
function newTable( rowCount, cellCount )
{
const TableX = document.body.appendChild(document.createElement('table'))
;
for( let r=0; r < rowCount; ++r )
{
let newRow = TableX.insertRow();
for( let c=0; c < cellCount; ++c )
{
newRow.insertCell().textContent = `${r},${c}`;
}
}
return TableX;
}
myTable.onclick = e => // event delegation method
{
if (!e.target.matches('td')) // to accept only clicks over a <TD> element
return;
let
index_Cell = e.target.cellIndex
, index_Row = e.target.closest('tr').rowIndex
;
console.clear();
console.log('clicked on :', index_Row, index_Cell );
}
table {
border-collapse : collapse;
}
table td {
padding : .2em .8em;
border : 1px solid darkblue;
text-align : center;
cursor : pointer;
}
.BgPink {
background : pink;
}

How to get out of onmousemove event?

I am dynamically creating a table, with a set number of rows and columns. Once you fire the onmousedown event, it will run a loop to find out which <td></td> you pressed and are moving over, and set it's background color to red.
JSFiddle
I just don't understand how to stop the background color changing once you have fired the onmouseup event.
This is the code part that I am stuck on:
function mousedown() {
var elements = document.querySelector('table').getElementsByTagName('td');
for (var i = 0; i < elements.length; i++) {
elements[i].onmousemove = function() {
this.style.background = "red";
}
elements[i].onmouseup = function() {
this.style.background = "none";
};
}
}
Simply use a flag:
var flag = false;
function mousedown() {
flag = true;
var elements = document.querySelector('table').getElementsByTagName('td');
for (var i = 0; i < elements.length; i++) {
elements[i].onmousemove = function() {
if (flag) this.style.background = "red";
}
elements[i].onmouseup = function() {
flag = false;
};
}
}
JSFiddle here.
I added:
function mouseup() {
var elements = document.querySelector('table').getElementsByTagName('td');
for (var i = 0; i < elements.length; i++) {
elements[i].onmousemove = null;
}
}
and
table.onmouseup = mouseup;
Slightly different approach: http://jsfiddle.net/QYg28/
(function() {
var body = document.querySelector('body');
var table = document.createElement('table');
function createTable(rows, columns) {
var tr;
var td;
for (var i = 0; i < rows; i++) {
tr = document.createElement('tr');
table.appendChild(tr);
for (var j = 0; j < columns; j++) {
td = document.createElement('td');
td.onmouseover = function(){
if(mouseDown)
this.style.background = "red";
}
tr.appendChild(td);
}
}
body.appendChild(table);
}
mouseDown = false;
function mousedown() {
mouseDown = true;
}
function mouseup(){
mouseDown = false;
}
window.onload = createTable(25, 25);
table.onmousedown = mousedown;
table.onmouseup = mouseup;
})();

MineField with Js

I am trying to create a minefield game with javascript.
When I click on clear ro**w it gives "passed" but sometimes "died" too or clicking on **mined row gives sometimes "passed". It's supposed to give only "passed" with clear and "died" with mined row.
I can't figure out the reason..
Could you see it?
Here is my code so far:
var level = 9;
// create the table
var body = document.getElementsByTagName("body")[0];
var tbl = document.createElement("table");
tbl.setAttribute('id', 'myTable');
var tblBody = document.createElement("tbody");
//Create 2d table with mined/clear
for (var i = 1; i <= 10; i++) {
var row = document.createElement("tr");
document.write("<br/>");
for (var x = 1; x <= 10; x++) {
var j = Math.floor(Math.random() * 50);
if (j <= 15) {
j = "mined";
} else {
j = "clear";
}
var cell = document.createElement("td");
var cellText = document.createTextNode(j + "");
cell.appendChild(cellText);
row.appendChild(cell);
}
tblBody.appendChild(row);
}
tbl.appendChild(tblBody);
body.appendChild(tbl);
tbl.setAttribute("border", "1");
//Check which row is clicked
window.onload = addRowHandlers;
function addRowHandlers() {
var table = document.getElementById("myTable");
var rows = table.getElementsByTagName("td");
for (p = 0; p < rows.length; p++) {
var currentRow = table.rows[p];
var createClickHandler = function (row) {
return function () {
var cell = row.getElementsByTagName("td")[1];
var id = cell.innerHTML;
if (id == "mined") {
alert("Died");
} else {
alert("Passed!");
}
};
}
currentRow.onclick = createClickHandler(currentRow);
}
}
JSFiddle Here:
http://jsfiddle.net/blowsie/ykuyE/
Thanks in advance!
Its' this line, which causes the faulty behaviour: var cell = row.getElementsByTagName("td")[1]; Everytime a click is made, the [1] selects the 2nd cell of a column, no matter which cell was actually clicked.
I modified your fiddle: http://jsfiddle.net/ykuyE/1
The onclick handler is now applied to the individual cell directly, when the table is created.
cell.onclick = function() {
if (this.innerHTML == "mined") {
alert("Died");
} else {
alert("Passed!");
}
}

jqgrid set cell editable false dynamically on condition

I have a jqgrid populated by some fields. I want some of the cells to be
editable:true
or
editable:false
based on a condition
Here's my function (EDITED):
var grid = $("#mygrid");
var getColumnIndexByName = function(gr,columnName) {
var cm = gr.jqGrid('getGridParam','colModel');
for (var i=0,l=cm.length; i<l; i++) {
if (cm[i].name===columnName) {
return i; // return the index
}
}
return -1;
};
function abilitaDisabilitaEditRecord() {
var pos=getColumnIndexByName(grid,'descrizione');
var pos2=getColumnIndexByName(grid,'endDate');
var allIds = $('#mygrid').jqGrid('getDataIDs');
var cells = $("tbody > tr.jqgrow > td:nth-child("+(pos+1)+")",grid[0]);
var cells2 = $("tbody > tr.jqgrow > td:nth-child("+(pos2+1)+")",grid[0]);
for (var i = 0; i < allIds.length; i++) {
for (var j=0; j<cells.length; j++) {
var cell = $(cells[j]);
var cell2 = $(cells2[j]);
var checkDataFine = $('#mygrid').jqGrid('getCell', allIds[i], 'date');
if (!checkDataFine==false) {
cell.addClass('not-editable-cell');
cell2.addClass('not-editable-cell');
}
}
}
}
you can call this in you else condition, i didn't test it, but i think this should work
$("#mygrid").jqGrid('restoreRow',allIds[i]);
Here's the working solution:
var grid = $("#mygrid");
var getColumnIndexByName = function(gr,columnName) {
var cm = gr.jqGrid('getGridParam','colModel');
for (var i=0,l=cm.length; i<l; i++) {
if (cm[i].name===columnName) {
return i;
}
}
return -1;
};
function changeEditableByContain() {
var pos=getColumnIndexByName(grid,'field1');
var pos2=getColumnIndexByName(grid,'field2');
var pos3=getColumnIndexByName(grid,'field3');
var pos4=getColumnIndexByName(grid,'field4');
var allIds = $('#mygrid').jqGrid('getDataIDs');
var cells = $("tbody > tr.jqgrow > td:nth-child("+(pos+1)+")",grid[0]);
var cells2 = $("tbody > tr.jqgrow > td:nth-child("+(pos2+1)+")",grid[0]);
var cells3 = $("tbody > tr.jqgrow > td:nth-child("+(pos3+1)+")",grid[0]);
var cells4 = $("tbody > tr.jqgrow > td:nth-child("+(pos4+1)+")",grid[0]);
for (var i = 0; i < allIds.length; i++) {
var cell = $(cells[i]);
var cell2 = $(cells2[i]);
var cell3 = $(cells3[i]);
var cell4 = $(cells4[i]);
if (condition...) {
cell.addClass('editable-cell');
cell2.addClass('editable-cell');
cell3.addClass('editable-cell');
cell4.addClass('editable-cell');
}
else{
cell.addClass('not-editable-cell');
cell2.addClass('not-editable-cell');
cell3.addClass('not-editable-cell');
cell4.addClass('not-editable-cell');
}
}
}
and then call onloadcomplete:
changeEditableByContain();

Toggle table row visibility based on presence of td class

How can I toggle multiple rows in a table if the <td> class is set to an specific class. For instance toggle all rows if they contain the class="foo".
<table id="bar">
<tr>
<td>hello</td>
<td class="foo">there</td>
<td class="foo">bye</td>
</tr>
</table>
Here's a non-jQuery solution, written just for you: http://phrogz.net/tmp/toggling_rows_with_class.html
Here's the relevant JS:
window.onload = function() {
var visible = true;
document.getElementById('toggle').onclick = function() {
visible = !visible;
var tds = findElementsWithClass('td', 'foo');
for (var i=0, len=tds.length; i<len; ++i) {
tds[i].parentNode.style.display = visible ? '' : 'none';
}
};
}
function findElementsWithClass(tagName, className) {
if (document.querySelectorAll) {
return document.querySelectorAll(tagName + "." + className);
} else {
var results = [];
var all = document.getElementsByTagName(tagName);
var regex = new Regexp("(?:^|\\s)" + tagName + "(?:\\s|$)");
for (var i=0, len=all.length; i<len; ++i) {
if (regex.test(all[i].className)) {
results.push(all[i]);
}
}
return results;
}
}
Modify the class
Why is everyone using selectors? There is already a class attached to all the appropriate elements, so why not just modify the class?
This function will find the class of a given name, and set an attribute for that class. Be careful if you have multiple classes with coincident names in different stylesheets, because the function isn't!
function changeStyle(stylename,attribute,newvalue) {
var cssRules = 'rules';
if(document.styleSheets[0].cssRules) {
cssRules = 'cssRules';
}
for(var sheetId=0; sheetId<document.styleSheets.length; sheetId++) {
var sheet = document.styleSheets[sheetId];
for(var ruleId=0; ruleId<sheet[cssRules].length; ruleId++) {
var rule = sheet[cssRules][ruleId];
if(rule.selectorText == "."+stylename) {
rule.style.setProperty(attribute,newvalue,"");
}
}
}
return false;
}
Now, just call
changeStyle('foo','display','none');
and the cells should disappear, then with 'block' to get them back (IE can't do the more recent display styles like ). I suspect that in a table you'll want to hide entire rows rather than just cells, but you can also make them disappear by setting visibility to hidden - they will still take up space, but not draw.
See, no jquery, no homemade element selectors. Just a slightly annoying bit of javascript to loop through the stylesheets and their rules...
td = document.getElementsByTagName('td');
for (var i = 0; i < td.length; i++) {
if (td[i].className === 'foo')
if (!td[i].style.display)
td[i].style.display = 'none';
else
td[i].style.display = '';
}
}
http://jsfiddle.net/qwertymk/cyZn9/2/
Something like this should do it:
var trs = document.getElementsByTagName("tr");
for (var i in trs) {
var tr = trs[i];
if (tr.getElementsByClassName("foo").length > 0)
tr.style.display = (tr.style.display == "none" ? "block" : "none");
}
This will toggle the display on any TR that contains a child with class="foo".
Something like this?
$("table td.specific_class").toggle();
Edit
/* Go through the table rows */
var trs = document.getElementsByTagName("tr");
for (var i = 0; i < trs.length; i++ ) {
var myClass, tds, line_done = false;
/* Go through the table cells */
tds = trs[i].getElementsByTagName("td");
for ( var k = 0; k < tds.length; k++ ){
/* Check each class of each cell */
myClasses = tds[k].className.split(' ');
for ( var j = 0; j < myClasses.length; j++ ){
/* If the class corresponds we toggle the row and break until the next row */
if ( myClasses[j].className == "foo" ){
trs[i].style.display = trs[i].style.display == "none" ? "block" : "none";
line_done = 1;
break;
}
}
if ( line_done ){
break;
}
}
}
try this one
<html>
<head>
<title>test</title>
<script type="text/javascript">
var toggle = function (action) {
var trs = document.getElementById('bar').getElementsByTagName('tr'),
trs_count = trs.length,
needed = [],
total = 0,
pattern = /\bfoo\b/g,
initial= 'show';
for (i=0; i<trs_count; i++) {
var tds = trs[i].getElementsByTagName('td'),
tds_count = tds.length;
for (j=0; j<tds_count; j++) {
if (pattern.exec(tds[j].className)) {
needed.push(tds[j]);
total++;
}
}
}
toggle = function (action) {
if (this.display == null) {
this.display = initial;
}
if (action == null) {
this.display = (this.display == 'hide') ? 'show' : 'hide';
}
else {
this.display = action;
}
for (i=0; i<total; i++) {
if (this.display == 'show') {
needed[i].style.display = 'block';
}
else {
needed[i].style.display = 'none';
}
}
return true;
}
return toggle();
}
</script>
</head>
<body>
<table id="bar">
<tr><th>Header</th></tr>
<tr><td class="foo">1 Data foo</td></tr>
<tr><td>2 Data</td></tr>
<tr><td class="foo">3 Data foo</td></tr>
<tr><td>4 Data</td></tr>
</table>
<button type="button" onclick="toggle()">toggle</button>
</body>
</html>

Categories

Resources