If you only need to assign a classname to the selected element, rather than altering the DOM structure whenever a new item in the navbar is clicked, there is a simpler way to achieve this:
The following code snippet accomplishes the task:
let nav = document.getElementsByClassName('navbar')[0];
for (let item of document.getElementsByClassName('item')) {
item.addEventListener('click', () => {
let selected = nav.querySelector('.item.selected');
if (selected) selected.classList.remove('selected');
item.classList.add('selected');
});
}
body { font-family: monospace; }
.navbar {
position: absolute;
left: 0; top: 0;
width: 60px; height: 100%;
padding-left: 10px;
background-color: rgba(70, 0, 170);
overflow: hidden;
}
.item {
position: relative;
height: 60px; line-height: 60px;
color: #fff;
text-align: center;
z-index: 1;
cursor: pointer;
}
.item.selected {
background-color: #fff;
border-radius: 100%;
color: #000;
}
.item.selected::before,
.item.selected::after {
content: ' '; display: block;
position: absolute;
left: 0; width: 100%; height: 100%;
pointer-events: none;
border-radius: 100%;
border: 50px solid rgba(0, 0, 0, 0);
border-right: 50px solid #fff;
margin-left: -50px;
z-index: -1;
}
.item.selected::before { bottom: 100%; margin-bottom: -50px; transform: rotate(45deg); }
.item.selected::after { top: 100%; margin-top: -50px; transform: rotate(-45deg); }
<div class="navbar">
<div class="item">(1)</div>
<div class="item">(2)</div>
<div class="item selected">(3)</div>
<div class="item">(4)</div>
</div>
This code snippet is interactive, so feel free to click on the menu items!
Note that the design closely resembles the image provided, as we are utilizing true circle radii.
The key here is to add ::before
and ::after
pseudo-elements to the selected item, apply borders to them, eliminate all but one side of the border, and then rotate these elements to achieve the desired effect. The following code demonstrates this technique:
@keyframes styleBefore {
0% {
border: 0 solid #000;
margin-left: 0;
margin-bottom: 0;
}
20% {
border: 50px solid #000;
border-right: 50px solid #000;
margin-left: -50px;
margin-bottom: -50px;
}
40% {
border: 50px solid #000;
border-right: 50px solid #fff;
}
60% {
border: 50px solid transparent;
border-right: 50px solid #fff;
transform: rotate(0deg);
}
95% {
border: 50px solid transparent;
border-right: 50px solid #fff;
transform: rotate(45deg);
}
100% {
border: 50px solid transparent;
border-right: 50px solid transparent;
margin-left: -50px;
margin-bottom: -50px;
transform: rotate(45deg);
}
}
@keyframes styleAfter {
0% {
border: 0 solid #000;
margin-left: 0;
margin-top: 0;
}
20% {
border: 50px solid #000;
border-right: 50px solid #000;
margin-left: -50px;
margin-top: -50px;
}
40% {
border: 50px solid #000;
border-right: 50px solid #fff;
}
60% {
border: 50px solid transparent;
border-right: 50px solid #fff;
transform: rotate(0deg);
}
95% {
border: 50px solid transparent;
border-right: 50px solid #fff;
transform: rotate(-45deg);
}
100% {
border: 50px solid transparent;
border-right: 50px solid transparent;
margin-left: -50px;
margin-top: -50px;
transform: rotate(-45deg);
}
}
body { font-family: monospace; }
.navbar {
position: absolute;
left: 0; top: 0;
width: 90px; height: 100%;
padding-left: 10px;
background-color: rgba(70, 0, 170);
overflow: hidden;
}
.item {
position: relative; top: -85px;
height: 90px; line-height: 90px;
color: #fff;
text-align: center;
z-index: 1;
cursor: pointer;
font-size: 200%;
}
.item.selected {
background-color: #fff;
border-radius: 100%;
color: #000;
}
.item.selected::before,
.item.selected::after {
content: ' '; display: block;
position: absolute;
left: 0; width: 100%; height: 100%;
pointer-events: none;
border-radius: 100%;
z-index: -1;
}
.item.selected::before { bottom: 100%; animation: 10s infinite linear styleBefore; }
.item.selected::after { top: 100%; animation: 10s infinite linear styleAfter; }
<div class="navbar">
<div class="item">(1)</div>
<div class="item">(2)</div>
<div class="item selected">(3)</div>
<div class="item">(4)</div>
</div>