Wrapping HTML table rows in <a> tags
HtmlHtml TableHtml Problem Overview
Is it possible to wrap entire table rows in <a>
tags? I want the the entire row to be a clickable link.
If I try the following, the links get rendered above and outside the table:
This:
<table>
<a href="value_url"><tr><td>value</td><td>value</td></tr></a>
<a href="value_url"><tr><td>value</td><td>value</td></tr></a>
</table>
Renders like this:
<a href="value_url"></a>
<a href="value_url"></a>
<table>
<tr><td>value</td><td>value</td></tr>
<tr><td>value</td><td>value</td></tr>
</table>
Html Solutions
Solution 1 - Html
Edit 2021:
It seems nowadays there's better options that are more semantic and more screen-reader-friendly. Check out e.g. Jans solution.
Original answer:
as a link in each td is not a good alternative and using js is a bit dirty, here is another html/css approach:
HTML:
<div class="table">
<a class="table-row" href="/mylink">
<div class="table-cell">...</div>
<div class="table-cell">...</div>
<div class="table-cell">...</div>
</a>
</div>
CSS:
.table { display:table; }
.table-row { display:table-row; }
.table-cell { display:table-cell; }
Solution 2 - Html
It renders like that because the browser is respecting the W3C specification and only allowing <tr>
tags as direct descendents of <table>
.
As a solution, you could either put an <a>
tag inside each <td>
that points to the same URL:
<table>
<tr>
<td>
<a href="http://url/stuff">
First column
</a>
</td>
<td>
<a href="http://url/stuff">
Second column
</a>
</td>
</tr>
</table>
Or you could bind an onClick
handler to the <tr>
with JavaScript. A jQuery example would be this:
$('table tr').click(function() {
window.location = 'http://location/here';
});
Or, even better, use delegated events (jQuery 1.7+):
$('table').on('click', 'tr', function() {
window.location = 'http://location/here';
});
Solution 3 - Html
Another simple, CSS only solution is turning <a>
into block elements
<tr>
<td><a href="#">name 1</a></td><td><a href="#">link 1</a></td>
</tr>
<tr>
<td><a href="#">name 2</a></td><td><a href="#">link 2</a></td>
</tr>
The CSS would be:
td > a {
display: block;
}
Then the <a>
would take up the entire <td>
, and making entire row clickable without JS if the link is repeated for each cell in a row.
Solution 4 - Html
It's invalid HTML to have any elements other than thead
,tbody
or tfoot
as a direct child of a table
, and those elements have only tr
as their valid children.
Any other content must be inside of a td
or th
to be valid.
What you're seeing is the browser restructuring your DOM so that it's as valid as it can be. The problem with relying on that behaviour though is that different browsers react in different ways.
If you need a tr
to be interactive (and for clicks on those elements to lead somewhere/do something) you should use JavaScript.
But, short answer, yes you can do it, but it's not valid to do so, so please don't.
For interactive table rows, though, one solution (in plain JavaScript):
var rows = document.getElementsByTagName('tr'),
url;
for (var i=0,len=rows.length; i<len; i++){
rows[i].onclick = function(){
uri = this.getAttribute('data-url');
window.location = uri;
};
}
(Which, obviously, requires that the tr
elements have a data-url
attribute to specify where they should link to.)
Solution 5 - Html
New and fancy solution: It is funny that in 2021 there is still no clean solution for table rows with links - I advice you to use display grid instead. Using repeat(YOUR_COLUMN_COUNT, minmax(0, auto))
for grid-template-columns
achieves a grid to behave really similar to a table - you get auto sizing and adjusting columns. In addition you can use anchor tags and even create a sticky header without going insane:
<div style="display: grid; grid-template-columns: repeat(3, minmax(0, auto));">
<div>Header column 1</div>
<div>Header column 2 (Any content size works)</div>
<div>Header column 3</div>
<a href="https://example.com" style="display: contents;">
<div>Every column adjusts dynamically</div>
<div>Content 2</div>
<div>Content 3</div>
</a>
<a href="https://example.com" style="display: contents;">
<div>Content 1</div>
<div>Content 2</div>
<div>Content 3</div>
</a>
<a href="https://example.com" style="display: contents;">
<div>Content 1</div>
<div>Works like a table!</div>
<div>Content 3</div>
</a>
</div>
Update: Unfortunately the original solution below does not work unless the table rows are dynamically inserted with javascript (see example below). Browsers skip the syntax check when new elements are inserted via javascript, so that way it is possible to get the anchor tags to stay in the table and prevent the browser from moving them outside.
let tbody = document.getElementById("myTBody");
for (let i = 0; i < 2; i++) {
let anchor = document.createElement("a");
anchor.setAttribute("style", "display: contents;");
anchor.setAttribute("href", "https://example.com");
let row = document.createElement("tr");
let col1 = document.createElement("td");
let col2 = document.createElement("td");
col1.append(document.createTextNode("Column 1"));
col2.append(document.createTextNode("My longer Column 2"));
row.appendChild(col1);
row.appendChild(col2);
anchor.appendChild(row);
tbody.appendChild(anchor);
}
<p>This table doesnt work with anchors around the rows:</p>
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
</tr>
</thead>
<tbody>
<a href="https://example.com" style="display: contents !important;">
<tr>
<td>Column 1</td>
<td>My longer Column 2</td>
</tr>
</a>
<a href="https://example.com" style="display: contents !important">
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>
</a>
</tbody>
</table>
<p>This table does work with anchors around the rows:</p>
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
</tr>
</thead>
<tbody id="myTBody">
<!-- Rows get inserted by javascript -->
</tbody>
</table>
Original: You could also use display: contents;
. This causes the a tags childs to behave like they are a child of the a tags parent.
In your HTML:
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
</tr>
</thead>
<tbody>
<a href="https://example.com" style="display: contents;">
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>
</a>
<a href="https://example.com" style="display: contents;">
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>
</a>
</tbody>
</table>
Support in different browsers should be enough most of the times (see https://caniuse.com/#feat=css-display-contents).
Solution 6 - Html
You could use onclick, so you can get the content dynamically
<td onclick="window.location='http://www.google.com';" style='cursor: pointer;'>Hello</td>
Solution 7 - Html
Why you don't want to make click event on tr
? It's not possible to have anchor tag like you want so you can make click event and add aditional CSS for making it to look like anchor.
Jquery:
$("table tr").click(function() {
window.location = "http://www.google.com/";
});
Solution 8 - Html
Just in additional to others I have to add that I personnaly prefer to do like this
<tr onclick="window.location='http://www.google.com';" >
<td>Text...</td>
</tr>
in case of ASP.NET MVC do it like
<tr onclick="window.location = '@Url.RouteUrl("Product", new { SeName = @product.SeName })';">
Solution 9 - Html
The solutions provided here helped me, but I don't think that using window.location is a good approach because user cannot do ctrl+click, open in another tab, or see where the url is going to. I'm building this page and I like to see the url built without clicking it, and chrome shows this on hover. I could not get over this, so I decided to use @Bojangles suggestion above to wrap each td with a link.
First, add the data-href to each tr, as others suggested.
Then, wrap each td in a link via js:
jQuery(document).ready(function ($) {
//wrap each td's contents with a hyperlink targeting the data-href
$('*[data-href]').children("td").wrapInner(function () {
return "<a href='" + this.parentElement.attributes["data-href"].value + "'></a>";
});
});
optionally, use css to make the hyperlink use up all of the cell
table tr a {
display:block; /*the link occupies the whole cell, so it's all clickable*/
}