HTML: Anchor in table - javascript

I was trying to make a table with anchor clickable rows. My first try was this:
<table>
<body>
<table>
<thead>
<tr>
<th>Id</td>
<th>Name</td>
<th>Address</td>
</tr>
</thead>
<tbody>
<a href="https://google.com">
<td> 1 </td>
<td> Google </td>
<td> https://google.com </td>
</a>
</tbody>
</table>
</body>
</table>
When I inspected the result in Chrome and Edge I noticed that the browsers had decided to split the anchor out and throw it behind the table!
So, this is my first question: Why do browsers manipulate the HTML structure?
My second try was using JavaScript:
var tbody = document.getElementsByTagName('tbody')[0];
var anchor = document.createElement("a");
anchor.href = "https://google.com"
var td = document.createElement('td');
td.innerHTML = "1"
anchor.appendChild(td);
td = document.createElement('td');
td.innerHTML = "Google";
anchor.appendChild(td);
td = document.createElement('td');
td.innerHTML = "https://google.com";
anchor.appendChild(td);
tbody.appendChild(anchor);
th {
width: 30%;
text-align: left;
}
a {
display: table-row;
text-decoration: none;
color: black;
}
<body>
<table>
<thead>
<tr>
<th>Id</td>
<th>Name</td>
<th>Address</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
</body>
It works like a charm: the entire row is clickable.
But I don't get why this time, the browsers don't manipulate with DOM? This is my second question.
Any helpful explanation is appreciated.

Why do browsers manipulate the HTML structure?
Because your HTML is invalid.
A <tbody> element is only allowed <tr> children.
The parsing rules browsers follow describe how to deal with invalid HTML in that way.
But I don't get why this time, the browsers don't manipulate with DOM?
The browser isn't building the DOM using the HTML parsing algorithm.
You are manually creating a DOM that represents invalid HTML.

you can able to redirect using data-href="your-link.com"
you can not modify any <table> element but you want to redirect on tr click
Please check working demo
HTML CODE
<table>
<body>
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr class="link" data-href="https://stackoverflow.com/">
<td> 1 </td>
<td> Google </td>
<td> https://google.com </td>
</tr>
</tbody>
</table>
</body>
CSS CODE
td,th{
border:1px solid #ddd;
}
tbody tr:hover{
color:red;
cursor: pointer;
}
table{
border-collapse: collapse;
}
jQuery CODE
$(document).ready(function(){
var link = $(".link").attr("data-href");
$('.link').click(function(){
window.location.href=link;
})
});

So you want the table rows clickable going to a link in that row.
Don't wrap the row with an a tag. It's invalid, and at best will result in unpredictable results.
You will need javascript to make this work. Add an event listener to the table rows. Check for a well formed a tag and redirect to its href attribute.
//Get rows
let rows = document.querySelectorAll(".clickRows tbody > tr");
for (i = 0; i < rows.length; i++) {
//Add Event listener to row
rows[i].addEventListener("click", function() {
//If there is a link
if (this.querySelector("a[href]")) {
//Redirect to it
window.location.href = this.querySelector("a[href]").href;
} else {
console.log("No Link");
}
})
}
th {
width: 30%;
text-align: left;
}
<table class="clickRows">
<thead>
<tr>
<!-- You had mismatched th and td tags here -->
<th>Id</th>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr>
<td> 1 </td>
<td> Google </td>
<td>https://google.com</td>
</tr>
<tr>
<td> 2 </td>
<td> Ding </td>
<td>Ding</td>
</tr>
</tbody>
</table>
If you are willing to drop the HTML table
/*Displat as table*/
.clickRows {
display:table;
width:30%;
}
/*Set appropriate children as rows*/
.clickRows > div, .clickRows > a
{
display:table-row;
}
/*Then set the cells*/
.clickRows > div > div, .clickRows > a > div
{
display:table-cell;
}
.head {
font-weight: bold;
}
<div class="clickRows">
<div class="head">
<!-- You had mismatched th and td tags here -->
<div>Id</div>
<div>Name</div>
<div>Address</div>
</div>
<a href="https://google.com">
<div> 1 </div>
<div> Google </div>
<div>https://google.com</div>
</a>
<div>
<div> 2 </div>
<div> Ding </div>
<div>Ding</div>
</div>
</div>
On a side note your HTML as provided is quite broken. body is not a valid child element for table. Opening and closing td and th must match. For more info on what is and isn't valid for table and it's associated elements, see MDN

Related

How to make expandable table cells hidden by default using jQuery/JavaScript, and click-able outside of a label?

