eventlistner in table each row only trigger last row - javascript

I'm writing a table allow user click table row and show the hidden rows.
I use addEventListener with loop to allow each table row call the hidden rows under the row.
For example:
row A
row B
after clicking row A, hidden rows will show under row A and above row B
row A
hidden row 1
hidden row 2
row B
however, my code will always and only trigger the last row. If there are 15 rows in the table could click and show rows. Only the hidden rows of 15th row will show. No matter I click 1st row or 15th row, the web always show the 15th row.
var id = "";
var count = 1;
var table = document.getElementById("header_table");
for (var i = 0; i < r.length - 1; i++) {
id = 'detail_' + i;
console.log(id);
table.rows[count].addEventListener('click', function () {
console.log("clicking row " + i);
$("." + id).toggle();
});
count = count + 6;
}

Thank you all, I solved my question. I should bind the parameter into the function.
Reference: Function.prototype.bind

I had a similar problem with my program.
I needed to add an event listener to each row of a table, so I made a for-each loop of the rows and added the event listener:
for (var r of table.childNodes[0].childNodes) {
r.addEventListener("click", () => {
// Do Stuff To Row 'R'
for (var c of r.childNodes) { // Access all cells in row 'r'
c.style.backgroundColor = "blue" // Change the background of all cells in row 'r'
}
}
}
The way the program ran made it so when I would click on a table, row r held the value of the last row in the table, making it such that if row r were to be changed in the event listener, it would only be changing the last row.
It took me hours to figure out, but the solution was to change the loop from var r to let r:
for (let r of table.childNodes[0].childNodes) {
r.addEventListener("click", () => {
// Do Stuff To Row 'r'
for (var c of r.childNodes) { // Access all cells in row 'r'
c.style.backgroundColor = "blue" // Change the background of all cells in row 'r'
}
}
}

Related

How to hide columns in HTML table if rows under this column is empty?

I have a table with this columns in HTML page. Razor is used for output. The values are generated dynamically from the database.
ID
Name
Week_1
Week_2
...
Week_52
1
Test1
3
1
2
Test2
2
3
3
3
Test3
5
1
And for example I want to hide column Week_52 because column has no values in all rows.
The column (table -> thead -> tr -> th) has no children, so I don't understand how they can be associated with values in (table -> tbody -> tr -> td).
How can I do this in vanilla javascript? Or maybe there is another solution?
Edited. 0 -> empty
I solved the problem. I added id="myAnchor" to the table with attribute data-currentweek. (I get currentweek value from database using Razor)
function hideWeekColumns(){
const myAnchor = document.getElementById("myAnchor");
currentweek = myAnchor.dataset.currentweek-1;
let weeks = document.getElementsByClassName('column week');
let weeks_array = Array.from( weeks );
weeks_array.splice(0, currentweek); // from position 0, remove 1 element
weeks_array.forEach(element => {
element.style.display = 'none';
});
};
hideWeekColumns();
A slightly different take on the last answer with comments, still in jquery though.
hideemptycols()
function hideemptycols() {
var hide = {} //set an object to track the qty of cells within each column that HAVE zero data
//to hide a column including header, all cells within need to be empty
$('table tbody tr').each(function () { // iterate through all rows
$(this).find('td').each(function (i, v) { // with each of the rows cells
if (!hide[i]) { hide[i] = 0 } //check if the object contains the column index, if not add it and give it a zero value
if ($.trim($(v).text()).length == 0) { //check the length of data if zero
$(v).hide()// hide the cell
}
else {
hide[i] += 1//add 1 against the column for every cell found with data
}
})
})
$.each(hide,function (index, EmptyQty) { //loop the object
if (EmptyQty == 0) { //with each column if the qty of cells with dataequals zero then hide the column
$('table tr th:nth-child(' + index + ')').hide()//get the column by index and hide
}
})
}
By Using jquery you can achieve this
The first step includes a jquery file and then
at bottom add
<script>
let hide = {}
for(let i = 1; i <= 52; i++){
hide[i] = true
}
$('table tbody tr').each(function(){
let cn = -1
$(this).find('td').each(function(){
if($.trim($(this).text()) != '0' && hide[cn]){
hide[cn] = false
}
cn++
})
})
console.log(hide)
for (var key of Object.keys(hide)) {
if(hide[key]){
$('table tr td:nth-child('+key+')').hide()
}
}
</script>

I can't access nested JavaScript function in index.jsp

I have a function that is supposed to perform pagination on a table. Within that function is another function that needs to be executed when I click on the numbers in the navigation bar. The code works fine in VSCode. When I put the code in index.jsp in eclipse, the function gives the error: Uncaught ReferenceError: sort is not defined at HTMLButtonElement.onclick.
// get the table element
var table = document.getElementById("disposalTable"),
// number of rows per page
n = 5,
// number of rows of the table
rowCount = table.rows.length,
// get the first cell's tag name (in the first row)
firstRow = table.rows[0].firstElementChild.tagName,
// boolean var to check if table has a head row
hasHead = (firstRow === "TH"),
// an array to hold each row
tr = [],
// loop counters, to start count from rows[1] (2nd row) if the first row has a head tag
i,ii,j = (hasHead)?1:0,
// holds the first row if it has a (<TH>) & nothing if (<TD>)
th = (hasHead?table.rows[(0)].outerHTML:"");
// count the number of pages
var pageCount = Math.ceil(rowCount / n);
// if we had one page only, then we have nothing to do ..
if (pageCount > 1) {
// assign each row outHTML (tag name & innerHTML) to the array
for (i = j,ii = 0; i < rowCount; i++, ii++)
tr[ii] = table.rows[i].outerHTML;
// create a div block to hold the buttons
table.insertAdjacentHTML("afterend","<div id='buttons'></div");
// the first sort, default page is the first one
sort(1);
}
// ($p) is the selected page number. it will be generated when a user clicks a button
function sort(p) {
/* create ($rows) a variable to hold the group of rows
** to be displayed on the selected page,
** ($s) the start point .. the first row in each page, Do The Math
*/
var rows = th,s = ((n * p)-n);
for (i = s; i < (s+n) && i < tr.length; i++)
rows += tr[i];
// now the table has a processed group of rows ..
table.innerHTML = rows;
// create the pagination buttons
document.getElementById("buttons").innerHTML = pageButtons(pageCount,p);
// CSS Stuff
document.getElementById("id"+p).setAttribute("class","active");
}
// ($pCount) : number of pages,($cur) : current page, the selected one ..
function pageButtons(pCount,cur) {
/* this variables will disable the "Prev" button on 1st page
and "next" button on the last one */
var prevDis = (cur == 1)?"disabled":"",
nextDis = (cur == pCount)?"disabled":"",
/* this ($buttons) will hold every single button needed
** it will creates each button and sets the onclick attribute
** to the "sort" function with a special ($p) number..
*/
buttons = "<input type='button' value='<< Prev' onclick='sort("+(cur - 1)+")' "+prevDis+">";
for (i=1; i<=pCount;i++)
buttons += "<input type='button' id='id"+i+"'value='"+i+"' onclick='sort("+i+")'>";
buttons += "<input type='button' value='Next >>' onclick='sort("+(cur + 1)+")' "+nextDis+">";
return buttons;
}
I figured it out. Instead of trying to access the nested function. Put the pager code into a function. Then create an instance of the function as a variable. And call that variable every time.
For instance:
let pager = new Pager();
Then the onclick method will call pager.sort(1) or pager.sort(2) everytime.

HTML table cell event listeners don't seem to work?

I'm working on a HTML / Javascript project to create a simple game of Minesweeper. In order to create a clickable grid of cells, I adopted code that I found in another thread. That code works perfectly on its own (see this JSBin for more details):
var lastClicked;
var grid = clickableGrid(10,10,onClick);
document.body.appendChild(grid);
// This is the clickableGrid object constructor. It takes in the number of rows and columns
// for the grid, and a callback function that will be executed when a cell is clicked
function clickableGrid( rows, cols, callback ){
// 'i' is the variable that will store the number in each cell. A 10x10
// grid, for example, will see i range from 1 (top left cell) to 100 (bottom right cell)
var i=0;
// Create the table element and assign it a class name so that CSS formatting
// may be applied
var grid = document.createElement('table');
grid.className = 'grid';
// Build the grid row by row
for (var r=0;r<rows;++r){
// Create a table row element and append it to the table element ('grid')
var tr = grid.appendChild(document.createElement('tr'));
tr.row = r;
// Build the row cell by cell
for (var c=0;c<cols;++c){
// Create the cell element and append it to the row element ('tr')
var cell = tr.appendChild(document.createElement('td'));
// Input the number to the cell's innerHTML
cell.innerHTML = ++i;
// Add an event listener that will execute the callback function
// when the cell is clicked, using the cell's element and information
cell.addEventListener('click',(function(el, r, c, i){
return function() {
callback(el, r, c, i);
}
})(cell, r, c, i),false);
}
}
return grid;
}
// This function contains the actions we want to be executed when the click occurs
function onClick(el, row, col, i) {
// Log to the console the details of the cell that was clicked
console.log("You clicked on element:",el);
console.log("You clicked on row:",row);
console.log("You clicked on col:",col);
console.log("You clicked on item #:",i);
// Record in the element that it was clicked
el.className='clicked';
// If the element is not the same as
if (lastClicked) lastClicked.className='';
lastClicked = el;
}
However, I can't seem to make it work properly in my Minesweeper game. The "mined" grid gets built and appended to the DOM, but the innerHTML and listeners are not applied. This other JSBin contains all the game code I have so far. The current process is:
1) Run init to initialize the page and create all elements. Part of this includes adding an event listener to the "New Game" button.
2) When the "New Game" button is clicked, create a clickable grid that will be "mined". At the moment, no mines get placed, but the code tries to place an "X" inside each cell. Furthermore, each cell should have an event listener attached to it.
The relevant code section in the game is:
function startGame() {
var gameDifficulty = document.getElementsByTagName("select")[0].value;
var currGrid = document.querySelector('.grid');
var newGrid = new minedGrid(gameDifficulty, onClick);
currGrid.parentNode.replaceChild(newGrid, currGrid);
}
// minedGrid object constructor: creates and returns a fully-mined and
// prepared Minesweeper grid
function minedGrid(difficulty, callback){
var rows, cols, mines;
var newGrid = document.createElement('table');
newGrid.className = 'grid';
switch (difficulty) {
case 0:
rows = 10;
cols = 10;
mines = 10;
break;
case 1:
rows = 16;
cols = 16;
mines = 40;
break;
case 2:
rows = 16;
cols = 30;
mines = 99;
break;
default:
rows = 10;
cols = 10;
mines = 10;
break;
}
for (var r = 0; r < rows; ++r) {
var tr = newGrid.appendChild(document.createElement('tr'));
for (var c = 0; c < cols; ++c) {
var cell = tr.appendChild(document.createElement('td'));
cell.innerHTML = "X";
cell.addEventListener('click',(function(el, r, c){
return function() {
callback(el, r, c);
}
})(cell, r, c),false);
}
}
return grid;
}
// This function contains the actions we want to be executed when the click occurs
function onClick(el, row, col) {
// Log to the console the details of the cell that was clicked
console.log("You clicked on element:",el);
console.log("You clicked on row:",row);
console.log("You clicked on col:",col);
// Record in the element that it was clicked
el.className='clicked';
// If the element is not the same as
if (lastClicked) lastClicked.className='';
lastClicked = el;
}
Solution by OP.
Chris G, who commented on the question, was absolutely correct when noting that the grid is "never appended to the document." The issue is that the minedGrid function has return grid; at the bottom, when it should do return newGrid; instead. If you would like to see a revised version of this code, please refer to this link.
I want to again specifically highlight Chris G for his help, as well as his version of the minedGrid code: Chris G's JSBin.

