要实现的效果是当点击某一个菜单的时候,菜单的底部有一个线条高亮,从上一次的位置切换到点击的位置,而且需要添加动画效果。
首先我们的DOM结构是这样的,li 是显示每一个菜单的内容,我们添加了自定义的属性,用来区分在点击的时候具体是点击第几个的位置。线条是通过定位在最底下的。
<div class="wrapper">
<ul class="list" id="list">
<li class="item" data-index="0">首页</li>
<li class="item" data-index="1">详情</li>
<li class="item" data-index="2">评价</li>
</ul>
<div class="line" id="line">
<span class="inner"></span>
</div>
</div>
在css上面,线条通过两个 div 控制,外层的宽度要和菜单的宽度保持一致,里面的宽度是具体线条显示的宽度。这样就能居中显示。
.wrapper {
position: relative;
}
.list {
display: flex;
}
.item {
flex: 1;
text-align: center;
line-height: 36px;
}
.line {
position: absolute;
text-align: center;
left: 0;
bottom: 0;
width: 0;
transform: translate3d(0, 0, 0);
transition: transform 0.3s ease-out;
display: none;
}
.inner {
width: 20px;
height: 2px;
background: red;
display: inline-block;
vertical-align: bottom;
}
在JS里面,我们先需要获取每个菜单的宽度和距离页面左边的距离,然后绑定点击事件,根据自定义属性我们知道点击的位置,然后重新设置线条的偏移位置。
const list = document.getElementsByClassName('item')
const line = document.getElementById('line')
const listWrapper = document.getElementById('list')
const resut = []
for (let i = 0; i < list.length; i++) {
const element = list[i]
resut.push({
width: element.offsetWidth,
left: element.offsetLeft
})
}
line.style.width = `${resut[0].width}px`
line.style.display = 'block'
listWrapper.onclick = function (e) {
const index = e.target.dataset.index
const width = resut[index].width
line.style.width = `${width}px`
const left = resut[index].left
line.style.transform = `translate3d(${left}px, 0, 0)`
}
设置了宽度是为了防止有些菜单宽度不一样,所以为了能居中,我们就需要点击的时候去重新设置一下。
经过扩展,我还写了一个标题滚动的项目:GitHub:链接,分别用JS和Vue写了。
文章介绍:链接