Paul Murray
Paul Murray

Reputation: 429

Responsive Table Breaking Page Layout

I have a page divided into three columns: Left hand nav (fixed width), content (remaining width), right hand nav (fixed width). The expected behavior is that as the screen is resized, the middle column (content) resizes responsively. In general, this works. The issue is that my center content sometimes has html tables, and sometimes those tables need to display more data than there exists horizontal space for them. My expectation in this situation is that my tables should scroll horizontally while staying within the normal bounds of the content column.

What actually happens is that the tables grow to take up the full width of the screen (enlarging the content column along with them), which completely breaks my UI. Instead of the columns appearing next to each other on the page, they stack vertically. As a workaround, I made my page set a watcher on the window resize event and store the screen width in my data store so that my table components can use it as a computed prop and change their max-width accordingly, but this is visibly sluggish and not an acceptable solution long-term.

I've been trying to solve this using pure css (and have a feeling there must be a way) but haven't made any progress on that front (other than using max-width, which is not ideal). I also have a feeling this is the same root issue as a similar problem I encountered a while back.

new Vue({
  el: "#app",
  data: {}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/[email protected]/dist/bootstrap-vue.min.js"></script>
<link href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/[email protected]/dist/bootstrap-vue.css" rel="stylesheet" />

<div id="app">
  <b-container fluid>
    <b-row>
      <b-col cols="auto">
        <div>left col</div>
      </b-col>
      <b-col>
        <b-button class="w-100" v-b-toggle="'collapse'">center col</b-button>
        <b-collapse id="collapse">
          <div class="w-100">
            <b-table-simple responsive>
              <b-thead>
                <b-tr>
                  <b-th>
                    Question:
                  </b-th>
                  <b-th v-for="i in 50" :key="i">{{ i }}
                  </b-th>
                </b-tr>
              </b-thead>
            </b-table-simple>
          </div>
        </b-collapse>
      </b-col>
      <b-col cols="auto">
        <div>right col</div>
      </b-col>
    </b-row>
  </b-container>
</div>

Upvotes: 1

Views: 4138

Answers (2)

Paul Murray
Paul Murray

Reputation: 429

I wasn't having success trying to use flex-box as was suggested in Hiws's answer due to some nesting issues in my final code so I decided to try to optimize my brute-force solution since I was out of ideas otherwise. In my original solution, I was seeing performance issues because I had a watcher for my page width that I was passing to the four table components on my page to dynamically set max-width. Instead of setting max-width on the table components, I set it on the column containing all four of them, reducing four dynamic widths down to one. This gave me the result I needed while appearing seamless and responsive.

Column JS:

  data() {
    return {
      windowWidth: window.innerWidth
    };
  },
  computed: {
    // magic number
    maxWidth() {
      return `${this.windowWidth - 612}px`;
    }
  },
  mounted() {
    window.addEventListener("resize", this.widthHandler);
  },
  destroyed() {
    window.removeEventListener("resize", this.widthHandler);
  },
  methods: {
    widthHandler() {
      this.windowWidth = window.innerWidth;
    }
  }

Column CSS:

<b-col :style="{ 'max-width': maxWidth }">

Upvotes: 0

Hiws
Hiws

Reputation: 10364

You'll need to go away from using b-row and b-col for your page layout and instead define a min-width on the two side panels, and make your middle container width: 100% and overflow-x: auto. And then wrap the entire thing in a container with display: flex.

I've tested it in Chrome, Chromium Edge and Firefox.

new Vue({
  el: "#app"
});
.table-wrap {
  width: 100%;
  overflow-x: auto;
}

.side-panel {
  min-width: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/[email protected]/dist/bootstrap-vue.min.js"></script>
<link href="https://unpkg.com/[email protected]/dist/bootstrap-vue.css" rel="stylesheet" />
<link href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />

<div id="app">
  <b-container fluid>
    <div class="d-flex">
      <div class="side-panel">
        <div>left col</div>
      </div>
      <div class="table-wrap">
        <b-button class="w-100" v-b-toggle="'collapse'">center col</b-button>
        <b-collapse id="collapse">
          <div class="w-100">
            <b-table-simple responsive>
              <b-thead>
                <b-tr>
                  <b-th>
                    Question:
                  </b-th>
                  <b-th v-for="i in 50" :key="i">{{ i }}
                  </b-th>
                </b-tr>
              </b-thead>
            </b-table-simple>
          </div>
        </b-collapse>
      </div>
      <div class="side-panel">
        <div>right col</div>
      </div>
    </div>
  </b-container>
</div>

Upvotes: 1

Related Questions