Highlight multiple rows in several tables - javascript

I have a page with several rows containing information, made by several users. I'm looking for a way to highlight the all the users rows on mouseover.
This "Highlight multiple items on hover's condition" almost solved my problem, but since the classes or id's in my problem are dynamic from a database, and would contain an identifier from the DB and are unique each time. I have not been able to apply it.
Example code: https://jsfiddle.net/3cehoh78/
<table class="testtable">
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 1a</td>
<td class="cellclass">Sam</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 2a</td>
<td class="cellclass">Frodo</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 3a</td>
<td class="cellclass">Sam</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 4a</td>
<td class="cellclass">Legoman</td>
<td class="cellclass">data</td>
</tr>
</table>
<br>
<br>
<table class="testtable">
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 1b</td>
<td class="cellclass">Sauron</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 2b</td>
<td class="cellclass">Sam</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 3b</td>
<td class="cellclass">Sam</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 4b</td>
<td class="cellclass">Legoman</td>
<td class="cellclass">data</td>
</tr>
<tr id="uniqueIDthatcantbechanged">
<td class="cellclass">Line 5b</td>
<td class="cellclass">Frodo</td>
<td class="cellclass">data</td>
</tr>
</table>
In this example, I want all the rows with "Sam" to be highlighted on mouseover on one of them, so rows 1a,3a,2b,3b.
I was thinking of adding a class to all the Sam rows when generating the tables (Sam has a unique user ID), but how do I then change css that affects all the rows on mouseover (and not just one).
Please note that I cant pre-add css classes for all the unique userID's, this is just an example.

Here a solution with JQuery https://jsfiddle.net/3cehoh78/5
$(document).ready(function() {
$( "tr" ).hover(function() {
var search = $(this).find("td:eq(1)").text();
$( ".highlight" ).removeClass("highlight");
$("tr:contains('"+search+"')").addClass("highlight");
}); /* END HOVER */
}); // end document ready

Simple solution without using jQuery and co: https://jsfiddle.net/3cehoh78/3/
var rows = [].slice.call(document.querySelectorAll('.testtable tr'));
rows.forEach(function(row) {
row.addEventListener('mouseover', function() {
resetHighlighting();
var name = row.querySelector('td:nth-child(2)').textContent;
rows.forEach(function(r) {
if (r.querySelector('td:nth-child(2)').textContent === name) {
r.classList.add('highlighted');
}
});
});
});
function resetHighlighting() {
rows.forEach(function(row) {
row.classList.remove('highlighted');
});
}

Here's another way using vanilla-JavaScript.
var tds = document.querySelectorAll('td');
var highlight = function () {
// take this person's name from the 2nd cell
var name = this.parentNode.children[1].innerHTML;
// highlight cells with same name
tds.forEach(function (td) {
var tr = td.parentNode;
// compare other's person name with this person name
// highlight if there is a match
tr.classList.toggle('highlight', tr.children[1].innerHTML === name)
});
}
// attach an event listener to all cells
tds.forEach(function (td) {
td.onmouseover = highlight;
});
Demo

Related

Convert HTML table to javascript array using class names

