-1

I want to create a table of buttons that, when generated with x rows and y columns and user generated button captions, each button in a row is as tall as the tallest one in its row, and each button in a column is as wide as the widest one in its column.

Is there a solution to this with flexbox, or do I need to use JS?

.button1 {width:97px; height:37px;}
.button3 {height:37px;}
.button5 {width:61px;}
<table>
  <tr>
   <td><button class="button1">First</button></td> 
   <td><button>Second<br>Button</button></td>
   <td><button class="button3">Third</button></td>
  </tr>
  <tr>
   <td><button>Fourth is long</button></td> 
   <td><button class="button5">Fifth</button></td>
   <td><button>Sixth</button></td> 
  </tr>
</table>

Above snippet is also on JS Fiddle.

Thank you

cjl750
  • 4,380
  • 3
  • 15
  • 27
vooka
  • 3
  • 4
  • Your title and body are contradictory: Should the columns and rows conform to the width of the buttons, or should the buttons all match the size of the other buttons in their row/column? Are you trying to adjust the widths of the rows/columns themselves or the buttons within them? – cjl750 Sep 19 '17 at 20:12
  • Not all buttons should be the same size. The buttons in each column should have the width of their widest sibling while the buttons in each row should have the height of their tallest sibling. – vooka Sep 19 '17 at 20:28
  • @cjl750 ^^^^^^^ – vooka Sep 19 '17 at 21:45
  • Yeah, sorry, I saw your comment, but after some testing with CSS I just reaffirmed my initial guess that this is non-trivial. I think you need a JavaScript solution to traverse the table, find the largest button in each row/column, and then resize all the other buttons accordingly. I may be able to put something together for you later tonight or tomorrow, if someone else doesn't come along first. – cjl750 Sep 19 '17 at 22:09
  • @cjl750 ah, this is why I was hoping I could use flex box - I'm not very familiar with JS. Could you possibly point me in the right direction where I can familiarize myself enough to hopefully solve this? – vooka Sep 19 '17 at 22:28
  • We can make all buttons in a given row the same width with flexbox, and maybe the same height with some tricks, but consider the column scenario: how does a given cell know which column it's in? It doesn't because it doesn't share a parent with all the other cells in that column. They each have different parents (different ``). The cells in the third column all match `:nth-child(3)`, so if we know in advance that the largest one is somewhere in the third column, we can style all `:nth-child(3)` cells accordingly, but with user input, we can't know that in advance. – cjl750 Sep 19 '17 at 22:47
  • [See here](https://stackoverflow.com/questions/7246683/detect-the-widest-cell-w-jquery) and [also here](https://stackoverflow.com/questions/31945868/make-all-cells-in-a-table-have-the-same-width-equal-to-widest-cell-width) for some pieces to your puzzle. You'll have to basically find the widest element in each row/column, *then* use something like `:nth-child(x)` to style all the related cells that width, where *x* is that cell's [`.cellIndex`](https://www.w3schools.com/jsref/prop_tabledata_cellindex.asp). – cjl750 Sep 19 '17 at 22:49
  • @cjl750 now since I am looking to have each column have different width's based off their widest button instead of having every button have the width of the widest button...is this going to be a lot more difficult? – vooka Sep 20 '17 at 16:58
  • I had time to look at this today and post an answer. Hopefully it's comprehensive enough that you can run with it. – cjl750 Sep 20 '17 at 19:05

1 Answers1

0

Before we start, quick note, make sure you include a tbody tag inside your table tag; the browser will just add it for you anyway.

Now then: You can't pull this off with CSS alone. The reason is that CSS is a two-dimensional language, not a three-dimensional one. So you can structure your HTML into columns and get all the buttons in a column matching, or you can do a table as you have, and get all the buttons in a row matching, but you can't do both.

Here's an example of making buttons within the same column have the same width (but not height) with flexbox, by reversing the default flex behavior to allow growing but not shrinking:

.button1 {width:97px; height:37px;}
.button3 {height:37px;}
.button5 {width:61px;}

td div {
  display: flex;
}
td div button {
  flex: 1 0 auto;
}
<table>
  <tbody>
    <tr>
     <td><div><button class="button1">First</button></div></td> 
     <td><div><button>Second<br>Button</button></div></td>
     <td><div><button class="button3">Third</button></div></td>
    </tr>
    <tr>
     <td><div><button>Fourth is long</button></div></td> 
     <td><div><button class="button5">Fifth</button></div></td>
     <td><div><button>Sixth</button></div></td> 
    </tr>
  </tbody>
</table>

And an example of making heights in a row match, but not widths:

.button1 {
  width: 97px;
  height: 37px;
}
.button3 {
  height: 37px;
}
.button5 {
  width: 61px;
}
table {
  display: flex;
}
tr {
  display: flex;
}
td {
  display: flex;
}
td div {
  display: flex;
  flex-direction: column;
}
td div button {
  flex: 1 0 auto;
}
<table>
  <tbody>
    <tr>
      <td><div><button class="button1">First</button></div></td>
      <td><div><button>Second<br>Button</button></div></td>
      <td><div><button class="button3">Third</button></div></td>
    </tr>
    <tr>
      <td><div><button>Fourth is long</button></div></td>
      <td><div><button class="button5">Fifth</button></div></td>
      <td><div><button>Sixth</button></div></td>
    </tr>
  </tbody>
</table>

To get around CSS's limitations, we really need a JavaScript solution. We can query the width/height of each button in a given row/column, then set all buttons in that row/column to match that of the widest/tallest one.

The below snippet completes your request by setting both widths and heights in JavaScript, forgoing flexbox entirely. But as you can see from the CSS examples above, it's conceivable that you could use flex CSS to handle one dimension and then pull out the relevant half of the JavaScript below to handle the other dimension.

$(function() {
  //for each row
  $('table tr').each(function() {
    var maxHeight = 0;
    var tallestButton = null;
    var $element;
    //find the tallest button
    $(this).find('td').each(function() {
      $element = $(this).find('button');
      var buttonHeight = $element.height();
      if (buttonHeight > maxHeight) {
        maxHeight = buttonHeight;
        tallestButton = $element;
      }
    });
    //then set each button in that row to the widest button's width
    $(this).find('td button').each(function() {
      $(this).height(tallestButton.height());
    });
  });

  //find width of table in columns, based on number of cols in first row
  var x = $('table tr:first-child td').length;

  //for each column
  for (var i=1; i<x+1; i++) {
    var maxWidth = 0;
    var widestButton = null;
    var $element;
    var cellInCol = $('table tr td:nth-child(' + i + ')');

    $(cellInCol).each(function() {
      $element = $(this).find('button');
      var buttonWidth = $element.width();
      if (buttonWidth > maxWidth) {
        maxWidth = buttonWidth;
        widestButton = $element;
      }
    });
    $(cellInCol).find('button').each(function() {
      $(this).width(widestButton.width());
    });
  }
});
.button1 {
  width: 97px;
  height: 37px;
}
.button3 {
  height: 37px;
}
.button5 {
  width: 61px;
}
.button7 {
  height: 80px;
}
table {
  border-collapse: collapse;
}
table td {
  border: 1px solid gray;
  padding: 5px;
  text-align: center;
}
button {
  white-space: no-wrap; /* fix for long captions on PC */
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <tbody>
    <tr>
      <td><button class="button1">First</button></td>
      <td><button>Second<br>Button</button></td>
      <td><button class="button3">3rd</button></td>
    </tr>
    <tr>
      <td><button>Fourth is long</button></td>
      <td><button class="button5">Fifth</button></td>
      <td><button>Sixth (6th)</button></td>
    </tr>
    <tr>
      <td><button>Six</button></td>
      <td><button class="button7">7</button></td>
      <td><button>8</button></td>
    </tr>
    <tr>
      <td><button>Nine</button></td>
      <td><button>Ten</button></td>
      <td><button>Eleven</button></td>
    </tr>
  </tbody>
</table>
cjl750
  • 4,380
  • 3
  • 15
  • 27
  • Thank you so much @cjl750. I'm fluent enough in programming that I can tell what the JS is doing but could you inform me as to why the need for the first line of the html section - why does the JS only work with that – vooka Sep 20 '17 at 21:31
  • also, when you give a longer caption to a button a good amount of the time the last word will be on a second line. It's almost as if the widest button for that column get's resized smaller than it was previously forcing part of it's caption to wrap? – vooka Sep 20 '17 at 21:48
  • @vooka The script at the beginning of the HTML is the jQuery framework. Since I'm using jQuery in my code, we have to include that. In stack snippets, when you include an external resource, it just adds it to the HTML. On your page, you can either include a link to jQuery somewhere in your `` or in the `` of your document, but it must be included before the actual script doing the button resizing, or you'll get errors that `$` is not defined. Since you tagged your question with jQuery, I presumed it was okay to use, but if not, this would have to be rewritten in vanilla JS. – cjl750 Sep 20 '17 at 22:20
  • As for the long caption issue you mention, I can't reproduce that in the snippet included in this answer, unless I'm just missing something. Can you provide more info? Are you seeing this problem if you edit the captions in my snippet and run it, or are you seeing the problem on your actual webpage? – cjl750 Sep 20 '17 at 22:22
  • @cjl705 if you move all your code to jsfiddle - and change the button caption of 8, from "8" to "This is the longest button this this table" - you should be able to replicate what I'm talking about. As a fix, I place a 1+ in front of widestButton.width() – vooka Sep 20 '17 at 23:17
  • @cjl705 the jquery is fine, I'll have to familiarize myself with that as well. One more question for you though - if there is another table of buttons (or just another table), will this code work through both of them (or ignore one) or does it have to be set up differently? – vooka Sep 20 '17 at 23:20
  • @vooka The script would work on any table on the page as it is written. If you run into problems, you could wrap the whole thing (everything inside `$(function(){ ... }`) in another each function for tables. Or, to exclude certain tables, you could also just modify the selectors. For example, instead of `$('table tr').each(function...`, you'd have something like `$('table.yourClass tr').each(function...`. And I still do not see a problem with long captions when dropping this into JS Fiddle: https://jsfiddle.net/5byhwmf1/ – cjl750 Sep 21 '17 at 01:13
  • I was able to replicate the issue in codepen, notice how the fifth button creates a second line because there isn't enough space for it - https://codepen.io/vooka/pen/yzajMM – vooka Sep 22 '17 at 16:24
  • @vooka Can you link a screenshot or something? Everything looks fine to me in that pen. I don't know if we're not seeing the same thing, or I'm not understanding the problem. – cjl750 Sep 22 '17 at 16:45
  • @vooka Figured out that this happens on a PC but not on a Mac. Adding `white-space: nowrap` to buttons seems to fix the problem. I will edit my answer to include that. – cjl750 Sep 22 '17 at 17:26
  • sorry for just now marking this as answered, I'm new to stackoverflow. Thank you so much – vooka Oct 02 '17 at 21:32