45

I'm not sure how to build a horizontal list that looks like this:

Centered list with bullets between items

Here are the rules:

  • There is an unlimited number of items in the list.
  • Each item should be on a single line and not wrap to a 2nd line.
  • Multiple items can be on a single line if there is space for them to fit
  • If multiple items are on a single line, they should be separated by a divider
  • The divider looks like a bullet, but it could be an image
  • Need it to work in modern browsers as well as IE8+

The thing I'm not sure how to do is to make the bullets appear only between items, and not also before or after each row of items.

Tauren
  • 26,795
  • 42
  • 131
  • 167
  • 1
    CSS has no way to distinguish between 'rows,' so it's not able to avoid putting a bullet before the first item on a subsequent row. – David Thomas Mar 09 '13 at 01:30
  • @Tauren You'll probably have to use `:first-child` or `:last-child` pseudo selectors. So that you can, have the item on the end not have a bullet. Depending on how you're styling the default items. – Amy Mar 09 '13 at 01:37
  • @Tauren have you strict with `ul li`'s or for achieving this you can try any other html structure too. – Sachin Mar 09 '13 at 01:51
  • Can you please post your html here and a test page. – Shail Mar 09 '13 at 02:27
  • do you allow jquery ? – Jeffrey Nicholson Carré Mar 09 '13 at 02:36
  • I agree with @Shail , it would be much appreciated if we know you have made an effort on this problem before consulting SO – Amy Mar 09 '13 at 03:03
  • @sweetamylase Without codes and test page , we cant provide a fruitful answer , to a question so specific . – Shail Mar 09 '13 at 03:11
  • 4
    see http://stackoverflow.com/questions/9847663/css-last-element-on-line – grc Mar 09 '13 at 03:14
  • 1
    @grc improved with centering : http://jsfiddle.net/bleuscyther/Y77LJ/ – Jeffrey Nicholson Carré Mar 09 '13 at 03:20
  • Sorry everyone to post this question and then disappear. Been super busy getting this project done. In the end, the client changed the requirements, so I didn't need to solve this anyway. Appreciate all your effort and assistance! – Tauren Mar 25 '13 at 07:16
  • @bleuscyther your jsfiddle is good. If you post as an answer, I'll accept as the solution. I was really hoping to not have to require JS, but am not surprised that it is needed. Thanks grc for the SO link! – Tauren Mar 25 '13 at 07:20
  • I stumbled upon this question when searching for how to do this in LaTeX rather than CSS and HTML; I eventually found it: [Creating a ragged right, inline list, separated by bullets or line breaks](https://tex.stackexchange.com/q/372662/146829). – Sebastian Simon Jul 09 '19 at 06:58

11 Answers11

37

For those of you who don't have to worry about IE8, this is as simple as:

ul li { list-style: none; display: inline; }
ul li:after { content: " \00b7"; }
ul li:last-child:after { content: none; }
  • 3
    Does this actually work? Won't it add a bullet at the end of each line, too? – Clément Mar 30 '16 at 23:34
  • 5
    Lines 2 and 3 can be simplified by using "not-last-child": `ul li:not(:last-child):after { content: " \00b7"; }` (It does indeed add a bullet at the end of lines which wrap) – Tom Robinson Oct 20 '16 at 17:28
  • 2
    This worked well with the unicode bullet too: `content: " \2022";` – mlissner Aug 14 '20 at 23:18
33

This solution matches all of OP's requirements, except IE8 compatibility (that was 2013).

Simple markup. No JavaScript. No :last-child

Link to CodePen

ul {
  display: inline-block;
  padding: 0;
  margin: .5rem;
  text-align: center;
  background-color: #fff;
}
li { display: inline; }
li a { white-space: nowrap; }
li:after {
  content: " ";
  letter-spacing: 1em;
  background: center center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwAAADsABataJCQAAABl0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC4xMkMEa+wAAAAnSURBVBhXY/Dz89MA4sNA/B9Ka4AEYQIwfBgkiCwAxjhVopnppwEApxQqhnyQ+VkAAAAASUVORK5CYII=);
}
body { background-color: #D3D3D3; }
<div id="d">
<ul>
  <li><a>Profile Image</a></li>
  <li><a>Name</a></li>
  <li><a>Activity Information</a></li>
  <li><a>Distance</a></li>
  <li><a>Pace</a></li>
  <li><a>Points Earned</a></li>
</ul>
</div>
<div style="width: 20rem"><script>document.write(d.innerHTML)</script></div>
<div style="width: 10rem"><script>document.write(d.innerHTML)</script></div>

enter image description here

myf
  • 9,874
  • 2
  • 37
  • 49
Tom Robinson
  • 1,850
  • 1
  • 15
  • 14
  • This seems like it's probably the best answer, based on your CodePen, but when I fork it and edit just the HTML (and leave your CSS intact), I don't understand why it doesn't work: https://codepen.io/anon/pen/MBdZwE – Ryan Aug 15 '18 at 18:59
  • 2
    This solution is a great one, but you need to be careful with whitespace. @Ryan, your CodePen sample has line breaks after the `` tags, which prevents the bullets from showing. If you remove the line breaks it works nicely. – wicketyjarjar Oct 14 '18 at 08:10
  • @wicketyjarjar Awesome catch! Thank you so much! +1 for you and Tom. – Ryan Oct 14 '18 at 12:55
  • Very clever. What allows this to work? I noticed that adding any other character (even other kinds of whitespace characters) causes layout issues and prevents the desired behavior. – gfullam Apr 01 '20 at 17:20
  • I know this is an old post but can anyone help explain how this works please. The solution is exactly what I was looking for but I just can't figure out how it works. I've looked and looked and looked some more but my mind has exploded both now and before! – philip Nov 24 '20 at 13:01
  • I absolutely cannot figure out how this skips adding the "bullet" between elements at the end of a visual line. Echoing the request for an explainer! – dgw Jan 11 '21 at 22:39
  • 1
    When a line wrap occurs, the white space that allowed the line wrap is replaced by the line wrap. So when the white space added by the li:after is rendered as a line wrap, it becomes essentially zero-width, and therefore doesn't show a background. – Tom Robinson Jan 13 '21 at 02:35
  • I've tried to explain this white space collapsing principle in example for similar answer https://stackoverflow.com/a/37053489/540955 – myf May 25 '21 at 22:50
14

For almost all browsers, you can use the CSS3 selector last-child instead of JavaScript:

ul li { display: inline; white-space: pre; }
ul li:after { content: "  \00b7  "; }
ul li:last-child:after { content: ""; }

The white-space: pre stops wrapping within list items (because usually you want it to wrap between list items), and is a hack that allows you to increase the space between list items by adding spaces on the second line.

u00b7 ⋅ (MIDDLE DOT) is the standard unicode character for interpuncts, but you could also use u2022 • (BULLET),   u2b24 ⬤ (BLACK LARGE CIRCLE),   U+2043 ⁃ (HYPHEN BULLET), or any other unicode character you choose.

Note that some characters may not be supported on all systems.

Zaz
  • 46,476
  • 14
  • 84
  • 101
  • 3
    The 2 drawbacks of this approach are: if there are linebreaks in the HTML, `pre` messes those up, and if a device is narrow enough that it's showing multiple rows of `li` elements, bullets also get shown at the end of each row (until the final one). – Ryan Aug 15 '18 at 18:50
10

Here is a further improved version. I kept getting an inconsistency at certain page widths where two bullets would be missing rather than just the last one. i.e.

link1 · link2 · link3 link4

link5 · link6

I think the issue was that removing the last bullet separator could itself affect the text flow if the page width was just right. The new script locks the original text flow by adding and removing literal line breaks.

I have the same script to run every time the screen is resized so you don't get stuck with awkward line breaks.

<style>
ul { width: 700px; text-align : center }
ul li { display: inline; white-space: nowrap; }
ul li:after { content: " \00b7"; }
ul li.nobullet:after { content: none; }
</style>

<body onresize="processBullets()" onload="processBullets()">
<ul>
    <li><a href="http://google.com">Harvard Medical School</a></li>
    <li><a href="http://google.com">Harvard College</a></li>
    <li><a href="http://google.com">Harvard Medical School</a></li>
    <li><a href="http://google.com">Harvard College</a></li>
    <li><a href="http://google.com">Harvard Medical School</a></li>
    <li><a href="http://google.com">Harvard College</a></li>
    <li><a href="http://google.com">Harvard Medical School</a></li>
    <li><a href="http://google.com">Harvard College</a></li>
</ul>
<body>

<script>
function processBullets() {
    var lastElement = false;
    $("br").remove(".tempbreak");
    $("ul li").each(function() {
        $(this).removeClass("nobullet");
        if (lastElement && lastElement.offset().top != $(this).offset().top) {
            $(lastElement).addClass("nobullet");
            $(lastElement).append('<br class="tempbreak" />');
        }
        lastElement = $(this);
    }).last().addClass("nobullet");
}

</script>
Community
  • 1
  • 1
Jeffrey Nicholson Carré
  • 2,950
  • 1
  • 26
  • 44
6

If you don't mind creating a PNG image (with transparent background) of the bullet (or other separator), then you can use a natural space between the list items painted with this as the background.

Where the list items wrap onto the next line, the space---and thus its background---won't be rendered.

This avoids layout issues relating to the space taken up by the separator, as well as avoiding any Javascript/jQuery, taking advantage of the browser's own layout engine to do the work. You can adjust the space for the separator with the word-spacing attribute.

You'll need to ensure there is no other whitespace within the markup that might otherwise be used as the natural space. You could use a higher-res image than the 5x5 here, in conjunction with background-size, so that it still looks ok when zooomed, but note IE8 doesn't support scaling of background images. The other drawback is that if you want to change the colour you'll have to edit the PNG.

FIDDLE

Code based on modifying @bleuscyther's answer:

CSS :

ul { max-width: 700px; padding: 0; text-align: center; }
ul li { display: inline; white-space: nowrap; }
ul .separator {
  word-spacing: 1.1em;
  background-repeat: no-repeat;
  background-position: 50% 60%;
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAOElEQVQI113M0Q3AIBRC0aM76P7jmHSmSj/6mibyc4EQkEEWuYtDmU1SXO1d6H7tjgupIl8+P+cD22cff9U1wbsAAAAASUVORK5CYII=);
}

