Ziv Weissman Ziv Weissman - 4 months ago 29
CSS Question

Using pure CSS to slide an arrow under a menu

When I am hovering on a menu, I'd like an arrow will slide from left to the menu item I'm currently hovering on.

In JavaScript I'd bind an event to hover and play with the arrow element's "left" property, but I wonder if I can have a pure css3 animated solution.

The arrow is similar to this thread-
Show border triangle in navbar using CSS

But I don't want it to appear and hide- I want the arrow always visible and that it will slide right to left depending on the menu I'm currently hovering on.

This is how it would look if I'd done it in JS:



var arrEle = $("#indication-mark-wrap");
var startHover = $(arrEle).css("left");
$("li").mouseenter(function() {
var left = $(this).position().left;
$(arrEle).css("left", left+30);
});

$("li").mouseleave(function() {
$(arrEle).css("left", startHover);
});

ul {
list-style-type: none;
padding: 0px;
}

li {
display: inline-block;
width: 80px;
color: white;
background-color: black;
height: 50px;
}

#indication-mark-wrap {
position: absolute;
left: 38px;
top: 56px;
background: wheat;
-webkit-transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
-moz-transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
-o-transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
/* custom */
-webkit-transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
-moz-transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
-o-transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
/* custom */
}

.indication-mark {
position: relative;
display: block;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="menu">
<ul>
<li>Selection1</li>
<li>Selection2</li>
<li>Selection2</li>
</ul>
</div>

<div id="indication-mark-wrap">
<canvas width="20" height="20" class="indication-mark"></canvas>
</div>





Sample JSFiddle

Answer

With a change in your markup, you can do like this.

$(function(){
$("li").click(function(){
  $(".active").removeClass("active");
  $(this).addClass("active");
})
});
ul {
  list-style-type: none;
  padding: 0px;
}

li {
  background-color: black;
}

li:nth-child(-n+3) {
  display: inline-block;
  width: 80px;
  color: white;
  height: 50px;
}

#indication-mark-wrap {
  position: absolute;
  left: 35px;
  top: 56px;
  background: wheat;
  -webkit-transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
  -moz-transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
  -o-transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
  transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
  /* custom */
  -webkit-transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
  -moz-transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
  -o-transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
  transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
  /* custom */
}

.indication-mark {
  position: relative;
  display: block;
}

li:nth-child(2):hover ~ li #indication-mark-wrap {
  left: 118px;
}
li:nth-child(3):hover ~ li #indication-mark-wrap {
  left: 200px;
}
 
li:nth-child(1).active ~ li #indication-mark-wrap {
  left: 35px;
}

li:nth-child(2).active ~ li #indication-mark-wrap {
  left: 118px;
}
li:nth-child(3).active ~ li #indication-mark-wrap {
  left: 200px;
}

#indication-mark-wrap:before,
#indication-mark-wrap:after {
  content: "";
  position: absolute;
  top: 15px;
  width: 0;
  height 0;
  border-left: 15px solid transparent;
  border-right: 15px solid transparent;
  border-bottom: 15px solid white;
}
#indication-mark-wrap:before {
  top: 14px;
  border-bottom-color: black;
}
.active {
  background-color: darkcyan;
}
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="menu">
  <ul>
    <li class="active">Selection1</li>
    <li>Selection2</li>
    <li>Selection2</li>
    <li class="test"><div id="indication-mark-wrap"></div></li>
  </ul>
</div>

CSS only

ul {
  list-style-type: none;
  padding: 0px;
}
li {
  background-color: black;
}
li:nth-child(-n+3) {
  display: inline-block;
  width: 80px;
  color: white;
  height: 50px;
}
li:nth-child(-n+3) label {
  display: inline-block;
  width: 100%;
  height: 100%;
}

#indication-mark-wrap {
  position: absolute;
  left: 35px;
  top: 56px;
  background: wheat;
  -webkit-transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
  -moz-transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
  -o-transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
  transition: all 1000ms cubic-bezier(0.000, 0.000, 0.330, 0.990);
  /* custom */
  -webkit-transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
  -moz-transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
  -o-transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
  transition-timing-function: cubic-bezier(0.000, 0.000, 0.330, 0.990);
  /* custom */
}

.indication-mark {
  position: relative;
  display: block;
}

#s1, #s2, #s3 {
  display: none;
} 
#s1:checked ~ ul #indication-mark-wrap {
  left: 35px;
}
#s2:checked ~ ul #indication-mark-wrap {
  left: 118px;
}
#s3:checked ~ ul #indication-mark-wrap {
  left: 200px;
}
li:nth-child(-n+3):hover {
  background: darkgray;
}
#s1:checked ~ ul li:nth-child(1),
#s2:checked ~ ul li:nth-child(2),
#s3:checked ~ ul li:nth-child(3) {
  background-color: darkcyan;
}

#indication-mark-wrap:before,
#indication-mark-wrap:after {
  content: "";
  position: absolute;
  top: 15px;
  width: 0;
  height 0;
  border-left: 15px solid transparent;
  border-right: 15px solid transparent;
  border-bottom: 15px solid white;
}
#indication-mark-wrap:before {
  top: 14px;
  border-bottom-color: black;
}
<div class="menu">
  <input type="radio" id="s1" name="s123" checked="checked">
  <input type="radio" id="s2" name="s123">
  <input type="radio" id="s3" name="s123">
  <ul>
    <li><label for="s1">Selection1</label></li>
    <li><label for="s2">Selection2</label></li>
    <li><label for="s3">Selection3</label></li>
    <li class="test"><div id="indication-mark-wrap"></div></li>
  </ul>
</div>

Comments