[html] 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.

This question is related to html css

The answer is


Yes, you'll need to use pseudo elements AND pseudo selectors: http://jsfiddle.net/cYky9/


I came across a solution today that does not appear to be here already and which seems to work quite well so far. The accepted answer does not work as-is on IE10 but this one does. http://codepen.io/vithun/pen/yDsjf/ credit to the author of course!

_x000D_
_x000D_
.pipe-separated-list-container {_x000D_
  overflow-x: hidden;_x000D_
}_x000D_
.pipe-separated-list-container ul {_x000D_
  list-style-type: none;_x000D_
  position: relative;_x000D_
  left: -1px;_x000D_
  padding: 0;_x000D_
}_x000D_
.pipe-separated-list-container ul li {_x000D_
  display: inline-block;_x000D_
  line-height: 1;_x000D_
  padding: 0 1em;_x000D_
  margin-bottom: 1em;_x000D_
  border-left: 1px solid;_x000D_
}
_x000D_
<div class="pipe-separated-list-container">_x000D_
  <ul>_x000D_
    <li>One</li>_x000D_
    <li>Two</li>_x000D_
    <li>Three</li>_x000D_
    <li>Four</li>_x000D_
    <li>Five</li>_x000D_
    <li>Six</li>_x000D_
    <li>Seven</li>_x000D_
    <li>Eight</li>_x000D_
    <li>Nine</li>_x000D_
    <li>Ten</li>_x000D_
    <li>Eleven</li>_x000D_
    <li>Twelve</li>_x000D_
    <li>Thirteen</li>_x000D_
    <li>Fourteen</li>_x000D_
    <li>Fifteen</li>_x000D_
    <li>Sixteen</li>_x000D_
    <li>Seventeen</li>_x000D_
    <li>Eighteen</li>_x000D_
    <li>Nineteen</li>_x000D_
    <li>Twenty</li>_x000D_
    <li>Twenty One</li>_x000D_
    <li>Twenty Two</li>_x000D_
    <li>Twenty Three</li>_x000D_
    <li>Twenty Four</li>_x000D_
    <li>Twenty Five</li>_x000D_
    <li>Twenty Six</li>_x000D_
    <li>Twenty Seven</li>_x000D_
    <li>Twenty Eight</li>_x000D_
    <li>Twenty Nine</li>_x000D_
    <li>Thirty</li>_x000D_
  </ul>_x000D_
</div>
_x000D_
_x000D_
_x000D_


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/


I know I'm a bit late to the party, but if you can put up with having the lines left-justified, one hack is to put the pipes before the items and then put a mask over the left edge, basically like so:

li::before {
  content: " | ";
  white-space: nowrap;
}

ul, li {
  display: inline;
}

.mask {
  width:4px;
  position: absolute;
  top:8px; //position as needed
}

more complete example: http://jsbin.com/hoyaduxi/1/edit


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

_x000D_
_x000D_
#menu {
  list-style: none;
}

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

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


One solution is to style the left border like so:

li { display: inline; }
li + li {
  border-left: 1px solid;
  margin-left:.5em;
  padding-left:.5em;
}

However, this may not give you desirable results if the entire lists wraps, like it does in your example. I.e. it would give something like:

foo | bar | baz
 | bob | bill
 | judy

You can use the following CSS to solve.

ul li { float: left; }
ul li:before { content: "|"; padding: 0 .5em; }
ul li:first-child:before { content: ""; padding: 0; }

Should work on IE8+ as well.


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.


Slightly modified SCSS version which gives you control of the pipe | size and will eliminate padding from first and last list items while respects borders.


$pipe-list-height: 20px;
$pipe-list-padding: 15px;

.pipe-list {
    position: relative;
    overflow: hidden;
    height: $pipe-list-height;

    > ul {
        display: flex;
        flex-direction: row;

        > li {
            position: relative;
            padding: 0 $pipe-list-padding;

            &:after {
                content: " ";
                position: absolute;
                border-right: 1px solid gray;
                top: 10%;
                right: 0;
                height: 75%;
                margin-top: auto;
                margin-bottom: auto;
            }

            &:first-child {
                padding-left: 0;
            }

            &:last-child {
                padding-right: 0;

                &:after {
                    border-right: none;
                }
            }
        }
    }
}
<div class="pipe-list">
  <ul>
    <li>Link</li>
    <li>Link</li>
    <li>Link</li>
  </ul>
</div>