首页 前端知识 使用scrollIntoView和监听滚动区域子div的相对于视口的顶部的距离,来实现导航定位功能

使用scrollIntoView和监听滚动区域子div的相对于视口的顶部的距离,来实现导航定位功能

2024-04-29 12:04:36 前端知识 前端哥 851 820 我要收藏

一、需求分解

  • 1.滚动左侧内容,关联激活右侧导航节点

  • 2.单击右侧导航节点,右侧相应的段落滚动到可视区顶部

二、关键技术点提前知

技术点1:Element.scrollIntoView()

scrollIntoView()方法会滚动元素的父容器,使被调用scrollIntoView()的元素对用户可见。

(1).基本用法介绍:

var el = document.getElementById("p1");
// true 可以省略效果相同
el.scrollIntoView(true)

// alignToTop:Boolean型参数。
// 如果为 true,元素的顶端将和其所在滚动区的可视区域的顶端对齐。
// 如果为 false,元素的底端将和其所在滚动区的可视区域的底端对齐。
el.scrollIntoView(false);

// 可选,scrollIntoViewOptions:{behavior: "smooth", block: "end", inline: "nearest"}
// 可选,behavior :定义动画过渡效果,"auto"或 "smooth" 之一。默认为 "auto"。
// 可选,block:定义垂直方向的对齐,"start", "center", "end", 或 "nearest"之一。默认为 "start"。
// 可选,inline:定义水平方向的对齐,"start", "center", "end", 或 "nearest"之一。默认为 "nearest"。
el.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});
el.scrollIntoView({block: "end"});

(2).注意事项

普通的布局没问题,但是要注意,取决于其它元素的布局情况,此元素可能不会完全滚动到顶端或底端。比如整体上已经到顶部了,无法再滚动,那么该元素就不会移动到可视区的顶部。

技术点2:Element.getBoundingClientRect()

Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置。返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合,就是该元素的 CSS 边框大小。返回的结果是包含完整元素的最小矩形,并且拥有left, top, right, bottom, x, y, width, 和 height这几个以像素为单位的只读属性用于描述整个边框。除了width 和 height 以外的属性是相对于视图窗口的左上角来计算的。

三、实现

html:

<!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>Document</title>
</head>
<body>
    <div class="container">
        <nav>
            <!-- <a  class="current">导航1</a>
            <a >导航2</a>
            <a >导航3</a>
            <a >导航4</a>
            <a >导航5</a>
            <a >导航6</a>
            <a >导航7</a> -->
        </nav>
        <div class="wrapper">
            <!-- <div class="section" id="section1">section1</div>
            <div class="section" id="section2">section2</div>
            <div class="section" id="section3">section3</div>
            <div class="section" id="section4">section4</div>
            <div class="section" id="section5">section5</div>
            <div class="section" id="section6">section6</div>
            <div class="section" id="section7">section7</div> -->
         </div>
      </div>
</body>
<script src="./js/jquery.min.js"></script>
</html>

css:

<style>
         *{
            padding: 0px;
            margin: 0px;
        }
        .container{
            width: 100%;
            display: flex;
            background-color: rgb(255, 216, 222);
        }
 
        .container .wrapper{
            margin-left: 300px;
        }
        .container .wrapper .section{
            width:1560px;
            height: 500px;
            text-align: center;
            line-height: 500px;
            color: #fff;
            font-size: 28px;
            background: orange;
            margin-top: 20px;
        }
        .container .wrapper .section:nth-child(2n){
            background: yellowgreen;
        }
        .container .wrapper .section:nth-child(3n){
            background: plum;
        }
        .container nav{
            width: 300px;
            position: fixed;
            text-align: center;
        }
        .container nav div{
            display: block;
            color: rgb(143, 78, 143);
            font-size: 24px;
            text-decoration: none;
            margin-top: 20px;
        }
        .container nav .current{
            color: orange;
        }
        .container nav div:not(:first-child){
            margin-top: 50px;
        }
    </style>

JavaScript:

<script>
    let list =['section1','section2','section3','section4','section5']
    let activeNav=[]
    let activeNavIndex=0

    // 从内容数据list中,获取段落标题作为导航标题。并为导航节点增加href,以段落的id值作为href的值
    list.map((item,index)=>{
        activeNav.push({title: item, act: false, href: "#section" + (index+1)});
    })
    console.log(activeNav);

    //渲染导航
   function renderNav(){
    let dom=$('nav')
    let str=``
    activeNav.forEach((item,index)=>{
        str+=
        `
        <div class="${activeNavIndex==index? 'current':'' }" onclick='navToPosition(${JSON.stringify(item)},${JSON.stringify(index)})'>${item.title}</div>  
        `
    })
    dom.html(str)
   }
   renderNav()

    //渲染左侧滚动区域
    function renderContext(){
        let dom=$('.wrapper')
        let str=``
        list.forEach((item,index)=>{
            str+=
            `
            <div class="section" id="${'section'+(Number(index)+1)}">${item}</div>
            `
        })
        dom.html(str)
    }    
    renderContext()

    let timeout=null
    // 监听滚动条
    window.addEventListener("scroll", function (e) {
        // 防抖动处理
        clearTimeout(timeout)
        timeout = setTimeout(() => {
            activeNavNode(e)
        }, 10)
    });
    //点击导航右侧滚动区域定位
    function navToPosition(item,index){
        console.log(item,index);
        // 根据导航节点的href信息即id信息,获取对应的元素节点,通过 scrollIntoView 滚动该元素到可视区顶部
        document.querySelector(item.href)
        console.log(document.querySelector(item.href));
        document.querySelector(item.href).scrollIntoView(true)
    }
    
    $('nav div').on('click',function(){
        $(this).addClass('current')
        $(this).siblings().removeClass('current')
    })

    //通过上面的监听滚动,判断每个div距离视口顶部激活左侧对应的导航条
    function activeNavNode(e) {
    const nodes = document.getElementsByClassName('section')
    for (let i = 0; i < nodes.length; i++) {
        let node = nodes[i];
        // 获取该元素此时相对于视口的顶部的距离,即是元素顶部距离视口屏幕上边的距离
        let nodeY = node.getBoundingClientRect().y
        // 当元素距离视口顶部的距离在 [0,200] 之间,设置激活该元素对应左侧的导航标题,这个数字可以按需定义
        // 这里关联内容和导航标签,是巧妙利用了内容在元素集合中的索引序号和导航标签中的一致
        // 即是 list 和 activities 和 nodes 中下标相等的元素,具有对应关联的关系
        if (nodeY <= 200 && nodeY >= 0) {
            // console.log($('nav> div')[i]);
        //    console.log(document.querySelectorAll('nav > div')[i]);
        //document.querySelectorAll('nav > div')[i].setAttribute('class','current')
           $('nav> div').each(function(index,el){
            if(index==i){
                console.log(index,el);
                $(el).addClass('current')
                $(el).siblings().removeClass('current')
            }
           })
        return
        }
    }
    }

</script>

转载请注明出处或者链接地址:https://www.qianduange.cn//article/6426.html
标签
评论
发布的文章

HTML5本地存储

2024-05-06 09:05:10

HTML5和CSS3新特性

2024-04-16 17:04:36

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!