Home > Net >  How to avoid wrapping of elements in a long list with scrollbars in Firefox
How to avoid wrapping of elements in a long list with scrollbars in Firefox

Time:05-25

I have a long flex list which should wrap it's items (Buttons) when they have not enough space (flex-wrap). However, as soon as the list gets a scrollbar, the items will be wrapped in Firefox even if there is enough space.

Chrome displays the buttons side by side as expected.

(I use Vue here, to keep the HTML simple. It actually just creates the text/button element 30 times)

new Vue({
  el: "#app",
})
body, html {
  margin: 0;
}

.container {
  position: absolute; 
  height: 100vh; 
  overflow: auto;
}

.row {
  display: flex;
  flex-wrap: wrap;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div  id="app">
  <div v-for="(i, index) in 30" :index="i">
    Text
    <div >
      <button>Button 1</button>
      <button>Button 2</button>
    </div>
    <hr />
  </div>
</div>

CodePudding user response:

I see the flex container taking the whole available space in the viewport as expected by height: 100vh;. I'm using firefox.

Then your next problem is not about the vertical space but how to correctly display elements inline. You should have your buttons with display: inline-block; to start with.

Then you should try to set the width of the closest parent... I added at first a rule to the parent elements .container > div so that their width is set to width:fit-content;. Later I moved it to the .container, and better sized the scrollbar feature using overflow-y: scroll.

When using overflow: auto it will show the scrollbar only when needed but the container size will not take into account the possibility that the scrollbar will be shown or not. So in case they will be shown, they will occupy inner space covering content.

When using overflow: scroll it will always show the scrollbar and the container size will always take them into account.

To make the container size adaptive also when using overflow: auto there are several options like this one used here:

scrollbar-gutter: stable;

https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-gutter

But in the end, to have a better control over the width of your container, you can check via javascript if the content is overflowing the container, and only in that case add (or unset in the opposite case) the css attribute overflow: scroll.

I also added the box-sizing: border-box to avoid containers to occupy some extra vertical space.

Here's the demo showing two "apps" next to each other, one with a container having content not overflowing (thus no scrollbar) and another one having content overflowing (thus showing scrollbar):

This solution refreshes the containers in case the window was resized and adds/remove the scrollbars if the overflow condition of contents changed.

new Vue({
  el: "#app1", 
})

new Vue({
  el: "#app2", 
})

refreshPage();
window.addEventListener('resize', refreshPage);

function refreshPage(){
  const app1 = document.getElementById('app1');
  const app2 = document.getElementById('app2');

  const isApp1Overflowing = app1.scrollHeight > app1.clientHeight;
  const isApp2Overflowing = app2.scrollHeight > app2.clientHeight;
  
  if (isApp1Overflowing)
    app1.style.overflowY = 'scroll';
  else
    app1.style.overflowY = 'unset';

  if (isApp2Overflowing)
    app2.style.overflowY = 'scroll';
  else
    app2.style.overflowY = 'unset';
}
body, html {
  margin: 0;
}

.container {
  /*added borders to container to better highlights its size*/
  border: solid 1px black;  
  /*it will take 100% of viewport height*/
  height: 100vh;   
  /*scroll instead of auto to have the scrollbar not occupying inner space*/
  /*overflow-y: auto;*/
  /*scrollbar-gutter: stable;*/
  /*fit-content will adapt to content, otherwise if unset will go 100%*/
  width: fit-content;
  /*to add some padding from the container borders*/
  padding: 5px;  
  white-space: nowrap;
  vertical-align:top;
  box-sizing: border-box;
}

.row button{
  /*to have the buttons next to each other*/
  display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div  id="app1" style="display: inline-block">
  <div v-for="(i, index) in 5" :index="i">
    Text
    <div >
      <button>Button 1</button>
      <button>Button 2</button>
    </div>
    <hr />
  </div>
</div>

<div  id="app2" style="display: inline-block">
  <div v-for="(i, index) in 25" :index="i">
    Text
    <div >
      <button>Button 1</button>
      <button>Button 2</button>
    </div>
    <hr />
  </div>
</div>

  • Related