Just take me down to the solutions, please.
Creating a horizontal row of objects that are equidistant from each other is another one of those things in web design that is much more difficult than it should be. This can be a very useful thing to do, especially in fluid width layouts when you are trying to make the most of whatever horizontal space you have.
Here are the goals we are trying to achieve:
- The left-most object is left aligned with it’s parent element.
- The right-most object is right aligned with it’s parent element.
- Each object is equidistant from one another at all times.
- The objects will stop short of overlapping each other as the browser window narrows.
- The objects will not wrap down as the browser window narrows.
- The technique will work in a fluid width environment. Even one that is centered.
I tried a number of different techniques to try and achieve this. Let’s go through all my failures and then to the final technique which seems to work pretty good.
FAIL: Give each object a percentage left position
First I gave each image a unique class:
<img src="images/shape-red.png" class="first-r">
<img src="images/shape-green.png" class="second-r">
<img src="images/shape-yellow.png" class="third-r">
<img src="images/shape-blue.png" class="fourth-r">
Then I gave percentage-based left positioning to each of those classes:
img.first-r { left: 0%; position: relative; }
img.second-r { left: 25%; position: relative; }
img.third-r { left: 50%; position: relative; }
img.third-r { left: 75%; position: relative; }
Notice the relative positioning. This is required to get the left-most image to respect the parent element assuming the content is centered and not left-aligned. The problem with this is that the left margin being applied to the right-most object is 75% of the browser window width, but applied starting at the left of the parent element not the browser window. This can cause the right-most element to push off the screen (not respecting the right edge of the parent element). Also, inexplicably, these element will eventually wrap if you move the browser window narrow enough.
If you switch to absolute positioning here, you solve some of the above problem but then your objects will be left-aligned and ignore the parent elements left position entirely. Also, at narrow enough browser window sizes, the images will overlap. But hey, at least the objects were equidistant!
FAIL: Give the objects a common left percentage margin
My next stab was to give each element, except the first one, a common percentage left margin.
<span class="do-not-wrap">
<img src="images/shape-red.png">
<img src="images/shape-green.png" class="mover">
<img src="images/shape-yellow.png" class="mover">
<img src="images/shape-blue.png" class="mover">
</span>
Applying the margin:
img.mover {
margin-left: 15%;
}
You should be able to tell from looking at that percentage that this technique is doomed. I just picked a percentage that seemed to work best. There is nothing mathematical I could think of that would work here. Because the parent element is a percentage of the width of the browser window, and the margin is a percentage of the browser window not the parent element, the growth rates will be very difficult to match. Also notice the “no-wrap” span, which is necessary to prevent …wait for it… wrapping. But hey, at least the objects were equidistant!
FAIL: Just use a table!
Even “throwing in the towel” on CSS doesn’t seem to work here. I thought this would work for sure, being that tables have that sometimes-useful-sometimes-infuriating ability to auto space it’s cells evenly.
<table>
<tr>
<td class="leftalign">
<img src="images/shape-red.png">
</td>
<td>
<img src="images/shape-green.png">
</td>
<td>
<img src="images/shape-yellow.png">
</td>
<td class="rightalign">
<img src="images/shape-blue.png">
</td>
</tr>
</table>
Notice the extra align classes in the first and last cells. If all the cells are centered, that allows the objects to be equidistant but then neither the left object or the right object is aligned to the edge of the parent element. This is solveable by applying a left alignment to the left-most cell and a right alignment to the right-most cell — but then the objects are no longer equidistant. Back to the drawing board.
PASS: Flexbox Justification
<div class="container">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
.container {
display: flex;
justify-content: space-between;
}
See the Pen Equidistant Objects by Chris Coyier (@chriscoyier) on CodePen.
PASS: First on the left, float the rest right in equal size boxes
Fortunately the table idea sparked some thought. The first image needs to be left aligned, but all the rest of them could be right-aligned. In fact, if they are, and also inside of boxes that divide the rest of that space evenly, that might just do it. Perhaps this is best explained visually:
HTML:
<img src="images/shape-red.png">
<div id="movers-row">
<div><img src="images/shape-green.png"></div>
<div><img src="images/shape-yellow.png"></div>
<div><img src="images/shape-blue.png"></div>
</div>
CSS:
#movers-row {
margin: -120px 0 0 120px;
}
#movers-row div {
width: 33.3%;
float: left;
}
#movers-row div img {
float: right;
}
There is an example page, where I was working this. It’s not pretty… but you can see the winner on the bottom. I’m sure some of you all have some better solutions for this, so let me have it!
PASS: Using inline-block and justified text
This can be done by setting the elements to display: inline-block;
and the parent element to text-align: justify;
. Well, it’s slightly more complicated and what I’d call a bona fide CSS trick. You add an additional element (via pseudo element) that is 100% wide and the previous inline blocks will line up.
<div id="container">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
#container {
height: 125px;
text-align: justify;
border: 10px solid black;
font-size: 0.1px; /* IE 9/10 don't like font-size: 0; */
min-width: 600px;
}
#container div {
width: 150px;
height: 125px;
display: inline-block;
background: red;
}
#container:after {
content: '';
width: 100%; /* Ensures there are at least 2 lines of text, so justification works */
display: inline-block;
}
Demo:
Check out this Pen!
Very nice, although when the screen is reduced down to a small point they condense together but I don’t think anyone is going to go that far when they are when they are browsing and they need to look at something like that. Good job.
I don’t get why it does’t work with tables…. any way if you don’t want tables and don’t mind the extra div, this works in firefox probably others…
CSS:
HTML:
its just a table in disguise…=)
Interesting :)
Chris, thanks for such an awesome tip! I will definitely have to keep this trick in mind :D
I am sorry for my post above as i didn’t catch the part that the left and right img had to touch the borders. Thanks for the tip, keep the good work and love the videos.
Thanks! This looks like the perfect sollution. I will try it for sure in my next CSS challenge.
Keep it up,
Michal
Thank God! This has been bugging me for a while, so I’m glad someone with a larger brain than me has figured it out. Definitely going to use it!
Ahhh, sweet, sweet float property. Float property saves everything!
Ooh, this is helpful! I’m sitting here trying to do just that and you write about it! :)
Well, it’s messy, but I don’t think there’s a better way. Thanks for the suggestion.
It looks like the example page still fails the “stop short of overlapping” criterion, at least in my FF2. If I narrow down the window, The images start to overlap and cut each other, going from
OOOO
to something like
OICO
The elements appear to be stacked in the order (by their left-to-right position): 1432, which makes sense, as with the right-floating elements, that’s the same order they’d appear in the markup (I presume — I haven’t actually looked at the markup yet).
The reason the elements in the first attempt “inexplicably” wrap is that position:relative will offset a box relative to the position it would otherwise have (and not relative to the parent element).
Also, the percentages don’t necessarily refer to the browser window — they refer to the nearest ancestor which is itself positioned. If you want to use percentages relative to the parent element, just add a “position: relative” to the parent.
Proper use of absolute and relative positioning would solve all your problems. And, if you really wanted to use those nasty tables you could easily add a display:[inline][block] etc. With style, every element can be a span; you just have to remove the element’s default style.
Will need some hacks to work IE6…
Thanks to everyone who pointed out that indeed the “passing” solution does cause overlaps still. I missed that. Still a fairly effective solution I think and I think I prefer overlapping to wrapping in most cases that I would use this.
What about whole blocks of code like banners? I have 4 generated ads and i want them put together like this (not using tables).
Interesting Technique. Thank you for this. I’ll have to try it out and see how well this works.
You got my bookmark!
I tried this from scratch, and came up with almost the same solution you did:
HTML:
<div id="container">
<div id="butlast">
<div><img src="images/shapes-red.png"></div>
<div><img src="images/shapes-green.png"></div>
<div><img src="images/shapes-yellow.png"></div>
</div>
<img src="images/shapes-blue.png">
</div>
CSS:
#container {
min-width: 480px;
}
#butlast div {
float: left;
width: 33%;
}
#butlast {
margin-right: 120px;
}
The only real difference is that I grouped all but the last together instead of all but the first, and went with left floats instead of right. Just wrapping the whole thing in min-width: 480px takes care of the overlapping problem.
Nice work Vineet. Funny how it’s an exact inverse solution =). And thanks for the comment about min-width, applying that to the parent container will definitely help the overlapping problem for the browsers that support it.
this works well too – and uses more semantic markup
just needs a wrapper div called expando and a standard UL inside it – bada bing
http://www.fu2k.org/alex/css/cssjunk/equidistant
2 methods.
First riffs on yours, but has the first red circle inside the wrapper div and uses absolute positioning to shunt it to the left.
Second simply uses absolute positioning.
Min-width on the body (and an expression for IE6) takes care of the overlapping.
Two different Alex’s, Two more good examples. Nice work, both of you!
You have got to hand it to Alex Morris, his solution works in firefox and IE.
Thanks Alex ! ! !
Darn! Now I see that Bill G. and Steve B. , in spite of thier billions of bucks and thousands of developers, can’t get IE7 to parse the order of the list items in Alex Morris’s otherwise elegant solution.
hey you know what – forget that first-child stuff that crappy browsers dont understand.
you’d think that 4 items set at 25% would float correctly and make 100%
funnily enough good browsers do this fine but IE doesnt – so all you need to do is either set a separate sheet for IE and set the % to be a shade under 25% – say 24.9% and it renders perfect. so in short all u need is
and the css is simply:
#expando ul {
margin:0;
padding:0;
list-style-type:none;
}
#expando li span {
height:95px;
width:95px;
background:#f39;
}
#expando ul li {
width: 24.9%;
float: right;
background:#000;
margin:0 0 10px 0;
text-align:center;
}
You got my hopes up.
Good solution, although I was hoping you’d fix a different problem. I’ve been looking for a solution that does the same as the above, only for a list of elements that spans multiple rows.
If anyone has a good solution for that problem, do share :)
Not if you add position: relative to the container thereby establishing a new positioning context (ie all elements with position: relative/absolute will now be placed relative to the parent container and not the browser window).
FAIL: Use of it’s for its.
This one is a bit different than Alex’s: http://tjkdesign.com/lab/equidistant/
The method does not use “float”, but relies on “position:absolute” for all but the last element (that way the whole row is not out of the flow).
Also, I use left negative margin rather than right ones (I think it’s less headache).
Thanks Chris for the idea
Nice work Thierry, thanks for sharing!
Now the real challenge will be to figure out a way to do this without having to use any unique ID’s, with objects of any size. Hmmmmm
Nice. I’ve always done it by using a negative margin the same as the gap, such that:
<ul>
<li>item one</li>
<li>item two</li>
<li>item three</li>
<li>item four</li>
<li>item five</li>
<li>item six</li>
</ul>
div {
width : 600px; }
ul {
overflow : hidden; /* expand the ul around the floated li’s */
margin-left : -40px; }
li {
float : left;
display : block; width : 160px; margin-left : 40px; }
\sorry, maths is all screwy there, here’s my own write up : http://mattwilcox.net/archive/entry/id/977/
and here’s the example code: http://mattwilcox.net/sandbox/edge-to-edge/edge-to-edge-alignment.html
@Chris: Thanks
I think using adjacent sibling selectors we could do without the IDs, but the “any size thingy” is tricky ;-)
@Matt: The idea is to make this work inside a fluid container.
In your example, removing/changing the width of #content would break the layout.
@Thierry
Why could you not substitute the px I used in my example with percentages?
Here, have an example of a fluid version:
http://mattwilcox.net/sandbox/edge-to-edge/edge-to-edge-alignment-fluid.html
@Matt: your first example uses a fixed width container with fixed width children and the latest uses a fluid container with fluid children.
AFAIK, the examples given so far use fixed width elements (four) inside a fluid container.
Note that four is part of the challenge as keeping three apart is much easier.
Example: http://tjkdesign.com/lab/equidistant/three_is_easy.asp
@ Thierry
Please look at the link I provided above for a fluid varient.
My technique allows for any number of items on a row, you need only change the math; the technique stays the same whether you have three on a row or five, or any number: apply a negative margin on the containing element that is the same value as the gap between items. The only complicated bit is working out the math to get whatever number you want on one row.
My technique satisfies all of the point items set out in the body of the post. Further refinements are possible by supplying min-width values for the container, for example.
Also, for your information, the samples given in the body of this post do NOT have four fixed width items on a row. They have four ELASTIC containers on a row that each contain a fixed width item. You can achieve exactly the same effect using my example simply by popping an image into the LI’s. You can even use text-align to move them around within the LI itself.
But, for the sake of demonstrating how easy it is, here is an example with 4 on a row, and a minimum width per ‘cell’ of 120px:
http://mattwilcox.net/sandbox/edge-to-edge/edge-to-edge-alignment-fluid-4.html
If you want to add content like images into the li, that’s just mark-up. The ‘layout’ is the trick here, messing with content is trivial.
@Matt:
But we are trying to space out the images, not their container. Take a look at Chris’s diagram where is says “PASS”. Look at the big dots.
Why don’t you create another example where images behave as in the diagram (with that extra markup)? That way we don’t compare apples with oranges.
Aaah, I see the distinction now (which, even having re-read the post a third time, is not actually stated anywhere. My solution does check all the points, and yet also misses out on the exact effect that’s being looked for, because the example is more specific than the description of wanted features).
OK, with the nuance established, I can’t see a flexible way of achieving this with my method (or any other method than the one’s stated).
On the other hand, the method described in the post is also very specific and rather limited. It has a lot of excess mark-up, and it doesn’t lend itself well to multiple rows or dynamic generated content (I use my method mainly for category pages on e-commerce sites, where there’s an unknown number of items in a grid). How often are you going to want to align four images, each of which _must_ be exactly the same (pre-determined) width for the technique to work, on a page all in a row, in a fluid layout?
It’s a solution that works, but for something so very specific and requires such exact images, I think it’d be far easier to just set the UL as a position : relative, and then absolutely position the images directly (I’ve done that before too). If we already know the images must be an exact width, they may as well be a set max height too, so set the UL to block and give it a height too to avoid any ‘clearing’ issues.
Note to self, positioning not as easy as thought either.
heh, interesting! That ‘objects must be a fixed width’ detail that’s missing from the description really makes life very tricky. :-)
Good discussion fellas! It is interesting that having the objects of static width is what makes it difficult. I think we can all agree that this is a lot trickier than it should be.
There has been some great ideas posted here, but I still think the problem with all of them (including mine) is that there is just too much dependency on hard-coding all the details. With most techniques, you need to know HOW MANY, and HOW WIDE and hard-code both of those things. Some techniques even require unique ID’s for every single element. In a perfect world, we should be able to do this without know ANY of those things.
I would go so far as to say this is a major failing of CSS (and something that probably won’t be fixed in CSS3).
But Chris, what you want is possible with CSS2.1. Display:table will do the job. I’ve updated the page I pointed to before:
http://www.fu2k.org/alex/css/cssjunk/equidistant
#wrapper3 span
{
display: table-cell;
}
#wrapper3 span img
{
float: right;
}
#wrapper3 span:first-child
{
width: 1px;
}
No need to know how many elements there wil be. Nor are any heights or widths required, other than 100% on the “table” to make it stretch to fill the available width and 1px for the leftmost “table cell” to ensure that it collapses to be just the width of the image within it. Yes, this is basically just your original non-fail idea again.
It works in all browsers that support display:table (IE8b included)
Very nice Alex!
No good for IE6 and 7, sadly.
Like a number of things, life would be far easier if we could use _all_ of the bits of CSS we ought to be able to in a production environment, but thanks to a lack of support for some attributes on certain popular browsers, we can’t.
What makes this challenge so hard is that we are mixing unit types, and CSS sucks at that. We can work in pixels, or we can work in %, but mixing the two is, in many cases, impossible. If CSS supported expressions then this would not be so hard (width : 90%-120px;). Having looked into the CSS3 specs, I don’t think there is anything in there that would help us in this situation.
@matt wilcox
> No good for IE6 and 7, sadly.
Well, obviously. But my point is that it is a failing of certain current browser implementations, not something that CSS can’t do.
The display:table solution demonstrably works in any browser that supports display:table. Ergo, there is no need to look to CSS3 and wail. (Though a proper official way, display: spaced-out or whatever, would be preferable).
Equally obviously, it is not a viable solution at the moment, unless you supplement it with a Dean Edwards-style approach and use javascript to turn display:table into actual tables for those tricksy IEs
This is how I’ve been doing it:
.equidistant > * {
display: inline;
width: 30%;
float: left;
}
.equidistant > *:first-child { float: left; width: auto; }
.equidistant > * > * { float: right; }
Making Alex’s solution work in IE lt 8:
Using setExpression()
It is not the best solution as there is a reflow and expressions are kind of “evil”, but it may be usefull in some cases (and the reflow is not an issue if elements are below the fold).
Seems like :first-child is the best way to accomplish something like this. It works in both FF and IE7 perfectly (the IE7 part surprised me the first time I tried it).
Yes, there is a problem with IE6, but when is there NOT a problem with IE6? Odds are, you are hacking or using a conditional CSS anyway for other issues, so a couple of extra lines of code to make :first-child work shouldn’t be a big deal.
There is actually an easy way to do this with a table. Sometimes css makes things harder. The table solution is not as elegant as the css solution, but it works just as well. And, it won’t break in quirks mode. :-)
You can make the images in the FarLeftCell, MiddleCells and FarRightCell whatever width you want as long as they do not exceed the width of the parent object. (if the total width of your images equals 500px, but the parent is only 400px, the table will bee 500px)
The table cannot go any shorter that the total width of the images in its cells. The css sets the width for the MiddleCells to be 1px, but it could be 0px. The table realizes that the image is larger and expands the cell to fit around the image. The middle cells determine the amount equal space between the other cells. You have to interleave these between the other cells. Change the width in the css to reflect the number of middle cells you have. If you have 4 MiddleCells in the table divide 100% by 4 = 25%. If you have 6 MiddleCells, 100% divided by 6 = 16.66%.
VERY IMPORTANT: Make sure you put a non-breaking space ( ) in each of the SpacerCells. The Empty table SpacerCells won’t behave properly unless you do this.
HTML:
CSS:
After a weekend of trying to do this the right way, I figured out the easy way.
This is not my favorite way of doing this, but i set a DIV to {text-align: justify} and added a spacer image at the end of the images I wanted to evenly space, in this case, tile across with multiple rows with a width large enough to ensure it needed its own line.
Not pretty, but really easy and it works.
I worked out an alternate way of doing this a while ago which allows the same thing with no extra html elements.
Have a look at:
my blog… it works in all the usual suspects.
I wanted to solve a similar but different problem: distributing elements evenly but leaving some space at the edges (to be precise, 1/2 of the space between objects at each edge). My solution is similar to yours using inline-block and justified text, but had to change the html to
Does anyone know of a “cleaner” method that does not involve changes to the HTML?
Why not just plug some padding onto your container and shrink the width a bit?
Dan, I wanted a solution that worked for differently sized windows without having to do the math in JavaScript.
Thanks Chris it another nice article for interesting topic
Actually after reading this article i came with a new idea to solve the problem.
the main idea is that by math the margin should be mixed with px and %
so we can use margin for px and padding for %
i explain it in details here
http://mohammad-sabbagh.com/responsive-equidistant-menu-css/
How about if you had 100+ images of fixed height but aspect ratio has variable width. You want to fit as many as you can each row but as the browser window grows or shrink expand or shrink # of images and even perhaps size of image. Example would be google or bing image search. Expand/shrink browser viewable area and see what I mean. I want to mimic that behaviour for a set of N images.
I’m trying to do this with four images of different widths (company logos). I want the first logo to begin at the left edge and the last logo to be aligned with the right edge with logos 2 and 3 leaving an equal space between each. I can’t work out from the above, if any of the suggested solutions work for my scenario?
I used a variation of this to space out some links evenly, but I was able to do this without having to define any specific sizes. It works with the proviso that your content will never be wider than the container it is in, otherwise it will overflow to another line. On a quick test it also seems to work with images. The trick is add blank divs at either side.
So the css is:
and the html is:
In IE9+ you can do something even easier with calc() and margins. I think it’s more contained, and could be turned into a sass mixin with a couple arguments.
Codepen: http://codepen.io/robwierzbowski/pen/fromp
The short of it, in case the pen gets moved:
…ahem,