3

I want to add bullet points between my flexbox navigation menu, but only have them appear between flex items on the same line. Not at the beginning or end of line, and not at all if the flex item is on the line by itself.

I've been using this (CSS styling for horizontal list with bullet only between elements) question as a starting point, but modified combined the top two answers to make it work with a flexbox.

Here's what I've got: http://codepen.io/anon/pen/EjQzWZ

JS:

<script>
$(function() {
    var lastElement = false;
    $("ul li").each(function(index) {
        if (lastElement && lastElement.offset().top == $(this).offset().top) {
            $(lastElement).after( $('<li>').attr('class', 'bullet') );
        }
        lastElement = $(this);
    });
});
</script>

CSS (essentially the exact same as the original question I linked to):

<style>
.bullet {
    width: 6px;
    height: 7px;
    background-position: 50% 50%;
    background-repeat: no-repeat;
    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAOElEQVQI113M0Q3AIBRC0aM76P7jmHSmSj/6mibyc4EQkEEWuYtDmU1SXO1d6H7tjgupIl8+P+cD22cff9U1wbsAAAAASUVORK5CYII=);
}
</style>

Most of the time everything renders correctly. But at certain browser widths, bullet points are added where they shouldn't be. For instance, if the window is re-sized to a width of 678 px (according to the codepen.io counter that displays in the middle of screen while re-sizing), a bullet point appears on the right side of the top row when it shouldn't. (Depending on the browser it may work correctly at 678px, but there are several spots that it does occur, and I've only tested this on Chrome and IE11).

Here's a picture of the bullet point being rendered incorrectly

Sometimes a refresh is required to fix the bullet points locations, which is understandable, but at some widths refreshing doesn't help and they reappear incorrectly.

I believe the problem is caused by the extra space the bullet points add.

I think it would be a simpler and cleaner solution to do it entirely via CSS instead of JS, but I'm not sure that's possible with flexbox items. Even though the flexbox items appear to take up a wider width than their content, the css selector ::after seems to only calculate the actual content width.

What is going on with my JS? Can I make it better/cleaner?

Community
  • 1
  • 1

4 Answers4

3

You can put your bullet logic in a function, then call that function on page load and attach it to the window resize event.

The updated javascript would look like this:

   $(function() {
       addBullets();

      $(window).resize(addBullets);
   });

    function addBullets(){
        $('li.bullet').remove(); //remove bullets before you add new ones

        var lastElement = false;
        $("ul li").each(function(index) {
            if (lastElement && lastElement.offset().top == $(this).offset().top) {
                      $(lastElement).after( $('<li>').attr('class', 'bullet') );
            }
            lastElement = $(this);
        });
    }

See this updated CodePen for a demo.

Also, I don't think this would be possible with pure CSS.

UPDATE

The reason the bullet was showing up on the edge in those certain widths is because the bullets them selves where <li>'s also and were taking up 6px a piece. So when you iterate through the <li>'s It would correctly identify the correct element to put the bullet after, but when the browser renders all the small bullets it would... at certain widths... shove the last <li> to the next line.

To correct this I have created a CSS class for the bullets and will add that to the items that need bullets instead of creating new <li>'s which would take up additional space.

New CSS:

.flex-item {
  position:relative; /*added*/
  flex: 1 1 auto;

    color: #a6a7a9;
    font-size:1.5em;

  padding: 50px; /*use padding instead of margin for easier absolute positioning of bullet*/

  color: black;
  text-align: center;
}

.flex-item.bullet:after{
  position:absolute;
  right:0;
  top:50%;
  transform:translateY(-50%);
  color:blue;
  content:"•"; /*could also be "url(data:image/png;base64 ..."*/
}

New Javascript:

function addBullets(){
  $('li.bullet').removeClass('bullet'); //remove all bullet classes

    var lastElement = false;
    $("ul li").each(function(index) {
        if (lastElement && lastElement.offset().top == $(this).offset().top) 
            $(lastElement).addClass('bullet'); //add class

        lastElement = $(this);
    });
}

See this updated CodePen.

If for whatever reason you can't use this CSS class approach you could add your bullets (as <img> or <div>) inside the <li> and position them absolutely with CSS. As long as they don't contribute to the overall width of the <ul> it should work fine.

zgood
  • 12,181
  • 2
  • 25
  • 26
  • Thanks for the response, and at some point I was going to get add the ability for it to rerun the function on any window resize, but the error still occurs. I added a picture to my OP to show what I'm talking about. – SmokeyTehBear Jul 04 '15 at 00:42
  • @SmokeyTehBear I have found out why this is happening and have found a solution. I will update my answer. – zgood Jul 04 '15 at 01:24
  • I've been struggling with that all day. Works perfect. Thanks. I had a feeling it had to do with the bullet li widths, but couldn't figure out how to do it in CSS. – SmokeyTehBear Jul 04 '15 at 02:20
  • I had a rather unique situation where I had a list of centered items, seprated with bullet points. It wasn't possible to use a pure CSS solution with a negative margin, so this method worked well – MrCarrot Dec 07 '19 at 08:57
2

You can do this using CSS only.

Delete your .bullet definition, and add this CSS:

.flex-container {
  margin-left: -10px;  //hide the first bullet on each row
}

.flex-item {
  position: relative;  //so we can have absolute positioning on the :before
}

.flex-item:before {
  content: "•";        //the bullet
  position: absolute;  //positioned absolutely
  left: -55px;         //account for the 50px margin and the bullet's width
}

Updated CodePen

Rick Hitchcock
  • 35,202
  • 5
  • 48
  • 79
  • I like that solution too, nice and clean using just CSS. The only problem I'm having is that my actual navigation list is nested in a flexbox layout, which for the desktop width is placed to the right of the logo. So a negative margin-left doesn't necessarily hide the first bullet point of every row. I'll try changing everything to use :after and a negative margin-right, might work for me. Thanks man – SmokeyTehBear Jul 04 '15 at 16:37
  • And in response to my first comment: Or just use overflow:hidden ;] – SmokeyTehBear Jul 04 '15 at 16:45
  • Yes, `overflow:hidden` on the flexbox's parent should do it. – Rick Hitchcock Jul 04 '15 at 17:03
  • 1
    This is a good solution, its a simple yet well defined layout. I would definitely recommend this solution over any that involved javascript. Clever display solution beats simple javascript soltuion lol. I think I didn't have a good understanding of the flex-item - it will always take full width of flex container. – zgood Jul 07 '15 at 02:42
1
div:before{
    content:"\b7\a0"; /* \b7 is a middot, \a0 is a space */
    font-size:30px;
}

Or try this

div:before{
    content:"•";
}
Nerdkowski
  • 445
  • 5
  • 16
  • 1
    I think the OP wants more than just adding a bullet with CSS. He needs to only add them inside the menu, not on the outside. You could use a CSS class for the bullet points, but you would still need to toggle that class via javascript on the right items depending on the screen size – zgood Jul 03 '15 at 23:35
  • Nerdkowski, I'll try something similar, but as I said in the OP, :after didn't space them evenly between the items like I'm looking for. I'm sorry if that wasn't clear. – SmokeyTehBear Jul 04 '15 at 00:33
0

You can use the :not(:first-child) selector to apply the bullet to all but the first child.

.flex-row
{
  display: flex;
  flex-direction: row;
  &.bulleted
  {
    div:not(:first-child):before{
      content: "•";
    }
  }
}
vanboom
  • 1,274
  • 12
  • 20