CSS: Last element on line

15,461

Solution 1

Interesting question! Here's what I consider a "low-tech" solution with jQuery that does the trick:

$(function() {
    var lastElement = false;
    $("ul > li").each(function() {
        if (lastElement && lastElement.offset().top != $(this).offset().top) {
            lastElement.addClass("nobullet");
        }
        lastElement = $(this);
    }).last().addClass("nobullet");
});​

The algorithm is not hard to follow, but here's the idea: iterate over the elements, taking advantage that they all have the same properties (size, margin, display inline etc) that affect their position when the browser computes layout. Compare the vertical offset of each item with that of the previous one; if they differ, the previous one must have been at the end of the line so mark it for special treatment. Finally, mark the very last item in the set for special treatment as well, since by definition it is the last item in the line on which it appears.

IMHO the fact that this is a Javascript-based solution (can it be done without, I wonder?) is no problem at all here, as even if the script does not run there is only going to be an extremely slight visual degradation of your webpage.

See it in action.

Solution 2

I am facing the same situation, and doing this in a responsive design. here is my CSS only solution.

div {
  overflow: hidden;
  margin: 1em;
}
div ul {
  list-style: none;
  padding: 0;
  margin-left: -4px;
}
div ul li {
  display: inline;
  white-space: nowrap;
}
div ul li:before {
  content: " \00b7";
}
<div>
  <ul>
    <li>item 1</li>
    <li>item B</li>
    <li>item 3</li>
    <li>item IV</li>
    <li>the long item</li>
    <li>item 6</li>
    <li>item 7</li>
    <li>item awesome</li>
    <li>give me your money</li>
    <li>contact</li>
    <li>menu item more</li>
    <li>CSS is awesome</li>
    <li>CSS3 is great</li>
    <li>more</li>
    <li>last item</li>

  </ul>
</div>

here it is as a fiddle too

Solution 3

I actually found a flaw in Jon's solution: In the rare situation where two (or more) lis would fit on a row, but the two lis plus the bullet wouldn't fit, they would render next to each other sans bullet. (Having the bullet there originally would cause the two lis to be on separate lines, applying the class nobullet and reducing the spacing, moving the second li up a line.)

Here's an example of the flaw: http://jsfiddle.net/W2ULx/61/ (Notice in the first line, the last two lis render with no bullet between them. The only thing changed from the original fiddle is the width of the ul to force the issue.)

The simple solution I found was to change in the CSS

ul li.nobullet:after { content: none; }

to

ul li.nobullet:after { color: transparent; }

so that instead of removing the spacing, just makes it invisible. Anything that would have broken to a new line still will break to a new line, instead of being bumped up a line by the removed bullet.

Solution 4

I wanted a version that would be visually centered and would also deal with the re-layout bug Randall elegantly handled. This solution also adds a middle dot and space to the first item on each line, which counter-balances the middle dot and space on the end of each line. You might wish to remove this if you left-align your menu.

I also switched to offset().left in the code because Firefox wasn't working with offset().top.

The code is below, but here is a link so you can more easily see it working. http://kpao.typepad.com/files/middle-dot-separated-list-v2.html

