实现一个Tab栏

1. 效果(移动端展示,PC端屏幕太宽,tab item数量少滚动不起来)

  • 默认选中第一个
    image
  • 点击话费直充,被激活的item会自动居中
    image

2. 原理

  • 这里我没有使用a标签href="#xxid"这种形式,因为浏览器地址会带上hash
  • 我使用了scrollIntoView,但是默认元素滚动到视口后,是贴边的,而且左右滚动的话,默认是向左滚动
  • 所以我又使用了scroll-margin来指定滚动后距离边界的margin
  • 注意这个scroll-margin需要计算一下,如果每个tab item的宽度是不同的话,宽度固定的话,写死就好了

3. 代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Scroll Demo</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        body {
            background-color: rgb(198 212 227);
            font-size: 15px;
        }
        .container {
            background-color: #ffffff;
            color: rgb(217, 130, 100);
            font-weight: 500;
            font-size: 1rem;
            line-height: 1.5rem;
            width: 100vw;
            overflow-x: hidden;
            --item-rem: 2; /* 默认用两个字的宽度 */
        }
        nav {
            display: flex;
            align-items: center;
            width: 100vw;
            overflow-x: auto;
            margin-bottom: 0.3rem;
            scroll-behavior: smooth; /* 平滑过渡 */
            transition: 0.3s; /* 过渡时间3s */
        }
        nav::-webkit-scrollbar {display: none;} /* 隐藏滚动条 */
        .item {
            margin: 0 2rem;
            flex-shrink: 0; /* 防止压缩成竖直排列的文本 */
            user-select: none; /* 禁止用户选中 */
            -webkit-user-select: none; /* 禁止用户选中,兼容safari3+ */
            cursor: pointer; /* 光标为pointer */
            scroll-margin: calc(50vw - calc(var(--item-rem) * 1px)); /* 滚动后自动居中 */
        }
        /* 底部小横条 */
        .item::after {
            content: " ";
            display: block;
            height: 0.2rem;
        }
        /* 小横条颜色 */
        .active::after {
            background-color: red;
        }
    </style>
</head>
<body>
    <div class="container">
        <nav>
        </nav>
    </div>
    <script>
        function handleClick(e) {
            if(e.target.tagName!=="SPAN") {
                return;
            }
            // 所有元素先取消类名
            const spans = document.getElementsByClassName('item');
            for(let i=0; i<spans.length; i++) {
                spans[i].classList.remove('active');
            }
            // 被点击元素添加类名
            e.target.classList.add('active');
            // 滚动到屏幕中间位置
            e.target.scrollIntoView();
        }

        (function() {
            const items = ['推荐', '爱心捐赠', '话费直充', '电子产品', '数码大清仓', '推荐', '爱心捐赠', '话费直充', '电子产品', '数码大清仓'];
            const nav = document.querySelector('nav');
            nav.addEventListener('click', handleClick);
            items.forEach((item, index)=>{
                const span = document.createElement('span');
                span.className = index===0 ? 'item active' : 'item';
                span.textContent = item;
                span.style.setProperty('--item-rem', item.length*7.5); // 计算自身宽度的一般,因为1rem: 15px
                nav.appendChild(span);
            });
        })()
    </script>
</body>
</html>
posted @ 2023-02-10 20:08  pangqianjin  阅读(39)  评论(0编辑  收藏  举报