Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

html

css

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?

like image 534
Adam Smith Avatar asked Jan 31 '20 01:01

Adam Smith


People also ask

What is* display in CSS?

The display CSS property sets whether an element is treated as a block or inline element and the layout used for its children, such as flow layout, grid or flex. Formally, the display property sets an element's inner and outer display types.

What is display properties?

The display property specifies the display behavior (the type of rendering box) of an element. In HTML, the default display property value is taken from the HTML specifications or from the browser/user default style sheet. The default value in XML is inline, including SVG elements.

What is display block HTML?

The display property sets or returns the element's display type. Elements in HTML are mostly "inline" or "block" elements: An inline element has floating content on its left and right side. A block element fills the entire line, and nothing can be displayed on its left or right side.


3 Answers

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>
like image 69
ajobi Avatar answered Oct 21 '22 20:10

ajobi


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.

like image 3
VorganHaze Avatar answered Oct 21 '22 18:10

VorganHaze


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>
like image 1
Aleksandr Belugin Avatar answered Oct 21 '22 20:10

Aleksandr Belugin