CSS: Last element on line
Asked Answered
T

6

37

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.

Tibia answered 23/3, 2012 at 23:17 Comment(1)
Even with JavaScript, it'd be difficult to find out where exactly the list items wrap.Athalla
B
32

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.

Ballyrag answered 23/3, 2012 at 23:40 Comment(2)
Agreed: this is beautiful. Thank you much, Jon!Tibia
For responsive: function removeSeparatorAtEndOfLine() { var lastElement = false; // https://mcmap.net/q/219734/-css-last-element-on-line $(".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);Jaime
B
8

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

Barrett answered 26/2, 2016 at 21:6 Comment(1)
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.Shameful
I
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.

Indecisive answered 5/5, 2016 at 22:53 Comment(0)
L
1

Here's a JavaScript only version of the accepted answer (No jQuery needed!):

document.addEventListener("DOMContentLoaded", function(event) {
    var lastElement = false;
    var els = document.querySelectorAll('ul li');
    for(var i = 0; i < els.length; i++){
        var el = els[i]; // current element
        if (lastElement && lastElement.offsetTop !== el.offsetTop) {
            lastElement.className += " nobullet";
        }
        lastElement = el;
    }
    els[els.length - 1].className += " nobullet";
});

UPDATE

Here's another way if you need to keep track of actual positions of the elements in each line (group):

var i = 0;
var j = 0;
var keys = [];
var groupsList = [];
$("ul li").each(function() {
    var topOffset = $(this).offset().top;
    if(Array.isArray(groupsList[topOffset])) {
        groupsList[topOffset].push(i);
    } else {
        if(j > 0) {
            var lastElementIndexOnRow = groupsList[keys[j-1]][groupsList[keys[j-1]].length - 1];
            $($('ul li').get(lastElementIndexOnRow)).addClass('nobullet');
        }
        groupsList[topOffset] = [];
        groupsList[topOffset].push(i);
        keys.push(topOffset);
        j++;
    }
    i++;
}).last().addClass("nobullet");
Lesotho answered 15/8, 2017 at 2:18 Comment(0)
S
1

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>
Shameful answered 22/8, 2017 at 18:59 Comment(0)
F
-1

Based on the accepted answer in coffee:

do lastinline = ->
        $('[lastinline]').each ->
            $parent = $ @
            lastElement = false
            $parent.find('> *').each ->
                $child = $ @
                if lastElement && lastElement.offset().top != $child.offset().top
                    lastElement.addClass "last-in-line"
                lastElement = $child

            .last().addClass "last-in-line"

$(window).on 'resize', lastinline
Flabby answered 2/11, 2015 at 11:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.