Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dynamic grid tiles spanning multiple rows + columns

Tags:

html

css

This may be hard to explain without seeing the result, so please have a look at these examples:

JSFiddle Example 1 - looks okay
JSFiddle Example 2 - broken

Code from fiddles:

HTML:

<ul id="homepage-grid">
        <li id="tile11" class="col1 row1 sizex1 sizey1">
            <a href="#" title="test">
                <img src="http://placehold.it/295x160" alt="test" title="test" style="width: 295px;height: 160px" />
            </a>
        </li>
        <li id="tile8" class="col2 row1 sizex2 sizey1 last">
            <a href="#" title="test">
                <img src="http://placehold.it/602x160" alt="test" title="test" style="width: 602px;height: 160px" />
            </a>
        </li>
        <li id="tile1" class="col1 row2 sizex1 sizey1">
            <a href="#" title="testing">
                <img src="http://placehold.it/295x160" alt="testing" title="testing" style="width: 295px;height: 160px" />
            </a>
        </li>
        <li id="tile4" class="col2 row2 sizex2 sizey1 last">
            <a href="#" title="test3">
                <img src="http://placehold.it/602x160" alt="test3" title="test3" style="width: 602px;height: 160px" />
            </a>
        </li>
        <li id="tile10" class="col1 row3 sizex1 sizey2">
            <a href="#" title="test">
                <img src="http://placehold.it/295x332" alt="test" title="test" style="width: 295px;height: 332px" />
            </a>
        </li>
        <li id="tile12" class="col2 row3 sizex1 sizey2">
            <a href="#" title="test">
                <img src="http://placehold.it/295x332" alt="test" title="test" style="width: 295px;height: 332px" />
            </a>
        </li>
        <li id="tile2" class="col3 row3 sizex1 sizey1 last">
            <a href="#" title="testing2">
                <img src="http://placehold.it/295x160" alt="testing2" title="testing2" style="width: 295px;height: 160px" />
            </a>
        </li>
        <li id="tile9" class="col3 row4 sizex1 sizey1 last">
            <a href="#" title="test">
                <img src="http://placehold.it/295x160" alt="test" title="test" style="width: 295px;height: 160px" />
            </a>
        </li>
    </ul>

CSS:

#homepage-grid {
    width:910px;
    position:relative;
    padding:0;
    overflow: hidden;
}

#homepage-grid li {
    list-style:none;
    float:left;
    padding:0 12px 12px 0;
    display:block;
}

#homepage-grid li.last {
    list-style:none;
    float:left;
    padding:0 0 12px 0;
}

#homepage-grid li a {
    display:block;
}

Basically what I want to create is a dynamic grid, which is populated by a database (the database part works fine at the moment). In the grid, each tile can span up to 3 columns wide, however can span unlimited rows, which seems to be where I'm running into the problems.

I'm having trouble with the HTML/CSS for something so dynamic. As you can see, one small change from example 1 to example 2 and it broke most of the grid as the bottom left tile should be pushed up to fill the space, and the right tile should be moved up to fill that space.

However, I have full control over the code, so the HTML/CSS can change as I need it to (i.e. to add classes/inline styles/etc).

I guess this would work (relatively) easily using tables, but since it's not tabular content, I don't really want to go down that path.

Would there be a way to make the CSS this dynamic?
Would I need to use more inline styles to achieve this?
Should I be doing it in another way, e.g. absolutely positioning instead of floating?

Any help on how to achieve this would be appreciated.

like image 913
Nick Avatar asked Mar 01 '13 15:03

Nick


2 Answers

My first thought was just to set the height of #title12 to 160px, but this doesn't work if the element below it is the same width.

Instead, you might try the following:

Assuming each "row" of your grid has a fixed height, wrap it with a <div>...</div> or a similar block element, with height:160px; (or whatever size is appropriate, but make it the same for every div "row").

Fill the first row with the first row of images. For the second (and subsequent) rows, determine if each "cell" is overlapped by the row before it. If so, add a "filler" block consisting of an empty block element with height and width equal to the row and column sizes you desire.

