Моя версия на React
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import './testMenu.css';
class testMenu extends Component {
constructor(props) {
super(props);
this.state = {
menu: [
{
title: 'Пункт 1',
active: true
},
{
title: 'Пункт 2',
active: false
},
{
title: 'Пункт 3',
active: false
},
{
title: 'Пункт 4',
active: false
},
{
title: 'Пункт 5',
active: false
}
]
};
this._menuItems = [];
this._menuHover = null;
this._menuBlock = null;
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
if (this.state.menu != nextState.menu) {
console.log('Should');
return true;
}
return false;
}
componentDidMount() {
this.getStyleMenuBlock()
}
componentDidUpdate() {
this.getStyleMenuBlock();
}
handlerClickItem = (indexClick) => {
let menuItems = this.state.menu.slice();
menuItems.map((item, indexItem) => {
if (indexItem == indexClick) {
item.active = true;
} else {
item.active = false;
}
});
this.setState({
menu: menuItems
})
};
getStyleMenuBlock = () => {
let menuItems = this.state.menu.slice(),
activeIndex = 0;
let leftMenuBlock,
leftMenuItem;
menuItems.map((item, index) => {
if (item.active) {
activeIndex = index;
}
});
leftMenuItem = ReactDOM.findDOMNode(this._menuItems[activeIndex]).getBoundingClientRect().left;
leftMenuBlock = ReactDOM.findDOMNode(this._menuBlock).getBoundingClientRect().left;
this._menuHover.style.transform = `translate3d(${ leftMenuItem - leftMenuBlock }px, 0, 0)`;
this._menuHover.style.width = ReactDOM.findDOMNode(this._menuItems[activeIndex]).getBoundingClientRect().width + 'px';
this._menuHover.style.height = ReactDOM.findDOMNode(this._menuItems[activeIndex]).getBoundingClientRect().height + 'px';
};
render() {
return (
<div className='test-menu'>
<ul className='test-menu__menu' ref={ref => { this._menuBlock = ref }}>
<React.Fragment>
{
this.state.menu.map((item, index) =>
<li
key={ index }
className={`test-menu__item ${ item.active ? 'test-menu__item_active' : '' }`}
onClick={ () => this.handlerClickItem(index) }
ref={ref => { this._menuItems[index] = ref; return true; }}
>{ item.title }</li>
)
}
<li
className='test-menu__hover'
ref={ref => { this._menuHover = ref }}
/>
</React.Fragment>
</ul>
</div>
)
}
}
export default testMenu;
.test-menu {
margin-top: 100px;
display: flex;
}
.test-menu__menu {
list-style-type: none;
position: relative;
display: flex;
padding: 0;
margin: 0;
margin-left: 100px;
margin-bottom: 100px;
box-sizing: border-box;
}
.test-menu__item {
padding: 5px 10px;
border: 1px solid black;
list-style-type: none;
margin: 0 0 0 10px;
}
.test-menu__item_active {
border: 1px solid red;
}
.test-menu__item:before {
display: none;
}
.test-menu__block:before {
display: none;
}
.test-menu__hover {
position: absolute;
left: 0;
top: 0;
width: 120px;
height: 2px;
background: red;
opacity: 0.3;
box-sizing: border-box;
-webkit-transition: -webkit-transform 0.5s, width 0.5s, height 0.5s;
transition: transform 0.5s, width 0.5s, height 0.5s;
-webkit-transition-timing-function: cubic-bezier(1, 0.01, 0, 1);
-webkit-transition-timing-function: cubic-bezier(1, 0.01, 0, 1.22);
transition-timing-function: cubic-bezier(1, 0.01, 0, 1.22);
}