I have this short piece of code that allows for sections of a table to be collapsed (they are like collapsible headers). This is neat, but I'm trying to make for the inverse to happen upon loading the page -- to be collapsed by default on load, but expandable when clicked. How would I go about doing this?
My present code, shown below, also features sections that only collapse when the words in the section are clicked, not when the section itself (outside of the words) are clicked. This is because I used labels to make the collapsible. Is there a way to make the entire row expandable/collapsible?
table {
width: 100%;
}
table,
tr,
th,
td {
border: 1px solid black;
border-collapse: collapse;
font-family: Arial;
}
[data-toggle="toggle"] {
display: none;
}
<table>
<thead>
<tr>
<th>Name</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tbody class="labels">
<tr>
<td colspan="2">
<label for="section">Click me!</label>
<input type="checkbox" id="section" data-toggle="toggle">
</td>
</tr>
</tbody>
<tbody class="hide">
<tr>
<td>Jack</td>
<td>100</td>
</tr>
<tr>
<td>Jill</td>
<td>300</td>
</tr>
</tbody>
</table>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('[data-toggle="toggle"]').change(function() {
$(this).parents().next('.hide').toggle();
});
});
</script>
I'm trying to make for the inverse to happen upon loading the page --
to be collapsed by default on load, but expandable when clicked. How
would I go about doing this?
Simply add a line in your jquery above your toggle function and call on your .hide class selector and use .hide(); Then when you click it the toggle function fires.
also features sections that only collapse when the words in the
section are clicked, not when the section itself (outside of the
words) are clicked. This is because I used labels to make the
collapsible. Is there a way to make the entire row
expandable/collapsible?
Yes... Make your label display as block in your CSS file...
label {
display: block;
}
$(document).ready(function() {
$('.hide').hide();
$('[data-toggle="toggle"]').change(function() {
$(this).parents().next('.hide').toggle();
});
});
table {
width: 100%;
}
table,
tr,
th,
td {
border: 1px solid black;
border-collapse: collapse;
font-family: Arial;
}
[data-toggle="toggle"] {
display: none;
}
label {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<th>Name</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<tbody class="labels">
<tr>
<td colspan="2">
<label for="section">Click me!</label>
<input type="checkbox" id="section" data-toggle="toggle">
</td>
</tr>
</tbody>
<tbody class="hide">
<tr>
<td>Jack</td>
<td>100</td>
</tr>
<tr>
<td>Jill</td>
<td>300</td>
</tr>
</tbody>
</table>
Several things going on here...
You were hiding your checkbox, which I don't think was your intent.
Check this example, where I fixed some things: https://jsfiddle.net/za73qf65/
Fixes include:
changing the name of your "hide" class to "hidable"
defaulting that "hidable" class to be display:none
unhiding your checkbox
changing your change() event handler to a click() (optional)
attaching your event handler to a button with an ID (you can vary that)
Point is, with my changes, your example works. You might want to tweak it for a more specific need.

how to keep the css style after inserting a row to table by jquery

I have a css style
#mytbody > tr > th {
background-color: red;
}
And the script
function myclick() {
$("#mytbody").append("<tr><td>1</td><td>1</td>/tr>");
}
Here's my html code:
<table>
<thead>
<tr>
<td>head 1</td>
<td>head 2</td>
</tr>
</thead>
<tbody id="mytbody">
<tr>
<th>1</th>
<th>2</th>
</tr>
</tbody>
</table>
<button type="button" onclick="myclick()">button</button>
The css style disappeared for the new line that added by clicking the button.
Why the css doesn't work for the adding line and how to keep the css style for adding new line?
Thank you very much!
Here is my answer,
$("button").click(function(){
$("#mytbody").append("<tr><td>1</td><td>2</td></tr>");
});
#mytbody > tr > th {
background-color: red;
}
#mytbody > tr > td {
background-color: orange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<table>
<thead>
<tr>
<td>head 1</td>
<td>head 2</td>
</tr>
</thead>
<tbody id="mytbody">
<tr>
<th>1</th>
<th>2</th>
</tr>
</tbody>
</table>
<button type="button">button</button>
I will not repeat answers from previous examples but want to mention something realated to this.
If you load complete table via ajax and forgot to put <thead> or <tbody> inside your code, you can expirience the similar problem with CSS especially if you use Bootstrap.
Many developers not use that tags inside tables and browsers fix that in DOM but when you load with ajax you will not get that tags if you not define before in file and that can break CSS also.
It is happening because your css is only for the th tag. You should add css for td as well:
#mytbody > tr > th,
#mytbody > tr > td {
background-color: red;
}
It looks like the main reason, from the information you provided, is that the CSS declaration doesn't actually correspond to the added elements. Your style declaration is only for the th tags but not the td tags.
Try something like this:
#mytbody > tr > th,
#mytbody > tr > td {
background-color: red;
}

Highlighting Table Rows

