I have a huge table which most of the entries are "display:none". When the user click on an entity the rows should appear until the same class happen.
The table looks something like this:
<tbody>
<tr id="1" class="department"></tr>
<tr style="display:none;" id="43" class="sub"></tr>
<tr style="display:none;" id="55" class="sub"></tr>
<tr style="display:none;" id="85" class="sub"></tr>
<tr id="6" class="department"></tr>
<tr style="display:none;" id="150" class="sub"></tr>
</tbody>
So by clicking on id = 1 row the table should expand to show id= 43,55,85 (until reach to class="department" again)
I know it's a bit confusing so feel free to ask me questions if you need more explanation.
In plain javascript, you can do something like this:
function hasClass(elem, cls) {
var str = " " + elem.className + " ";
var testCls = " " + cls + " ";
return(str.indexOf(testCls) != -1) ;
}
var table = document.getElementById("myTable");
var rows = table.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
(function(index) {
rows[index].addEventListener("click", function(e) {
for (var i = index + 1; i < rows.length; i++) {
var row = rows[i];
if (hasClass(row, "department")) {
break;
}
row.style.display = "";
}
});
})(i);
}
Working demo: http://jsfiddle.net/jfriend00/Dh3p3/
The code uses a closure to capture the row index for each row, such that when it is clicked on, you can use that index into the previously captured array of rows. It then walks down that array showing rows until it finds an item that has the "department" class.
FYI, this puts event listeners on all the rows so if you manually show one of the hidden rows, it can be clicked on and have the same behavior. If you only want click handlers on the class="department" rows, the code can easily be modified to do that too.
Here's a version that works with a hierarchy of classes. It expands only items at the next level on a click:
function hasClass(elem, cls) {
var str = " " + elem.className + " ";
var testCls = " " + cls + " ";
return(str.indexOf(testCls) != -1) ;
}
var table = document.getElementById("myTable");
var rows = table.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
(function(index) {
rows[index].addEventListener("click", function(e) {
// nothing to do if clicking on the last item
if (index + 1 >= rows.length) {
return;
}
// get class name to stop on
var clsToStopOn = this.className;
// get class name to show
var clsToShow = rows[index + 1].className;
for (var i = index + 1; i < rows.length; i++) {
var row = rows[i];
if (hasClass(row, clsToStopOn)) {
break;
}
if (hasClass(row, clsToShow)) {
row.style.display = "";
}
}
});
})(i);
}
Working multi-level demo: http://jsfiddle.net/jfriend00/9HgPt/
Related
This is creating my table and working - It creates a table and I am going to have it check a list to hide columns. For testing purposes I am trying to hide the colun "Proj_Type" it does hide the header but does not hide the actual column of data. I want the entire thing to hide.
function createTab(Name, id) {
var $button = $('<button/>', {
'class': 'tablinks',
'onclick': 'return false;',
'id': name,
text: Name,
click: function () {
return false;
}
});
var $div = $('<div>').prop({
id: Name,
'name': id + 'MKTTAB',
className: 'tabcontent'
})
var $table = $('<table/>', {
'class': 'GRD1',
id: id + "GRDMKTLIST",
}
)
$table.append('<caption>' + Name + '</caption>')
var $tbody = $table.append('<tbody />').children('tbody');
$tbody.append('<tr />').children('tr:last');
$.ajax({
type: "POST",
url: "../WebMethods/MarketPersuitMethods.aspx/GetQueryInfo",
data: {},
contentType: "application/json; charset=utf-8",
dataType: 'json',
async: false,
success: function (d) {
var data = $.parseJSON(d.d);
var colHeader = Object.keys(data[0]);
for (var i = 0; i < colHeader.length; i++) {
if (colHeader[i] != "notes") {
$tbody.append("<th>" + colHeader[i] + "</th>");
}
}
//sets new line
$tbody.append('<tr />').children('tr:last')
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < colHeader.length; j++) {
if (colHeader[j] != "notes") {
$tbody.append('<td>' + data[i][colHeader[j]] + '</td>');
}
}
$tbody.append('<tr />').children('tr:last')
}
setTimeout(function () {
}, 1000);
}
});
$($table.find('th')).each(function () {
var indextest = $(this).index() + 1;
if ($(this).text() == "Proj_Type") {
$('[id*=GRDMKTLIST] td:nth-child(' + indextest + '),th:nth-child(' + indextest + ')').hide();
}
})
$button.appendTo('#tabs');
$table.appendTo($div);
$div.appendTo('#TabbedMktList');
}
However, on the bottom where i have
if ($(this).text() == "Proj_Type") {
$('[id*=GRDMKTLIST] td:nth-child(' + indextest + '),th:nth-child(' + indextest + ')').hide();
}
This only hides the header and I am trying to hide the entire column TD included.
There are two main issues:
$('[id*=GRDMKTLIST]
will look in the DOM, but your $table has not yet been added to the DOM, so does nothing. Use $table.find(... to use the $table variable in memory.
$tbody.append('<td
will append the td (and equivalent code for th) to the tbody - but these should be in a tr.
The browser will do some "magic" and see an empty <tr></tr> and put a new row in for you, but selectors for tbody > tr > td won't work. This also means there's only a single :nth-child(n) per n, not per row (as all cells across all rows are siblings).
You can add a new row to $tbody and return the new row by using .appendTo
$tr = $("<tr>").appendTo($tbody);
then add the th/td to $tr and not $tbody
$tr.append('<td...`
Regarding the selector for $table.find("td,th") you don't need the th part as you're looping th and it's already this, so you can do:
$(this).hide();
$table.find('td:nth-child(' + indextest + ')').hide();
Also make sure you only create tr as you need it. Your code create a tr before the data row loop and inside the data row loop, leaving an empty tr.
for (var i = 0; i < data.length; i++) {
$tr = $("<tr>").appendTo($tbody);
for (var j = 0; j < colHeader.length; j++) {
if (colHeader[j] != "notes") {
$tr.append('<td id=' + colHeader[j] + '>' + data[i][colHeader[j]] + '</td>');
}
}
}
Also updated in the fiddle: https://jsfiddle.net/n168ra2g/3/
I have an HTML page with 3 tables on it. I want to be able to copy specific cells in a table row to the clipboard. The row could come from any of the 3 tables.
Using the code below, I highlight and copy the row for a table with an ID of "final". How do I make this work for any of the 3 tables? I tried by getElementsByTagName and labeling them the same name but did not work - understandably so. Is there a way to designate the selected table? I am trying to avoid copying the whole row and might eventually add the formatted msg to a new page rather than copy to the clipboard.
function copy_highlight_row() {
var table = document.getElementById('final');
var cells = table.getElementsByTagName('td');
for (var i = 0; i < cells.length; i++) {
// Take each cell
var cell = cells[i];
// do something on onclick event for cell
cell.onclick = function () {
// Get the row id where the cell exists
var rowId = this.parentNode.rowIndex;
var rowsNotSelected = table.getElementsByTagName('tr');
for (var row = 0; row < rowsNotSelected.length; row++) {
rowsNotSelected[row].style.backgroundColor = "";
rowsNotSelected[row].classList.remove('selected');
}
var rowSelected = table.getElementsByTagName('tr')[rowId];
rowSelected.style.backgroundColor = "yellow";
rowSelected.className += " selected";
var cellId = this.cellIndex + 1
msg = 'Title: ' + rowSelected.cells[0].innerHTML;
msg += '\r\nDescription: ' + rowSelected.cells[1].innerHTML;
msg += '\n\nLink: ' + rowSelected.cells[2].innerHTML;
msg += '\nPublication Date: ' + rowSelected.cells[3].innerHTML;
//msg += '\nThe cell value is: ' + this.innerHTML copies cell selected
navigator.clipboard.writeText(msg);
}
}
};
Based on a couple of the suggestions I came up with the following:
function highlight_row() {
var cells = document.querySelectorAll('td')
for (var i = 0; i < cells.length; i++) {
// Take each cell
var cell = cells[i];
// do something on onclick event for cell
cell.onclick = function () {
// Get the row id where the cell exists
var rowId = this.parentNode.rowIndex;
var t_ID = this.closest('table').id
var table=document.getElementById(t_ID);
var rowsNotSelected = table.getElementsByTagName('tr');
for (var row = 0; row < rowsNotSelected.length; row++) {
rowsNotSelected[row].style.backgroundColor = "";
rowsNotSelected[row].classList.remove('selected');
}
var rowSelected = table.getElementsByTagName('tr')[rowId];
rowSelected.style.backgroundColor = "yellow";
rowSelected.className += " selected";
var cellId = this.cellIndex + 1
msg = 'Title: ' + rowSelected.cells[0].innerHTML;
msg += '\r\nDescription: ' + rowSelected.cells[1].innerHTML;
msg += '\n\nLink: ' + rowSelected.cells[2].innerHTML;
msg += '\nPublication Date: ' + rowSelected.cells[3].innerHTML;
navigator.clipboard.writeText(msg);
}
}
}
At the end of the html I call the function. The HTML contains 3 tables and this enabled me to click on any table, highlight the row, and copy the correct range of cells.
Wracking my brains on this one but I'm sure it's just my inexperience with js.
I have a list of items and I am trying to create an eventListener for each item row so when I mouseover the row some icons will appear. The icons should hide again on mouseout.
I am using setAttribute to change the opacity of the elements on mouseover and mouseout events.
My problem is after the loop runs to create the eventListeners the setAttribute only affects the very last row of the loop - regardless of which row I mouse over.
I have a JSFiddle here which shows you the exact behaviour (better than I can explain): https://jsfiddle.net/Finno/ds9q7zju/27/
Note: this is a simplified example but it exhibits the same behaviour. My real app has the items formatted in a drop down menu.
Here is the code :
var total = 8;
var items;
var row;
var sort;
var trash;
for (var i = 0; i < total; i++) {
items = items + '<div id="row' + i + '">';
items = items + '<span class="hidden" id="sort' + i + '">SORT</span>';
items = items + '<span id="content' + i + '">Some content</span>';
items = items + '<span class="hidden" id="trash' + i + '">TRASH</span>';
items = items + '</div><br>';
}
document.getElementById("queue").innerHTML = items;
for (var i = 0; i < total; i++) {
row = 'row' + i;
sort = 'sort' + i;
trash = 'trash' + i;
document.getElementById(row).addEventListener("mouseover", function() {
document.getElementById(sort).setAttribute("style", "opacity:1 !important");
document.getElementById(trash).setAttribute("style", "opacity:1 !important");
});
document.getElementById(row).addEventListener("mouseout", function() {
document.getElementById(sort).setAttribute("style", "opacity:0 !important");
document.getElementById(trash).setAttribute("style", "opacity:0 !important");
});
}
You need to recreate the variables in each iteration, Try this,
Also, you can use string interpolation in place of + to join strings
var total = 8;
var items;
for (var i = 0; i < total; i++) {
items = items + '<div id="row' + i + '">';
items = items + '<span class="hidden" id="sort' + i + '">SORT</span>';
items = items + '<span id="content' + i + '">Some content</span>';
items = items + '<span class="hidden" id="trash' + i + '">TRASH</span>';
items = items + '</div><br>';
}
document.getElementById("queue").innerHTML = items;
for (var i = 0; i < total; i++) {
let row = 'row' + i;
let sort = 'sort' + i;
let trash = 'trash' + i;
document.getElementById(row).addEventListener("mouseover", function() {
document.getElementById(sort).setAttribute("style", "opacity:1 !important");
document.getElementById(trash).setAttribute("style", "opacity:1 !important");
});
document.getElementById(row).addEventListener("mouseout", function() {
document.getElementById(sort).setAttribute("style", "opacity:0 !important");
document.getElementById(trash).setAttribute("style", "opacity:0 !important");
});
}
I have a problem with the following code. I am trying remove rows from a table if that row does not contain the meat in the td. To clarify, when the function is called, it takes in 2. (Which is where the word meat can be found in the table)
Some of the rows do not contain the word meat, x = rows[i].getElementsByTagName("td")[a]; The word meat can be found in [a] of the row. I think the problem is with x.innerHTML, as I don't think it returns a value to compare to b.
Any help or leads are appreciated. Right now when the button is clicked to call the function, nothing happens.
function clearTable(a) {
var table, rows, switching, i, x, c, shouldSwitch;
table = document.getElementById("Invtable");
switching = true;
var b = "meat";
while (switching){
switching = false;
rows = table.getElementsByTagName("tr");
for (i = 0; i < (rows.length); i++) {
shouldSwitch = false;
x = rows[i].getElementsByTagName("td")[a];
if(x.innerHTML.toLowerCase() != b){
shouldSwitch= true;
break;
}
}
if (shouldSwitch) {
table.deleteRow(i);
switching = true;
}
}
}
var table = "<tr>";
for (var i = 0; i < array.length; i++) {
table += "<tr>";
for (var j = 0; j < array[i].length; j++) {
if (j == 6) {
table += "<td> <img src='CSV_Photos/" + array[i][j] + "' style ='width:250px;height:250px'>" + "<br>" //every 6th column is a picture
+ "<center> " + '<button id="btn" onClick="clickMe(\''+ array[i][1] + ',' + array[i][5] + '\')"> Buy / Add To Cart </button> </td>' + "</center>"; //button onclick takes (name+price)
} else {
table += "<td>" + array[i][j] + "</td>";
}
}
table += "</tr>";
}
Edit: Starting from the var table, that's how the table was made in javascript in a function.
The table code in html looks like this :
<tr><td>1000</td><td>Chicken</td><td>Meat</td><td>Perfect</td><td>Yes</td><td>$2.99</td><td>image</td> </tr>
The problem is you are passing the incorrect column number as the argument. You only have two td per row and the index starts at 0. So you have to pass 1 as your argument: clearTable(1).
I created a simple table so you can see your function works with the correct argument.
EDIT
I recreated my table to have 6 columns and I created a button that runs the function onClick.
var table = '<table id="Invtable"><tr><td>Food</td><td>chicken</td><td>Veggies</td><td>Ceral</td><td>Soda</td><td>Water</td></tr><tr><td>Food</td><td>water</td><td>Soda</td><td>Meat</td><td>Water</td><td>Ceral</td></tr><tr><td>Third-Food</td><td>Meat</td><td>Chicken</td><td>Ceral</td><td>Water</td><td>Soda</td></tr></table>';
var btn = '<button onClick="clearTable(1)">Meat</button>';
document.body.innerHTML = table + btn;
//clearTable(1);
function clearTable(a) {
var table, rows, switching, i, x, c, shouldSwitch;
table = document.getElementById("Invtable");
switching = true;
var b = "meat";
while (switching){
switching = false;
rows = table.getElementsByTagName("tr");
console.log(rows);
for (i = 0; i < (rows.length); i++) {
shouldSwitch = false;
x = rows[i].getElementsByTagName("td")[a];
if(x.innerHTML.toLowerCase() != b){
shouldSwitch= true;
break;
}
}
if (shouldSwitch) {
table.deleteRow(i);
switching = true;
}
}
}
I have a nested accordion with a checkbox that makes an ajax call if checked, the problem is that the checkbox does not remain checked after the accordion expands.
if (childList.length > 0) {
list += '<ul class = ui-accordion>';
for (var j = 0; j < childList.length; j++) {
list += '<li><div><input type = "checkbox" class = "reqCheckBox" value="' + childList[i].ComponentID + '"/> ' + childList[j].ComponentDesc +'</div>';
var grandChildList = $.grep(data, function (n, k) {
return n.ParentID == childList[j].ComponentID;
}, false);
if (grandChildList.length > 0) {
list += '<ul class = ui-accordion>';
for (var k = 0; k < grandChildList.length; k++) {
list += '<li><div><input type = "checkbox" onclick="getReq(' + grandChildList[i].ComponentID + ')"/>' + grandChildList[k].ComponentDesc + '</div>';
var greatGrandChildList = $.grep(data, function (n, l) {
return n.ParentID == grandChildList[k].ComponentID;
}, false);
if (greatGrandChildList.length > 0) {
list += '<ul class = ui-accordion>';
for (var l = 0; l < greatGrandChildList.length; l++) {
list += '<li><div><input type = "checkbox" onclick="getReq(' + greatGrandChildList[i].ComponentID + ')"/>' + greatGrandChildList[l].ComponentDesc + '</div>';
}
list += '</li></ul>';
}
}
list += '</li></ul>';**
I know that I need to make this unobtrusive but any critiques of my JavaScript code as well would be appreciated as I am a noob. Thanks!
What you need is to persist the value of the checkbox. So when you check it your ajax calback needs to update the value of a property. This way when the accordion expands it persists the state of the checkbox.
I maintained checkbox state by adding this function
$("a").click(function (event) {
event.stopPropagation();
});
});
The following works.
$("#accordion h3 input").click(function(event) {
event.stopPropagation();
});