Wrapping HTML table rows in <a> tags

HtmlHtml Table

Html 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.

https://jsfiddle.net/evwa451n/

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;
    };
}

JS Fiddle demo.

(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>

http://jsfiddle.net/ankJw/

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*/
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYarinView Question on Stackoverflow
Solution 1 - HtmlN4ppeLView Answer on Stackoverflow
Solution 2 - HtmlBojanglesView Answer on Stackoverflow
Solution 3 - HtmlWeslie LeungView Answer on Stackoverflow
Solution 4 - HtmlDavid ThomasView Answer on Stackoverflow
Solution 5 - HtmlJan View Answer on Stackoverflow
Solution 6 - Htmluser1310420View Answer on Stackoverflow
Solution 7 - HtmlChuck NorrisView Answer on Stackoverflow
Solution 8 - HtmlNoWarView Answer on Stackoverflow
Solution 9 - HtmlMiguel HughesView Answer on Stackoverflow