This allows the <li>...</li> item(s) to overflow the div "row" into the one(s) below (since overflow is set to visible by default). The empty block elements prevent the next row from overlapping the overflow from the previous one.

The disadvantages to this approach are:

  • It makes your HTML somewhat uglier.
  • It's perhaps too much like a table to make it worth using instead of a real table.
  • It will require some additional work on the server-side.

EDIT: Also, <div> isn't allowed within a list, so you would need to start/stop your list within each row. Alternately, perhaps use the <ul> </ul> as rows instead of <div>.

like image 114
Josh Jackson Avatar answered Sep 17 '22 12:09

Josh Jackson


You're going to run into this problem with floats. However, you can change the positioning of one element on that page, and everything will fall into place like you expect.

Updated Fiddle

Add the following to your CSS:

#homepage-grid li#tile10 {
    position: absolute;
    top: 342px;
}

This is breaking the offending element out of document flow, thus eliminating the reservation on vertical space being assigned to it by the "float" applied to the rest of the list items. The last item is floated right, so will not overlap the absolutely positioned tile.

EDIT

Per the comments below, here's a slightly more flexible way of doing this. The example works for the posted code.

Assumptions made with the updated fiddle:

  1. The sizex1, sizey1, sizex2 etc... are constant values
  2. That the content to the right of these will be floated right (This will work in reverse for left floated items, but those scenarios seem like they wouldn't be an issue as the items on the right will float properly if elements to the left are vertically larger)
  3. The user has the ability to override styles, or apply styles upon layout creation This is primarily to control the position: absolute from being applied unnecessarily to certain elements

What this does:

This CSS sets different rules based on the existence of siblings with certain classes. This is why the constancy of the sizex1 sizex2 sizey1 and sizey2 values are important.

The CSS:

#homepage-grid li.col1:not(.row1):not(.row2) {
    position: absolute;
}
#homepage-grid li.col1.sizey1.row1 ~ li.col1.row2 { /* The first column of the first row has a sizey of 1 */
    top: 172px;
}
#homepage-grid li.col1.sizey2.row1 ~ li.col1.row2{ /* The first column of the first row has a sizey of 2 */
    top: 342px;
}
#homepage-grid li.col1.sizey1.row1 ~ li.col1.sizey1.row2 ~ li.col1.row3 { /* The first column of the first row has a sizey of 1 and the first column of the first row has a sizey of 1 */
    top: 344px;
}
#homepage-grid li.col1.sizey2.row1 ~ li.col1.sizey2.row2 ~ li.col1.row3{ /* The first column of the first row has a sizey of 2 and the first column of the second row has a sizey of 2 */
    top: 684px;
}
#homepage-grid li.col1.sizey2.row1 ~ li.col1.sizey1.row2 ~ li.col1.row3,
#homepage-grid li.col1.sizey1.row1 ~ li.col1.sizey2.row2 ~ li.col1.row3 { /* The first column of the first row has a sizey of 2 and the first column of the second row has a sizey of 1, or vice versa */
    top: 514px;
}

Ideally, you'd want to set the #homepage_grid li.col1 value at page delivery. That way, you can pick and choose which tiles are broken out of document flow to preserve correct spacing. I'd normally accomplish this by using the :not() selector, but if you're looking for older browser compatibility, you could just as easily use an override.

Example:

#homepage-grid li.col1:not(.row1):not(.row2) {
    position: absolute;
}

*The above will only break rows 3 and greater out of the document flow.

#homepage-grid li.col1 {
  position: absolute;
}
#homepage-grid li.col1.row1, #homepage-grid li.col1.row2 {
  position: static;
}

This accomplishes the same thing as above, but is more verbose. However, it will render appropriately in less standards-compliant browsers.

EDIT 2 Just for clarity's sake, here's an example using jQuery to set the appropriate elements as static on page load.

Another fiddle example

like image 31
Josh Burgess Avatar answered Sep 16 '22 12:09

Josh Burgess