<html>
<head>
<title>Wildlife Conservation Network programs menu</title>
<style>
body, div, p, div, li, a {font-family:"Avenir Next","Segoe UI",sans-serif; font-size:15px;}
a {color:royalblue; text-decoration:none;}
a:hover, a:active {text-decoration:underline;}
ul.menu {background-color:#f2f2f2; text-align:center; padding:1em 0; margin:0;}
ul li {display:inline; white-space:nowrap;}
ul li::before {content:""; padding-right:3px;}
ul li::after {content:"\00a0\00b7"; padding-left:3px;}
ul li.firstdot::before, ul li:first-child::before {content:"\00b7\00a0"; color:transparent;}
ul li.lastdot::after,  ul li:last-child::after {color:transparent;}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
function midDotIt() {
    var lastElement = false;
    $("ul li").each(function() {
        if (lastElement && lastElement.offset().left > $(this).offset().left) {
            $(lastElement).addClass("lastdot");
            $(this).addClass("firstdot");
       } else if (lastElement) {
            $(lastElement).removeClass("lastdot");
            $(this).removeClass("firstdot");
       }
        lastElement = $(this);
   });
}
</script>
</head>
<body onload="midDotIt();" onresize="midDotIt();">

<ul class="menu">
<li><a href="https://wildnet.org/wildlife-programs/african-wild-dog">African Wild Dog</a></li>
<li><a href="https://wildnet.org/wildlife-programs/andean-cat">Andean Cat</a></li>
<li><a href="https://wildnet.org/wildlife-programs/cheetah-botswana">Cheetah - Botswana</a></li>
<li><a href="https://wildnet.org/wildlife-programs/cheetah-namibia">Cheetah - Namibia</a></li>
<li><a href="https://wildnet.org/wildlife-programs/cotton-top-tamarin">Cotton-Top Tamarin</a></li>
<li><a href="https://wildnet.org/wildlife-programs/elephant">Elephant</a></li>
<li><a href="https://wildnet.org/wildlife-programs/elephant-crisis-fund">Elephant Crisis Fund</a></li>
<li><a href="https://wildnet.org/wildlife-programs/ethiopian-wolf">Ethiopian Wolf</a></li>
<li><a href="https://wildnet.org/wildlife-programs/grevys-zebra">Grevy&#039;s Zebra</a></li>
<li><a href="https://wildnet.org/wildlife-programs/lion-ewaso">Lion - Ewaso</a></li>
<li><a href="https://wildnet.org/wildlife-programs/lion-niassa-0">Lion - Niassa</a></li>
<li><a href="https://wildnet.org/wildlife-programs/lion-recovery-fund">Lion Recovery Fund</a></li>
<li><a href="https://wildnet.org/wildlife-programs/okapi">Okapi</a></li>
<li><a href="https://wildnet.org/wildlife-programs/penguin">Penguin</a></li>
<li><a href="https://wildnet.org/wildlife-programs/saiga-antelope">Saiga Antelope</a></li>
<li><a href="https://wildnet.org/wildlife-programs/sharks-rays">Sharks and Rays</a></li>
<li><a href="https://wildnet.org/wildlife-programs/small-wild-cats">Small Wild Cats</a></li>
<li><a href="https://wildnet.org/wildlife-programs/snow-leopard">Snow Leopard</a></li>
<li><a href="https://wildnet.org/wildlife-programs/spectacled-bear">Spectacled Bear</a></li>
<li><a href="https://wildnet.org/what-we-do/scholarships">Scholarship Program</a></li>
</ul>
</body>
</html>
Share:
15,461
benesch
Author by

benesch

Updated on June 01, 2022

Comments

  • benesch
    benesch almost 2 years

    I've got an unordered [inline] list of links that wraps across two lines:
    links widget

    <ul>
        <li><a href="http://google.com">Harvard Medical School</a></li>
        <li><a href="http://google.com">Harvard College</a></li>
        ...
    </ul>
    

    I add the dot separator via a CSS pseudo-element:

    #widget-links li { display: inline; }
    #widget-links li:after { content: " \00b7"; }
    

    Unfortunately, the separator appears after the last element on each line. With just one line, I'd simply grab :last-child and remove the psuedo-element.

    Any nifty tricks to hide that last dot with more than one line? I'm open to weird CSS, or JavaScript if absolutely necessary.

  • benesch
    benesch about 12 years
    Agreed: this is beautiful. Thank you much, Jon!
  • mhenry1384
    mhenry1384 over 7 years
    For responsive: function removeSeparatorAtEndOfLine() { var lastElement = false; // stackoverflow.com/a/9847817/24267 $(".FiveNumbers .Columns .Column").each(function () { if (lastElement) { if (lastElement.offset().top !== $(this).offset().top) { lastElement.addClass("LastInLine"); } else { lastElement.removeClass("LastInLine"); } } lastElement = $(this); }).last().addClass("LastInLine"); } $(removeSeparatorAtEndOfLine); $(window).resize(removeSeparatorAtEndOfLine);
  • Dave C
    Dave C over 6 years
    It's definitely useful and clever, but unfortunately only works for left aligned lists. Also to make sure there is equal space between the dot, I recommend: div ul li:before {content: "\00b7\00a0";} This gives more wiggle room to the negative margin for fonts with different metrics. I used div ul {margin-left: -.4em;} which I found will work with most fonts.