HTML :

<ul>
    <li><a href="http://google.com">Harvard Medical School</a></li><span class='separator'>
    </span><li><a href="http://google.com">Harvard College</a></li><span class='separator'>
    </span><li><a href="http://google.com">Harvard Medical School</a></li><span class='separator'>
    </span><li><a href="http://google.com">Harvard College</a></li><span class='separator'>
    </span><li><a href="http://google.com">Harvard Medical School</a></li><span class='separator'>
    </span><li><a href="http://google.com">Harvard College</a></li><span class='separator'>
    </span><li><a href="http://google.com">Harvard Medical School</a></li><span class='separator'>
    </span><li><a href="http://google.com">Harvard College</a></li>
</ul>
web-tiki
  • 99,765
  • 32
  • 217
  • 249
Jake
  • 948
  • 8
  • 19
4

user2511031's solution is almost ideal... it's just not a valid HTML. There should not be any SPAN outside LI, inside UL.

But it doesn't mean that there is no really ideal solution. I found one!

No need to put the spans allover and clean white-spaces in the markup. Place the needed space into the ":after" pseudo element content, apply the background image to it.

It does the same!

ul { max-width: 700px; padding: 0; text-align: center; }
ul li { display: inline; white-space: nowrap; }
ul li:after {
    content: " ";
    word-spacing: 2em;
    background-repeat: no-repeat;
    background-position: 50% 60%;
    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAOElEQVQI113M0Q3AIBRC0aM76P7jmHSmSj/6mibyc4EQkEEWuYtDmU1SXO1d6H7tjgupIl8+P+cD22cff9U1wbsAAAAASUVORK5CYII=);
}

Here's the Fiddle

Community
  • 1
  • 1
Liphtier
  • 552
  • 6
  • 13