I have a dynamically built table that ends up with the below code (with example values):
<table id="patientTable" class="display" cellspacing="0" width="100%">
<thead id="TableHeader">
<tr>
<th>Value1</th>
<th>Value2</th>
<th>Value3</th>
</tr>
</thead>
<tbody id="tableContent">
<tr class="clickable row_0" onclick="selectPatient(10001);" id="10001" style="background: #FFF;">
<td class="tableContent">Value1</td>
<td class="tableContent">Value2</td>
<td class="tableContent">Value3</td>
</tr>
</tbody>
</table>
I am trying to highlight the row that is been hovered over using the below CSS:
.clickable :hover {
background-color: #CCC;
}
For some reason, this changes the background of what would be the "<td>" element, for example, will just highlight Value1, Value2 or Value3 rather than the entire row.
I have tried (to no avail) to use:
.clickable tr:hover {
background-color: #CCC;
}
.clickable:hover {
background-color: #CCC;
}
.tr:hover {
background-color: #CCC;
}
.tr :hover {
background-color: #CCC;
}
I find this unusual behaviour, as it appears to work for everyone else on every other example i've seen..
Worth Mentioning: The table is build from a complex system, that basically performs an AJAX request, which performs a PHP database query, takes the values, throws them into a JSON array, passes them back to JS, re-parses the array as JSON, loops through and creates the table, then outputs it. Could the JS be causing the issue?
The class name ".clickable", "row_#" (where # is a number) and the ID for the table row need to stay, as they are used in future functions and provide me with a way to identify each row individually.
One solution is to apply the hover on child elements td's when hover on parent tr:
.clickable:hover td {
background-color: #CCC;
}
<table id="patientTable" class="display" cellspacing="0" width="100%">
<thead id="TableHeader">
<tr>
<th>Value1</th>
<th>Value2</th>
<th>Value3</th>
</tr>
</thead>
<tbody id="tableContent">
<tr class="clickable row_0" onclick="selectPatient(10001);" id="10001" style="background: #FFF;">
<td class="tableContent">Value1</td>
<td class="tableContent">Value2</td>
<td class="tableContent">Value3</td>
</tr>
</tbody>
</table>
This works (from your question) :
.clickable:hover {
background-color: #CCC;
}
but why is there nothing happening when you hover then ?
because this rule is overwritten by the inline style: style="background: #FFF;"
Hint : NEVER write inline style (except if you REALLY need it)
if you remove style="background: #FFF;" everything will be fine.
Working example :
.clickable {
background-color: #FFF;
}
.clickable:hover {
background-color: #CCC;
}
<table id="patientTable" class="display" cellspacing="0" width="100%">
<thead id="TableHeader">
<tr>
<th>Value1</th>
<th>Value2</th>
<th>Value3</th>
</tr>
</thead>
<tbody id="tableContent">
<tr class="clickable row_0" onclick="selectPatient(10001);" id="10001">
<td class="tableContent">Value1</td>
<td class="tableContent">Value2</td>
<td class="tableContent">Value3</td>
</tr>
<tr class="clickable row_1" onclick="selectPatient(10002);" id="10002">
<td class="tableContent">Value1</td>
<td class="tableContent">Value2</td>
<td class="tableContent">Value3</td>
</tr>
</tbody>
</table>
Edit :
For more information about which CSS rule will have priority over others, see this article on MDN : https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
You can't colour table rows. Colour the table cells (th and td) instead, using the direct child selector (>).
Edit: Apollo (below) is right: Of course you can colour table rows, but if you want to colour the row with a hover, you need this (just like the answer that was given before):
tr:hover > td,
tr:hover > th {
background-color:#ccc;
}

Is there a way to hide a data cell based on a specific value using just HTML/CSS?

For example I have this code:
<table>
<caption>Test</caption>
<tr>
<th>Values</th>
<td>$100</td>
</tr>
<tr>
<th>Initial value</th>
<td class="results"></td>
</tr>
</table>
Is there a way to hide the cells that are equal to $0 using HTML/CSS only?
Let's say instead of $0 I have a variable called fee that can be a variety of values: $0, $20, $100, etc.
For example:
<script>
var fees = ["$0", "$20", "$100"];
document.querySelector('.results').innerHTML = fees[1];
</script>
Is there a way to check what value it is and if it is found to be $0 can I then hide it?
My CSS is:
table{
border-width: 1px;
border-style: solid;
border-collapse: separate;
width: 400px;
}
#test{
empty-cells: show;
margin-bottom: 20px;
}
tr, th, td{
border-width:1px;
border-style: solid;
}
.results {
display: none; // I want this to only display none when fees = $0
}
TL;DR: It's possible. Look for the last solution in my answer, or check this blog:
Conditional formatting with pure css
I am assuming you do not want to hide the cell, but only its value. Hiding a cell does not make sense in a table since it would potentially change the layout, also any cell borders etc would also be hidden - probably not what you want.
Now CSS does not have any selectors based on element text content. But it does support attribute value selectors. So, you could change your code to be:
<table>
<caption>Test</caption>
<tr>
<th>Values</th>
<td><input value="$100"/></td>
</tr>
<tr>
<th>Initial value</th>
<td><input value="$0"/></td>
</tr>
</table>
And use a rule like
input[value="$0"] {
display: none;
}
You could even make the inputs not behave like inputs by adding a disabled attribute so they aren't editable.
If you don't want to use input elements, you could consider using spans instead and use a "data-value" attribute, and try if browsers respect that:
<table>
<caption>Test</caption>
<tr>
<th>Values</th>
<td><span data-value="$100">$100</span></td>
</tr>
<tr>
<th>Initial value</th>
<td ><span data-value="$0">$0</span></td>
</tr>
</table>
The css woudl be:
td > span[data-value="$0"] {
display: none;
}
Of course the drawback of this is that you would have to add the value twice (once as text content, once as attribute), and you need to generate an inner span element which feels a bit ugly.
Alternatively you could try to add a class attribute that includes the value and create a class selector:
<table>
<caption>Test</caption>
<tr>
<th>Values</th>
<td ><span class="value100">$100</span></td>
</tr>
<tr>
<th>Initial value</th>
<td ><span class="value0">$0</span></td>
</tr>
</table>
and the css would be:
td span.value0 {
display: none;
}
Of course the drawbacks are the same as with the previous method - you have to generate the value twice, once as text content and once as classname, and you need to add the inner span.
EDIT: dollar char is not valid in css classnames, so I removed it.
EDIT2: It turns out there is a way to do it without duplicating the value as both text and attribute. As a bonus, it turns out you don't need the inner span either if we rely on the :after pseudoclass (since it is that class that gets hidden, not the cell itself):
<table border="1">
<caption>Test</caption>
<tr>
<th>Values</th>
<td data-value="$100"></td>
</tr>
<tr>
<th>Initial value</th>
<td data-value="$0"></td>
</tr>
</table>
Using this css:
td:after {
content: attr(data-value);
}
td[data-value="$0"]:after {
content: "";
}

