1

I want to implement a list to which new items can be added with buttons. One button at the top of the list to prepend new items, another one at the bottom to append new items.

HTML:

<div>
    <div id="list">
      <button class="addbtn">prepend item</button>
      <ul id="items"></ul>
      <button class="addbtn">append item</button>
    </div>
</div>

The buttons are only visible when hovering the div containing the list (via css :hover selector).

CSS:

.addbtn {
  display: none;
}

#list:hover .addbtn {
  display: block;
}

Is there a way (without using JS, only with CSS) to only show the append button, but not the prepend button, when the list is empty? If the list contains at least one item, both buttons should be shown.

JSFiddle: https://jsfiddle.net/uLmzom18/

Edit: here's the JSFiddle with the working solution: https://jsfiddle.net/khu7bahm/

megagrump
  • 108
  • 8

3 Answers3

3

If you may use display:flex for your #list, you could reverse the order of the buttons and use the css sibling selector +

See this fiddle: https://jsfiddle.net/sb7zewp6/1/

If you need more info about this I can extend this answer, but I think this is only needed if display:flex is an option.

More info about display:flex usage at caniuse.com

$(".addbtn").click(function() {
  if($(this).hasClass("prepend"))
   $("#items").prepend($("<li>").text("prepended item " + $("#items").children().length));
 else
  $("#items").append($("<li>").text("appended item " + $("#items").children().length));
});
#list {
  width: 300px;
  min-height: 10em;
  background-color: #fff;
  display:flex;
}
.addbtn {
  display: none;
}

#items:empty+.addbtn {
  display:none !important;
}

#list:hover .addbtn {
  display: block;
}


.addbtn.append {
  order:3;
}
#items {
  order:2;
  outline:1px solid red;
}
.addbtn.prepend {
  order:1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
    <div id="list">
      <button class="addbtn append">append item</button>
      <ul id="items"></ul>
      <button class="addbtn prepend">prepend item</button>
    </div>
</div>
z00l
  • 878
  • 10
  • 21
  • Wow, but why is it appearing that way? I am still confused, how this works... Wow, so `:empty` is really a CSS selector? – Praveen Kumar Purushothaman Dec 25 '16 at 21:54
  • Yes, `:empty` is a valid css3 selector: http://caniuse.com/#search=empty – z00l Dec 25 '16 at 21:58
  • 1
    Good answer !!! You posted your answer before mine :-) I will keep mine because the way to reorder the flex elements is slighty different, and can be of utility. And may be better use inline-flex, the display will be more standard – vals Dec 25 '16 at 21:59
  • @vals Yes, I saw your answer just after I updated mine with the caniuse link ;-) Will upvote yours too, because the different way to reorder. Depends on the situation I think. – z00l Dec 25 '16 at 22:01
3

You can not influence with CSS an element that is upwards of the other in the DOM flow.

But you can trick it: inverse the order of the elements in the HTML, and rearrange them using flex

ul:empty ~ .addbtn {
  background-color: lightgreen;
}

#list, #list2 {
  display: inline-flex;
  flex-direction: column-reverse;
}
<div>
    <div id="list">
      <button class="addbtn">prepend item</button>
      <ul id="items"></ul>
      <button class="addbtn">append item</button>
    </div>
</div>
<div>
    <div id="list2">
      <button class="addbtn">prepend item</button>
      <ul id="items">
      <li>1</li>
      <li>2</li>
      </ul>
      <button class="addbtn">append item</button>
    </div>
</div>
vals
  • 54,758
  • 10
  • 75
  • 124
0

You cannot test the emptiness of an element using CSS as it doesn't have the possibility to traverse up. What you can do is, if you use jQuery, it is possible.

If you want a pure CSS based solution for your current HTML, then no!

However, if you are using some invalid HTML, it might work:

#items .addbtn:last-child {
  display: none;
}
<div>
  <div id="list">
    <ul id="items">
      <button class="addbtn">prepend item</button>
    </ul>
    <button class="addbtn">append item</button>
  </div>
</div>

Here, I have given the addbtn directly inside the <ul>, which is wrong. But if you are determined, we can make it better:

#items .btnAdd {
  margin-left: -40px;
  margin-bottom: 20px;
}
#items .btnAdd:last-child {
  display: none;
}
<p>No Prepend Button</p>
<div>
  <div id="list">
    <ul id="items">
      <li class="btnAdd"><button class="addbtn">prepend item</button></li>
    </ul>
    <button class="addbtn">append item</button>
  </div>
</div>
<hr />
<p>Prepend Button Shown</p>
<div>
  <div id="list">
    <ul id="items">
      <li class="btnAdd"><button class="addbtn">prepend item</button></li>
      <li>One item</li>
    </ul>
    <button class="addbtn">append item</button>
  </div>
</div>

The final snippet is a pure CSS based solution.

Praveen Kumar Purushothaman
  • 154,660
  • 22
  • 177
  • 226