Add a pipe separator after items in an unordered list unless that item is the last on a line

Is it possible to style this html ...

<ul>
    <li>Dogs</li>
    <li>Cats</li>
    <li>Lions</li>
    <li>Tigers</li>
    <li>Zebras</li>
    <li>Giraffes</li>
    <li>Bears</li>
    <li>Hippopotamuses</li>
    <li>Antelopes</li>
    <li>Unicorns</li>
    <li>Seagulls</li>
</ul>

... like this ...

enter image description here

... without adding classes to specific list items, or resorting to javascript? And if so how?

The line breaks are not fixed; the list widens to take up additional space, and list items are center aligned.


Just

li + li::before {
    content: " | ";
}

Of course, this does not actually solve the OP's problem. He wants to elide the vertical bars at the beginning and end of lines depending on where they are broken. I will go out on a limb and assert that this problem is not solvable using CSS, and not even with JS unless one wants to essentially rewrite the browser engine's text-measurement/layout/line breaking logic.

The only pieces of CSS, as far as I can see, that "know" about line breaking are, first, the ::first-line pseudo element, which does not help us here--in any case, it is limited to a few presentational attributes, and does not work together with things like ::before and ::after. The only other aspect of CSS I can think of that to some extent exposes line-breaking is hyphenation. However, hyphenating is all about adding a character (usually a dash) to the end of lines in certain situations, whereas here we are concerned about removing a character (the vertical line), so I just can't see how to apply any kind of hyphenation-related logic, even with the help of properties such as hyphenate-character.

We have the word-spacing property, which is applied intra-line but not at line beginnings and endings, which seems promising, but it defines the width of the space between words, not the character(s) to be used.

One wonders if there's some way to use the text-overflow property, which has the little-known ability to take two values for display of overflow text at both left and right, as in

text-overflow: '' '';

but there still doesn't seem to be any obvious way to get from A to B here.


This is possible with flex-box

The keys to this technique:

  • A container element set to overflow: hidden.
  • Set justify-content: space-between on the ul (which is a flex-box) to force its flex-items to stretch to the left and right edges.
  • Set margin-left: -1px on the ul to cause its left edge to overflow the container.
  • Set border-left: 1px on the li flex-items.

The container acts as a mask hiding the borders of any flex-items touching its left edge.

.flex-list {
    position: relative;
    margin: 1em;
    overflow: hidden;
}
.flex-list ul {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: space-between;
    margin-left: -1px;
}
.flex-list li {
    flex-grow: 1;
    flex-basis: auto;
    margin: .25em 0;
    padding: 0 1em;
    text-align: center;
    border-left: 1px solid #ccc;
    background-color: #fff;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css" rel="stylesheet"/>
<div class="flex-list">
    <ul>
        <li>Dogs</li>
        <li>Cats</li>
        <li>Lions</li>
        <li>Tigers</li>
        <li>Zebras</li>
        <li>Giraffes</li>
        <li>Bears</li>
        <li>Hippopotamuses</li>
        <li>Antelopes</li>
        <li>Unicorns</li>
        <li>Seagulls</li>
    </ul>
</div>

Before showing the code, it's worth mentioning that IE8 supports :first-child but not :last-child, so in similar situations, you should use the :first-child pseudo-class.

Demo

#menu {
  list-style: none;
}

#menu li {
  display: inline;
  padding: 0 10px;
  border-left: solid 1px black;
}

#menu li:first-child {
  border-left: none;
}
<ul id="menu">
  <li>Dogs</li>
  <li>Cats</li>
  <li>Lions</li>
  <li>More animals</li>
</ul>

Use :after pseudo selector. Look http://jsfiddle.net/A52T8/1/

<ul>
    <li>Dogs</li>
    <li>Cats</li>
    <li>Lions</li>
    <li>Tigers</li>
    <li>Zebras</li>
    <li>Giraffes</li>
    <li>Bears</li>
    <li>Hippopotamuses</li>
    <li>Antelopes</li>
    <li>Unicorns</li>
    <li>Seagulls</li>
</ul>

ul li { float: left; }
ul li:after { content: "|"; padding: 0 .5em; }

EDIT:

jQuery solution:

html:

<div>
    <ul id="animals">
        <li>Dogs</li>
        <li>Cats</li>
        <li>Lions</li>
        <li>Tigers</li>
        <li>Zebras</li>
        <li>Giraffes</li>
        <li>Bears</li>
        <li>Hippopotamuses</li>
        <li>Antelopes</li>
        <li>Unicorns</li>
        <li>Seagulls</li>
        <li>Monkey</li>
        <li>Hedgehog</li>
        <li>Chicken</li>
        <li>Rabbit</li>
        <li>Gorilla</li>
    </ul>
</div>

css:

div { width: 300px; }
ul li { float: left; border-right: 1px solid black; padding: 0 .5em; }
ul li:last-child { border: 0; }

jQuery

var maxWidth = 300, // Your div max-width
    totalWidth = 0;
$('#animals li').each(function(){
    var currentWidth = $(this).outerWidth(),
        nextWidth = $(this).next().outerWidth();
    totalWidth += currentWidth;
    if ( (totalWidth + nextWidth) > maxWidth ) {
        $(this).css('border', 'none');
        totalWidth = 0;
    }
});

Take a look here. I also added a few more animals. http://jsfiddle.net/A52T8/10/