Using empty() on tbody keeps resizing width of entire table

I am using jQuery to reload data inside the tbody. However, the header column width keeps resizing event though I put the fixed width for the columns. I am using Firefox.
Simple HTML table:
<table style="width:95%;" border="1" align="center" >
<thead>
<tr>
<td class="header" style="width:20%">ProductFamily
<select onchange="selectCategoryChange()">
<option "1">1</option>
<option "2">2</option>
</select>
</td>
<td class="header" style="width:20%">ProductNum</td>
<td class="header" style="width:30%">ProductDesc</td>
<td class="header" style="width:5%">DT Rate</td>
<td class="header" style="width:5%">List Rate</td>
<td class="header" style="width:5%">Man Hour Unit</td>
<td class="header" style="width:5%">Precall</td>
<td class="header" style="width:5%">SOW</td>
<td class="header" style="width:5%">Box</td>
</tr>
</thead>
<tbody id="tbodyDataRow">
<!-- Data Rows Here -->
</tbody>
</table>
When the tbody.empty(), it automatically resizes the width of the header columns, which make the page looks ugly. and then when I reload the new rows, it resize it back to original width. Here is my Jquery code.
$(document).ready(function() {
selectCategoryChange = function()
{
var tbody = $("#tbodyDataRow");
tbody.empty();
};
});
This works, but I've not tested it in all browsers. Change HTML to:
<table border="1" align="center">
<thead>
<tr>
<th>ProductNum</th>
<th>Product</th>
<th>DT Rate</th>
<th>List Rate</th>
<th>Man Hour Unit</th>
<th>Precall</th>
<th>SOW</th>
<th></th>
</tr>
</thead>
<tbody id="tbodyDataRow">
<!-- tbody Content -->
</tbody>
</table>
You should avoid inline styles anyway. Add this CSS file:
table {
border: 1px solid black;
}
.header {
background-color: #ccc;
font-weight: bold;
min-width: 200px;
}
That'll fix your problem, but again it's not cross-browser tested. You might consider allowing the resize.
Final result Fiddle
Your table style="width:95%;" is the problem. You are expecting a table with 8 X 200px = 1600px width, but your table should has 95% as width, which can be more or less than 1600px. If it be deferent, the headers will be ajusted automatic. I'm sorry about my english.
I had a similar problem with empty() function applied on ul element.
HTML structure :
<table>
<tr>
<td>
<ul id="list"></ul>
</td>
<td>
...
</td>
</tr>
I have a JS timer which call an ajax function responsible to refresh the content of the list (ul). To update the list (ul) I started to empty it with the following code:
$("#list").empty();
At each iteration, the column width was incremented even if content was the same. I finally replaced the content by the initial content by using replaceWith function and this time it was ok:
$("#list").replaceWith('<ul id="list"></ul>');

Categories

Resources