how can I figure out that cells are of same row

I have a html table with 3 columns and 7rows. When I click on a cell its bg is changed to red. I want to make sure that only one cell from each row is selected. So if I am clicking on a cell from row1, which has already a cell selected, I want the prev cell to be deselected and the new cell selected. I want to know how can I figure out that cells are of same row.
Given a table cell, its parentNode will be the containing row, and table rows have a cells property that contains a collection of all the cells. So you can loop through that collection.
function selectCell(cell) {
var siblings = cell.parentNode.cells;
for (var i = 0; i < siblings.length; i++) {
if (i != cell.cellIndex) {
siblings[i].style.backgroundColor = normalBGColor;
}
}
cell.style.backgroundColor = highlightBGColor;
}
If you are using jQuery, in your click handler, do this:
function() {
$(this).closest('tr').find('td').css('backgroundColor', '');
$(this).css('backgroundColor', 'red');
}
You can iterate through the rows, and iterate through the cell in an embedded for loop. The solution provided by John Hartsock on this stackoverflow page should allow you to qualify each cell by row and column.
How do I iterate through table rows and cells in javascript?

Hide table column ondblclick

I have a table and I want to hide a column when I double click a column.
Code for hiding a column is practically all around Stack Overflow. All I need is a hint on how/where to add the ondblclick event so I can retrieve the identity of a <td> within a <table>.
Here are two solutions that should work. One done with jQuery and one with only standard Javascript.
http://jsfiddle.net/GNFN2/2/
// Iterate over each table, row and cell, and bind a click handler
// to each one, keeping track of which column each table cell was in.
var tables = document.getElementsByTagName('table');
for (var i = 0; i < tables.length; i++) {
var rows = tables[i].getElementsByTagName('tr');
for (var j = 0; j < rows.length; j++) {
var cells = rows[j].getElementsByTagName('td');
for (var k = 0; k < cells.length; k++) {
// Bind our handler, capturing the list of rows and colum.
cells[k].ondblclick = column_hide_handler(rows, k);
}
}
}
// Get a click handler function, keeping track of all rows and
// the column that this function should hide.
function column_hide_handler(rows, col) {
return function(e) {
// When the handler is triggered, hide the given column
// in each of the rows that were found previously.
for (var i = 0; i < rows.length; i++) {
var cells = rows[i].getElementsByTagName('td');
if (cells[col]) {
cells[col].style.display = 'none';
}
}
}
}
With jQuery it is much cleaner. This method also uses event bubbling, so you don't need to bind an event handler to each table cell individually.
http://jsfiddle.net/YCKZv/4/
// Bind a general click handler to the table that will trigger
// for all table cells that are clicked on.
$('table').on('dblclick', 'td', function() {
// Find the row that was clicked.
var col = $(this).closest('tr').children('td').index(this);
if (col !== -1) {
// Go through each row of the table and hide the clicked column.
$(this).closest('table').find('tr').each(function() {
$(this).find('td').eq(col).hide();
});
}
});
You can do this way:
<td ondblclick="this.style.display = 'none';">Some Stuff</td>
Here this refers to current td clicked.
Working Example
To go unobtrusive, you can do that easily using jQuery if you want:
$('#tableID td').dblclick(function(){
$(this).hide();
});
Due to lack of answears I came up with a workaround, which is a big ugly, but it works fine.
On the window load event I decided to iterate the table and set each 's onclick event to call my show_hide_column function with the column parameter set from the iteration.
window.onload = function () {
var headers = document.getElementsByTagName('th');
for (index in headers) {
headers[index].onclick = function (e) {
show_hide_column(index, false)
}
}
}
show_hide_column is a function that can be easily googled and the code is here:
function show_hide_column(col_no, do_show) {
var stl;
if (do_show) stl = 'table-cell'
else stl = 'none';
var tbl = document.getElementById('table_id');
var rows = tbl.getElementsByTagName('tr');
var headers = tbl.getElementsByTagName('th');
headers[col_no].style.display=stl;
for (var row=1; row<rows.length; row++) {
var cels = rows[row].getElementsByTagName('td')
cels[col_no].style.display=stl;
}
}
Note: my html only had one table so the code also assumes this. If you have more table you should tinker with it a little. Also it assumes the table has table headers ();
Also I noted this to be an ugly approach as I was expecting to be able to extract the index of a table cell from the table without having to iterate it on load.

Categories

Resources