Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Layout divs either with wrapping or side by side using CSS only

Tags:

css

I'm developing a control aiming to display activity blocks on a calendar grid. It is a plain JavaScript/CSS, relying on jQuery for DOM manipulation and such. Here is a picture:

Calendar control in need of proper layout

There are bands A and B, each containing a couple of activity blocks ([1,2], [3,4]). Activities can either overlap or follow each other sequentially. My goal is to place the activity blocks accordingly: if activities overlap like [1,2], I want them wrap and sit on top of one another like pictured; if they are sequential like [3,4], I want them side by side, NOT like pictured.

Additionally, I would like bands' (A,B) height to adjust automatically. Thus, a band with overlapped activity blocks would have twice the height of the band with sequential ones.

At this point I can get either one or the other.

If activity blocks have display: block;, the activities wrap regardless whether they actually overlap (3,4). Band's height does get adjusted accordingly.

If activity blocks have display: inline-block;, activities share the same height so one gets hidden by the other (1,2). The band stays one activity block in height.

Everything is a div and here is the relevant HTML/CSS:

<div class="band">
  <div class="activity-block" style="left: 331.429px; width: 11.4286px;"></div>
  <div class="activity-block" style="left: 160px; width: 297.143px;"></div>
</div>
<div class="band">
  <div class="activity-block" style="left: 205.714px; width: 22.8571px;"></div>
  <div class="activity-block" style="left: 365.714px; width: 3417.14px;"></div>
</div>
<div class="band"></div>

Bands (A, B):

.band {
  min-height: 20px;
}

Activity blocks (1,2,3,4):

.activity-block {
  background-color: #66C6C2;
  border-radius: 3px;
  display: block;
  height: 20px;
  margin-bottom: 5px;
  margin-top: 5px;
  position: relative;
}

left and width of an activity block are set from JavaScript.

I would like to get there using only two CSS classes, one for the band and one for the activity blocks. I realize the goal can be achieved using JavaScript, but I wonder if this is something possible via CSS only.

like image 374
Nic Nilov Avatar asked Nov 23 '15 22:11

Nic Nilov


2 Answers

As commenting users suggested there is no way to do this in CSS only. CSS works in let's call it "rows". What you are trying to achieve is to based on some condition place content in one or two rows, that is something that CSS cannot handle. You can only specify how content behaves in its own row. The closest you can get is to define elements as inline-block with proper left padding when not overlapping and block when they are, but you cannot do that without using JavaScript for handling the logic.

like image 20
okolimar Avatar answered Oct 20 '22 21:10

okolimar


See if it suits your demands: you want to set positions in javascript and let css break lines for you if there is an overlap, right?

The idea is to put each block to float to one side and set the margins with your javascript code. But the band div must be float, or else it won't grow to accomodate the line break that happens in case of overlap (more on this in Floating elements within a div, floats outside of div. Why?).

<html>
<head>
    <style>
        .activity-block {
            background-color: #66C6C2;
            border-radius: 3px;
            height: 20px;
            margin-bottom: 5px;
            margin-top: 5px;
        }
        .band {
            min-height: 30px;
            border: solid 1px;
            width: 1200px;
            float: left;
        }
        .fl {
            float: left;
        }
        .fr {
            float: right;
        }
    </style>
</head>
<body>
    <div class="band">
        <div class="activity-block fl" style="margin-left: 331px; width: 81px;">1</div>
        <div class="activity-block fr" style="margin-right: 160px; width: 497px;" id="x2">2</div>
    </div>
    <div class="band">
        <div class="activity-block fl" style="margin-left: 205px; width: 22px;">3</div>
        <div class="activity-block fr" style="margin-right: 365px; width: 417px;" id="x4">4</div>
    </div>
</body>

I made a jsFiddle for testing:

https://jsfiddle.net/vdusch4y/6/

Some restrictions you need to check:

  1. No more than 2 blocks per band.
  2. The second block in the div floats to the right and represents an activity that starts after the first.
  3. Instead of setting left in your javascript, you set the margin-right of the second block inside each band, according to: (margin-right) = (band width) - (calculated block left) - (calculated block width). For symmetry, I am using margin (left in the case) for the first block too.

In the jsFiddle example, try changing the band width, or block margins/widths, to see how the break occurs automatically, leaving the band with a bigger height.

Hope it helps!

like image 179
Eduardo Poço Avatar answered Oct 20 '22 21:10

Eduardo Poço