How do you convert an HTML table to a javascript array using the tags's class names as the array values?
Say we have the following HTML code:
<table class="grid">
<tr>
<td class="available"></td>
<td class="busy"></td>
<td class="busy"></td>
<td class="available"></td>
</tr>
<tr>
<td class="busy"></td>
<td class="available"></td>
<td class="busy"></td>
<td class="available"></td>
</tr>
</table>
I want the array to look like: [["available","busy","busy","available"],["busy","available","busy","available"]]
I have tried the following:
var myTableArray = [];
$("table#grid tr").each(function() {
var arrayOfThisRow = [];
var tableData = $(this).find('td');
if (tableData.length > 0) {
tableData.each(function() { arrayOfThisRow.push($(this).text()); });
myTableArray.push(arrayOfThisRow);
}
});
console.log(myTableArray);
but it is printing an empty array as the td tags contain no text. I then tried replacing
$(this).text()
with
$(this).className()
but that did not work. Any suggestions?
map is the way to go.
jQuery's $.map is a little weird in that it seems to think it's ok to flatten mapped arrays without asking and we're not going to fix it so you have to couch the mapped array in an array.
// Cache the rows
const rows = $('.grid tr');
// `map` over each row...
const arr = $.map(rows, row => {
// Find the row cells...
const cells = $(row).find('td');
// ...and return an array of each cell's text
return [$.map(cells, cell => $(cell).text())];
});
console.log(arr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="grid">
<tr>
<td class="available">available</td>
<td class="busy">busy</td>
<td class="busy">busy</td>
<td class="available">available</td>
</tr>
<tr>
<td class="busy">busy</td>
<td class="available">available</td>
<td class="busy">busy</td>
<td class="available">available</td>
</tr>
</table>
Alternatively, if you wanted a vanilla JS solution, you can just pick up the rows with querySelectorAll, and then iterate over them with map, then return the text from the cell (assuming that you fix the HTML).
(Note: [...nodelist] is shorthand for creating an array from a nodelist so that map can work. You could also use Array.from(nodelist)).
// Cache the rows
const rows = document.querySelectorAll('.grid tr');
// `map` over each row...
const arr = [...rows].map(row => {
// Find the row cells...
const cells = row.querySelectorAll('td');
// ...and return an array of each cell's text
return [...cells].map(cell => cell.textContent);
});
console.log(arr);
<table class="grid">
<tr>
<td class="available">available</td>
<td class="busy">busy</td>
<td class="busy">busy</td>
<td class="available">available</td>
</tr>
<tr>
<td class="busy">busy</td>
<td class="available">available</td>
<td class="busy">busy</td>
<td class="available">available</td>
</tr>
</table>
Vanilla JS Solution
Get and Make array with <tr>
Map every <tr> and make array with <td> elements
Map only class names from every <td>
The example will return ARRAY from CLASS element names.
Example:
var res = [...document.querySelectorAll('.grid tr')] // 1. Get and Make array with <tr>
.map((el) => [...el.children] // 2. Map every <tr> and make array with <td> elements
.map(e => e.getAttribute('class'))); // 3. Map only class names from every <td>
console.log(res);
<table class="grid">
<tr>
<td class="available"></td>
<td class="busy"></td>
<td class="busy"></td>
<td class="available"></td>
</tr>
<tr>
<td class="busy"></td>
<td class="available"></td>
<td class="busy"></td>
<td class="available"></td>
</tr>
</table>
This example will make ARRAY from the text content of the table.
Example:
var res = [...document.querySelectorAll('.grid tr')] // 1. Get and Make array with <tr>
.map((el) => [...el.children] // 2. Map every <tr> and make array with <td> elements
.map(e => e.innerText)); // 3. Map only text content from every <td>
console.log(res);
<table class="grid">
<tr>
<td>available</td>
<td>busy</td>
<td>busy</td>
<td>available</td>
</tr>
<tr>
<td>busy</td>
<td>available</td>
<td>busy</td>
<td>available</td>
</tr>
</table>

How to get the id of a clicked element within a table row?

I have the following table
<table width="97%" border="1" class="queueList">
<thead>
<tr>
<th>Delete</th>
<th>Queue Name</th>
<th>Current queue length</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.ImportQueues)
{
<tr id="watchRow">
<td id="DeleteButton">X</td>
<td id="QueueName", value="#item.QueueName">#item.QueueName</td>
<td>#item.CurrentQueueLength</td>
</tr>
}
</tbody>
how can I get the value of the element "QueueName" when "#DeleteRow" is clicked, using JQuery ?
So far I have
$("body").on("click", "#DeleteButton", function (event) {
var queueName = $(event.target).closest("div.queueList").find("#QueueName");
alert(queueName);
}
Use the data attribute to store the desired value on the button itself
<tr class="watchRow">
<td class="DeleteButton" data-queuename="#item.QueueName">X</td>
<td class="QueueName">#item.QueueName</td>
<td>#item.CurrentQueueLength</td>
</tr>
then on click of TD (by the way, it should be a button)
$("body").on("click", ".DeleteButton", function() {
var queueName = $(this).data("queuename");
alert(queueName);
}
If you want to use this name on other buttuns also, like edit etc. then it is better to assign it to the whole row:
<tr class="watchRow" data-queuename="#item.QueueName">
<td class="DeleteButton">X</td>
<td class="QueueName">#item.QueueName</td>
<td>#item.CurrentQueueLength</td>
</tr>
and read it like this:
$("body").on("click", ".DeleteButton", function() {
var queueName = $(this).closest('tr').data("queuename");
alert(queueName);
}
Without JQuery
<tr id="watchRow">
<td class="DeleteButton" onclick="deleteItem(#item.QueueName)">X</td>
<td value="#item.QueueName">#item.QueueName</td>
<td>#item.CurrentQueueLength</td>
</tr>
<script>
function deleteItem(item) {
alert(item)
}
</script>
With JQuery
<tr id="watchRow">
<td class="DeleteButton">X</td>
<td value="#item.QueueName">#item.QueueName</td>
<td>#item.CurrentQueueLength</td>
</tr>
<script>
$(".DeleteButton").on("click", function() {
alert($(this).next("td").html());
}
</script>
</script>

Get column name from row mapping with onClick handler

In this reactjs app I have a table with the following body:
<tbody>
{results.map(result =>
<tr key={result.metric} onClick={this.handleClick}>
<td className="inpt-td">{result.t00}</td>
<td className="inpt-td">{result.t01}</td>
<td className="inpt-td">{result.t02}</td>
<td className="inpt-td">{result.t03}</td>
<td className="inpt-td">{result.t04}</td>
<td className="inpt-td">{result.t05}</td>
<td className="inpt-td">{result.t06}</td>
<td className="inpt-td">{result.t07}</td>
<td className="inpt-td">{result.t08}</td>
<td className="inpt-td">{result.t09}</td>
<td className="inpt-td">{result.t10}</td>
<td className="inpt-td">{result.t11}</td>
<td className="inpt-td">{result.t12}</td>
<td className="inpt-td">{result.t13}</td>
<td className="inpt-td">{result.t14}</td>
<td className="inpt-td">{result.t15}</td>
<td className="inpt-td">{result.t16}</td>
<td className="inpt-td">{result.t17}</td>
<td className="inpt-td">{result.t18}</td>
<td className="inpt-td">{result.t19}</td>
<td className="inpt-td">{result.t20}</td>
<td className="inpt-td">{result.t21}</td>
<td className="inpt-td">{result.t22}</td>
<td className="inpt-td">{result.t23}</td>
</tr>
)}
</tbody>
The header does not exist for this particular table, but I was wondering if there was a way to obtain the column name of a clicked cell. So for example if you clicked on the second cell in a given row, it would return "t01", which is the column name.
My searches online did not provide an efficient way of doing this. Is there a method to retrieve this info?
In your handleClick you can get access to the event.target property, which is a cell.
After that you can do:
var child = event.target;
var parent = child.parentNode;
// equivalent of parent.children.indexOf(child)
var index = Array.prototype.indexOf.call(parent.children, child);
var value = 't' + index // this will be value what you are looking for
If you need information how to use event.target - here is an example.

How to select the last cell from table which is not null, using JQuery

I have a table with following rows and cells:
<table id='table1'>
<tr id='row1'>
<th>index</th>
<th>Product</th>
<th>Description</th>
</tr>
<tr id='row2' name='row'>
<td name='index'>1</td>
<td name='product'>Apples</td>
<td name='description'>fruits</td>
</tr>
<tr id='row3' name='row'>
<td name='index'>2</td>
<td name='product'>Bananas</td>
<td name='description'>fruits</td>
</tr>
<tr id='row4' name='row'>
<td name='index'>3</td>
<td name='product'>Carrots</td>
<td name='description'>vegetables</td>
</tr>
<tr id='row5' name='row'>
<td name='index'></td>
<td name='product'></td>
<td name='description'></td>
</tr>
</table>
I need to select the value for the last td with name='index' which is not null. Anyone has any idea how can this be done.
Use the following selector :
$('td[name=index]:not(:empty):last')
For purely educational purposes, here is a non jQuery version:
function getLastNonEmptyCell(tableSelector) {
//Find parent table by selector
var table = document.querySelector(tableSelector)
//Return null if we can't find the table
if(!table){
return null;
}
var cells = table.querySelectorAll("td")
var lastNonEmptyCell = null;
//Iterate each cell in the table
//We can just overwrite lastNonEmptyCell since it's a synchronous operation and the return value will be the lowest item in the DOM
cells.forEach(function(cell) {
//!! is used so it's so if it is not null, undefined, "", 0, false
//This could be changed so it's just cell.innerText.trim() !== ""
if (!!cell.innerText) {
lastNonEmptyCell = cell;
}
})
return lastNonEmptyCell;
}
var cell = getLastNonEmptyCell("#table1")
Edit
As #squint suggested this can be done much more succintly:
function lastNonEmptyCell(tableSelector) {
//Since we want the last cell that has content, we get the last-child where it's not empty. This returns the last row.
var row = document.querySelectorAll(tableSelector + " td:not(:empty):last-child")
//Just grabbing the last cell using the index
return row[row.length - 1]
}

Total up values of table cell but only one value is allowed per row

I'm trying to create a scorecard program which allows the user to select cells of a table and gauge a score, I'm at a point where I can add each score the user clicks (admittedly in probably a very clunky way) but my problem is that the way the logic works is that only one value per row can be chosen. Here's an example - http://jsfiddle.net/5q9nS/2/
To clarify, if the user chooses "#a1" the value of "2" should be added to the total. If the user then selects "#b1" this value should subtract the previously selected value because duplicate values from the same row are not allowed. However the total should add any value choosen from the rows a2-c2, a3-e3, a4-c4 etc.....
I hope that's clear, the logic for the selected classes works to try and further demonstrate.
Any help would be hugely appreciated! As you will be able to tell i'm just on the learning curve and need to hit the summit.
HTML
<table width="100%" id="pcpScoring">
<tr>
<td width="30%"> </td>
<td width="14%" class="tblLabel">Own Savings</td>
<td width="14%" class="tblLabel">Bank or B.S. Loan</td>
<td width="14%" class="tblLabel">Dealer Finance</td>
<td width="14%" align="center"> </td>
<td width="14%" align="center"> </td>
</tr>
<tr valign="top">
<td>How do you normally fund your vehicle?</td>
<td class="tblNumber"><a id="a1">2</a></td>
<td class="tblNumber"><a id="b1">4</a></td>
<td class="tblNumber"><a id="c1">3</a></td>
<td align="center"> </td>
<td align="center"> </td>
</tr>
<tr>
<td> </td>
<td class="tblLabel">1-2 years</td>
<td class="tblLabel">3 years</td>
<td class="tblLabel">4 years</td>
<td align="center"> </td>
<td align="center"> </td>
</tr>
<tr valign="top">
<td>How often would you "ideally" like to change your vehicle?</td>
<td class="tblNumber"><a id="a2">4</a></td>
<td class="tblNumber"><a id="b2">2</a></td>
<td class="tblNumber"><a id="c2">1</a></td>
<td align="center"> </td>
<td align="center"> </td>
</tr>
<tr>
<td> </td>
<td class="tblLabel">upto 6,000</td>
<td class="tblLabel">6-12,000</td>
<td class="tblLabel">12-18,000</td>
<td class="tblLabel">18-25,000</td>
<td class="tblLabel">over 25,000</td>
</tr>
<tr valign="top">
<td>How many miles do you drive per year?</td>
<td class="tblNumber"><a id="a3">5</a></td>
<td class="tblNumber"><a id="b3">4</a></td>
<td class="tblNumber"><a id="c3">3</a></td>
<td class="tblNumber"><a id="d3">2</a></td>
<td class="tblNumber"><a id="e3">1</a></td>
</tr>
<tr>
<td> </td>
<td class="tblLabel">Part Exchange</td>
<td class="tblLabel">Private Sale</td>
<td class="tblLabel">Pass on to Famiy</td>
<td class="tblLabel"> </td>
<td class="tblLabel"> </td>
</tr>
<tr valign="top">
<td>How do you usually dispose of your vehicle? </td>
<td class="tblNumber"><a id="a4">4</a></td>
<td class="tblNumber"><a id="b4">2</a></td>
<td class="tblNumber"><a id="c4">1</a></td>
<td align="center"> </td>
<td align="center"> </td>
</tr>
<tr>
<td colspan="6" class="tblNumber" style="text-align:right"><span class="total">Total Score</span><span id="pcpScore" class="total">0</span></td>
</tr>
</table>
jQuery
$(document).ready(function(){
//PCP Scorecard on Click add class
$('.tblNumber a').click(function() {
$(this).addClass('selected');
});
$('#a1').click(function() {
$(this).addClass('selected');
$('#b1, #c1').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#b1').click(function() {
$(this).addClass('selected');
$('#a1, #c1').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#c1').click(function() {
$(this).addClass('selected');
$('#a1, #b1').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#a2').click(function() {
$(this).addClass('selected');
$('#b2, #c2').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#b2').click(function() {
$(this).addClass('selected');
$('#a2, #c2').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#c2').click(function() {
$(this).addClass('selected');
$('#a2, #b2').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#a3').click(function() {
$(this).addClass('selected');
$('#b3, #c3, #d3, #e3').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#b3').click(function() {
$(this).addClass('selected');
$('#a3, #c3, #d3, #e3').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#c3').click(function() {
$(this).addClass('selected');
$('#a3, #b3, #d3, #e3').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#d3').click(function() {
$(this).addClass('selected');
$('#a3, #c3, #e3, #b3').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#e3').click(function() {
$(this).addClass('selected');
$('#a3, #b3, #c3, #d3').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#a4').click(function() {
$(this).addClass('selected');
$('#b4, #c4').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#b4').click(function() {
$(this).addClass('selected');
$('#a4, #c4').removeClass('selected');
addUp(parseInt($(this).text()));
});
$('#c4').click(function() {
$(this).addClass('selected');
$('#a4, #b4').removeClass('selected');
addUp(parseInt($(this).text()));
});
function addUp(addScore) {
var totalScore = (parseInt($('#pcpScore').text())) + addScore;
$('#pcpScore').html(totalScore);
}
});
You can accomplish all you need within the click function:
$('.tblNumber a').click(function() {
$(this).parents('tr').find('a').removeClass('selected');
$(this).addClass('selected');
var total = 0;
$('.tblNumber .selected').each(function() {
total += parseInt($(this).text());
});
$('#pcpScore').text(total);
});
Line by line it: Clears the previously selected items in the row, selects the current item, then iterates over the selected items in the table adding to the total then sets the text in the pcpScore span.
EDIT: Working jsfiddle example: http://jsfiddle.net/cbailster/ycymB/
You can use the "each" method in jquery to traverse each of your rows.
http://api.jquery.com/jQuery.each/
$('table tr').each(function() { /*Magic Happens Here*/ });
Then, you can target a specific cell by using the "eq()" selector
http://api.jquery.com/eq-selector/
$('table tr').each(function() {
var value = $(this).find('td:eq(1)').text(); //second cell in the row
});
You can even just use a single selector
$('table tr td:eq(1)').each(function() {
var value = $(this).text(); //second cell in the row
});
Use selectors, you get the idea ;-)
Here's a working solution: http://jsfiddle.net/5q9nS/2/
And the JS:
var totalDiv = $('#pcpScore');
$('.tblNumber').click(function(){
var $this = $(this);
if(!$this.hasClass('selected')){
var prevSelected = $this.siblings('.selected');
$this.addClass('selected');
prevSelected.removeClass('selected');
var currentTotal = +totalDiv.text(); //the plus sign converts this to a number
var prevSelectedVal = +prevSelected.children('a').text();
var newSelectedVal = +$this.children('a').text();
totalDiv.text(currentTotal + newSelectedVal - prevSelectedVal);
}
});
First we store the element that holds the total in a variable because we're going to need to access it every time a .tblNumber is clicked. This makes the script more efficient because it doesn't have to re-find that element every time.
After that, we simply bind a click event handler to every .tblNumber element. We then use jQuery's DOM traversal methods to deselect the previously selected .tblNumber in that row, as well as get it's value and the new value so that we can adjust the total appropriately.
Having looked at your original code, the key to dramatically shortening it like I did here is the this keyword. While this can be a complicated topic in JS, when used inside an event handler (.click(function(){...})) like this, it refers to the element the fired the event. So it gives us the context of the event, which is what allows us to identify the correct cells, rather than binding a slightly different click event handler to every single cell, as you had done.

Categories