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

HtmlCss

Html Problem Overview


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.

Html Solutions


Solution 1 - Html

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.

Solution 2 - Html

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>

Solution 3 - Html

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>

Solution 4 - Html

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/

Solution 5 - Html

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

Solution 6 - Html

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

Solution 7 - Html

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!

.pipe-separated-list-container {
  overflow-x: hidden;
}
.pipe-separated-list-container ul {
  list-style-type: none;
  position: relative;
  left: -1px;
  padding: 0;
}
.pipe-separated-list-container ul li {
  display: inline-block;
  line-height: 1;
  padding: 0 1em;
  margin-bottom: 1em;
  border-left: 1px solid;
}

<div class="pipe-separated-list-container">
  <ul>
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
    <li>Four</li>
    <li>Five</li>
    <li>Six</li>
    <li>Seven</li>
    <li>Eight</li>
    <li>Nine</li>
    <li>Ten</li>
    <li>Eleven</li>
    <li>Twelve</li>
    <li>Thirteen</li>
    <li>Fourteen</li>
    <li>Fifteen</li>
    <li>Sixteen</li>
    <li>Seventeen</li>
    <li>Eighteen</li>
    <li>Nineteen</li>
    <li>Twenty</li>
    <li>Twenty One</li>
    <li>Twenty Two</li>
    <li>Twenty Three</li>
    <li>Twenty Four</li>
    <li>Twenty Five</li>
    <li>Twenty Six</li>
    <li>Twenty Seven</li>
    <li>Twenty Eight</li>
    <li>Twenty Nine</li>
    <li>Thirty</li>
  </ul>
</div>

Solution 8 - Html

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>

Solution 9 - Html

This should solve the problem without using borders.

li {
  display: inline-block !important;
  list-style: none;
  margin: 0 auto;
  height: 14px;
  }
    ul li::after {
      content: " | ";
      margin: 0 10px;
    }
    ul li:last-child:after {
      content: '';
      margin: 0 10px;
    }

<div>
  <ul>
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
    <li>Four</li>
    <li>Five</li>
    <li>Six</li>
    <li>Seven</li>
    <li>Eight</li>
    <li>Nine</li>
    <li>Ten</li>
    <li>Eleven</li>
    <li>Twelve</li>
    <li>Thirteen</li>
    <li>Fourteen</li>
    <li>Fifteen</li>
    <li>Sixteen</li>
    <li>Seventeen</li>
    <li>Eighteen</li>
    <li>Nineteen</li>
    <li>Twenty</li>
    <li>Twenty One</li>
    <li>Twenty Two</li>
    <li>Twenty Three</li>
    <li>Twenty Four</li>
    <li>Twenty Five</li>
    <li>Twenty Six</li>
    <li>Twenty Seven</li>
    <li>Twenty Eight</li>
    <li>Twenty Nine</li>
    <li>Thirty</li>
  </ul>
</div>

Solution 10 - Html

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

Solution 11 - Html

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontwsaefView Question on Stackoverflow
Solution 1 - Htmluser663031View Answer on Stackoverflow
Solution 2 - HtmlgfullamView Answer on Stackoverflow
Solution 3 - HtmlNabil KadimiView Answer on Stackoverflow
Solution 4 - HtmlelclanrsView Answer on Stackoverflow
Solution 5 - Htmluser2020056View Answer on Stackoverflow
Solution 6 - HtmlgreimView Answer on Stackoverflow
Solution 7 - HtmlbrahnpView Answer on Stackoverflow
Solution 8 - HtmlUser_coderView Answer on Stackoverflow
Solution 9 - HtmlZiftmanView Answer on Stackoverflow
Solution 10 - HtmlkinakutaView Answer on Stackoverflow
Solution 11 - HtmlBijoy AnupamView Answer on Stackoverflow