Adam Smith
Adam Smith

Reputation: 397

Display child elements in a single row unless they overflow, then display a single column

I have n elements containing a small amount of text that I want to display in a fixed-width container as one row like so:

Single row

However, when all the inner text elements cannot fit in a single row, I want them appear in a single column:

enter image description here

I don't want to use inline-blocks so that there will be multiple rows, and a dynamic number of elements per row:

enter image description here

And I don't want the elements to be split across n columns depending on the size. It should be one row, or one column if that doesn't fit.

Can this be done with just CSS? Perhaps using flexbox or grid layout?

Upvotes: 10

Views: 6040

Answers (4)

Aleksandr Belugin
Aleksandr Belugin

Reputation: 2227

As have already been said there is no CSS method of detecting overflow.

Dynamic solution

So we can make texts container display: flex with flex-wrap: wrap; and with javascript look on window resize if the last element offset top of text elements is bigger that first element's. And in this case modify texts container to flex-direction: column;

Look jquery and javascript versions in snippets in full page mode and resize the window.

Jquery version

$(function() {
  detectWrap();
});

$(window).resize(function() {
  detectWrap();
});

function detectWrap() {
  $(".container").each(function() {
    $(this).removeClass("direction-column");

    if ($(this).children().first().offset().top < $(this).children().last().offset().top) {
      $(this).addClass("direction-column");
    }

  });
}
.container {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  background-color: #D3D3D3;
  padding: 10px;
  margin: 10px 0;
}

.text-element {
  background-color: #A9A9A9;
  margin: 5px;
  padding-bottom: 4px;
}

.direction-column {
  flex-direction: column;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
  <div class="text-element">Some text, blah</div>
  <div class="text-element">Some text, blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
</div>

<div class="container">
  <div class="text-element">Some text, blah</div>
  <div class="text-element">Some text, blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
</div>

<div class="container">
  <div class="text-element">Some text, blah</div>
  <div class="text-element">Some text, blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
</div>

<div class="container">
  <div class="text-element">Some text, blah</div>
  <div class="text-element">Some text, blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
</div>

Javascript version

window.onload = function(event) {
  detectWrap();
};

window.onresize = function(event) {
  detectWrap();
};

function detectWrap() {
  const container = document.getElementsByClassName("container");

  for (let i = 0; i < container.length; i++) {
    container[i].classList.remove("direction-column");

    const firstText = container[i].firstElementChild;
    const lastText = container[i].lastElementChild;

    if (firstText.offsetTop < lastText.offsetTop) {
      container[i].classList.add("direction-column");
    }
  }

}
.container {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  background-color: #D3D3D3;
  padding: 10px;
  margin: 10px 0;
}

.text-element {
  background-color: #A9A9A9;
  margin: 5px;
  padding-bottom: 4px;
}

.direction-column {
  flex-direction: column;
}
<div class="container">
  <div class="text-element">Some text, blah</div>
  <div class="text-element">Some text, blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
</div>

<div class="container">
  <div class="text-element">Some text, blah</div>
  <div class="text-element">Some text, blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
</div>

<div class="container">
  <div class="text-element">Some text, blah</div>
  <div class="text-element">Some text, blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
</div>

<div class="container">
  <div class="text-element">Some text, blah</div>
  <div class="text-element">Some text, blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
</div>

<div class="container">
  <div class="text-element">Some text, blah</div>
  <div class="text-element">Some text, blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
  <div class="text-element">Some text, blah blah blah</div>
</div>

Upvotes: 1

MyHomework
MyHomework

Reputation: 47

The only way I know of to do that easily is by using the Bootstrap framework in your HTML.

https://getbootstrap.com/docs/4.4/layout/grid/Bootstrap Grid

<!--You put this in your <head> section:-->

  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">


<!--Put something like this in the body:-->

    <div class="container">
      <div class="row">
        <div class="col-sm">
          One of three columns
        </div>
        <div class="col-sm">
          One of three columns
        </div>
        <div class="col-sm">
          One of three columns
        </div>
      </div>
    </div>

Upvotes: -1

VorganHaze
VorganHaze

Reputation: 2066

I find this question interesting. One possible solution is using Media Query, but only if you know the exact width at which the .outer container will break the layout of your .inner classes. If the text won’t change dynamically, then you can easily calculate the width. In that case, you might use:

    .inner {
      display: inline-block;
    }

    @media only screen and (max-width: 877px) {
      .inner {
        display: block;
      }
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      font-size: 14px;
      color: #222;
      font-family: monospace;
    }

    .outer {

      padding: 0 10px;
      width: fit-content;
    }

    .inner {
      width: auto;
      height: auto;
      margin: 10px 0;
      background-color: darkgrey;
    }
  <div class="outer">
    <p>
      <span class="inner">blah blah</span>
      <span class="inner">blah blah blah</span>
      <span class="inner">blah blah blah blah</span>
      <span class="inner">blah blah blah blah blah</span>
      <span class="inner">blah blah blah blah blah blah</span>
    </p>
  </div>

It is an unusual workaround but it is CSS only.

Upvotes: 3

ajobi
ajobi

Reputation: 3116

I think this is not possible without JavaScript (because you need it to detect overflow). With that being said, you could base your solution around flexbox as it allows you to switch between row / column layout via single CSS property on your container element. Other option might be to switch between inline-block / block display on the children for example, but you would have to do that for all of them - that's why flexbox is better here. Example might look like this:

const outer = document.querySelector('.outer')

if (outer.clientWidth < outer.scrollWidth) {
  outer.classList.add('direction-column');
} else {
  outer.classList.add('direction-row');
}
.outer {
  background-color: lightgrey;
  width: 500px;
  padding: 10px;
  display: flex;
  align-items: flex-start;
}

.inner {
  background-color: darkgrey;
  margin: 5px;
  flex: none;
}

.direction-row {
  flex-direction: row;
}

.direction-column {
  flex-direction: column;
}
<div class="outer">
  <div class="inner">Some text, blah</div>
  <div class="inner">Some text, blah blah</div>
  <div class="inner">Some text, blah blah blah</div>
  <div class="inner">Some text, blah blah blah blah</div>
  <div class="inner">Some text, blah blah blah blah blah</div>
  <div class="inner">Some text, blah blah blah blah blah blah</div>
</div>

Upvotes: 9

Related Questions