简体   繁体   中英

How to distribute floated elements evenly with a dynamic column and row count in CSS?

I have multiple divs with a fixed width and height (think about some sort of catalog view with article pictures). Now I want to show them similar to the behavior of float:left. This ensures that the larger your browser window is, the more divs are shown in one row.

The downside of the float:left solution is, that there is a big white gap on the right side, until another div will fit. Now I have the job to distribute the divs evenly one the page, and instead of a big white gap on the right side, there should be evenly distributed gaps between the single divs.

A solution in JavaScript is easy: http://dl.dropbox.com/u/2719942/css/columns.html

You can see, if you resize the browser window, it behaves similar to float:left, but the space is evenly distributed between the boxes. The column and row count is dynamically calculated with JavaScript:

  function updateLayout() {
    var boxes = document.getElementsByClassName('box');
    var boxWidth = boxes[0].clientWidth;
    var boxHeight = boxes[0].clientHeight;
    var parentWidth = boxes[0].parentElement.clientWidth;

    // Calculate how many boxes can fit into one row
    var columns = Math.floor(parentWidth / boxWidth);

    // Calculate the space to distribute the boxes evenly
    var space = (parentWidth - (boxWidth * columns)) / columns;

    // Now let's reorder the boxes to their new positions
    var col = 0;
    var row = 0;
    for (var i = 0; i < boxes.length; i++) {
      boxes[i].style.left = (col * (boxWidth + space)) + "px";
      boxes[i].style.top = (row * boxHeight) + "px";

      if (++col >= columns) {
        col = 0;
        row++;
      }
    }
  }

Now I wonder if there is a solution without JavaScript? I would prefer a CSS-only solution, because I will have possibly up to hundreds of divs on one page.

I looked into CSS3 Flexible Box Layout , but this seems to be only useful for fixed column layouts. In my example, the column count gets calculated dynamically. Then I tried CSS3 Multi-column Layout , which I could get something similar working, but the divs are aligned vertically, cut off in the inside, and the browser support isn't there yet. This seems more useful for text, but in my case I have fixed divs with pictures, that shouldn't get cut off.

So my question is: can I realize something like this without JavaScript?

The closest pure CSS solution is based on text-align: justify .

Here are two of my answers showing the technique:

Here's a demo using your HTML/CSS: http://jsfiddle.net/thirtydot/5CJ5e/ (or fullscreen )

There's a difference in the way your JavaScript and this CSS handles the last row if there's a different number of boxes compared to the other rows.

Your JavaScript does this:

My CSS does this:

If what the CSS does with a different number of boxes on the last row is unacceptable, you could add some extra invisible boxes to complete the row:

http://jsfiddle.net/thirtydot/5CJ5e/1/ (or fullscreen )

Doing this has the issue that the invisible boxes increase the height of the containing element. If this is a problem, I can't think of a way to fix it without using a little JavaScript:

http://jsfiddle.net/thirtydot/5CJ5e/2/ (or fullscreen )

Of course, since JavaScript is now being used, you might as well use it to add the invisible boxes in the first place (instead of sullying the HTML):

http://jsfiddle.net/thirtydot/5CJ5e/5/ (or fullscreen )

( I also wrote a more complicated JavaScript fix for this in an earlier revision, before the idea of invisible boxes was brought to me. There should be no reason to use my old fix now.)

You will need to use Javascript to do this.

But just adjust the box class to adjust the margins of all the boxes at once.

// Calculate how many boxes can fit into one row
var columns = Math.floor(parentWidth / boxWidth);

// Calculate the space to distribute the boxes evenly
var space = (parentWidth - (boxWidth * columns)) / columns;

$('.boxClass').css('margin', space / 2);

-

Example: http://jsfiddle.net/VLr45/1/

也许MASONRY可以帮助安置?

So my question is: can I realize something like this without JavaScript?

I went through the same exercise recently and finally gave up and used JavaScript. Even abandoning IE 7 and 8 (which isn't practical) I couldn't find a pure CSS solution. I guess you could hack together a long list of media queries to accomplish this in a limited fashion.

I ended up writing a little (about 1K minified) library which handles width, height, proportionate scaling, max widths, scrolling, consistent margins and padding, etc.

Here is a rough example using my own code: http://jsfiddle.net/CmZju/2/ . I've placed a couple hundred elements in the example.

I would prefer a CSS-only solution, because I will have possibly up to hundreds of divs on one page.

The layout engine of the browser will have to perform the same (or similar) calculations that you do. Granted, it is highly optimized but I would test your JS solution on a few old PCs and a few mobile devices before discounting it as too slow.

It looks like you are using absolute positioning. My informal tests show absolute layout to be significantly faster than float calculation. This makes sense if you think about the work that the browser must do to calculate a relatively sized and floated item, versus just drawing an element at a specific place with specific dimensions.

If you wanted to really look at optimizing performance, you could only manipulate the content that was visible. This would be much more difficult than just resizing everything and would probably only be relevant if you had thousands of items on a page.

If you end up going the script route, you might want to take a look at jQuery Masonry for additional ideas. I have heard good things about it and it gave me some good ideas for my own library.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM