电梯导航也被称为锚点导航,当点击锚点元素时,页面内相应标记的元素滚动到视口。而且页面内元素滚动时相应锚点也会高亮。电梯导航一般把锚点放在左右两侧,类似电梯一样。常见的电梯导航效果如下,比如一些官方文档中:

之前可能会用 getBoundingClientRect() 判断元素是否在视口中来实现类似效果,但现在有更方便的方法了,那就是 IntersectionObserver + scrollIntoView,轻松实现电梯导航。

scrollIntoView() 介绍

scrollIntoView() 方法会滚动元素的父容器,使元素出现在可视区域。默认是立即滚动,没有动画效果。

如果要添加动画效果,可以这么做:

scrollIntoView({
behavior: 'smooth' // instant 为立即滚动
})

它还有两个可选参数 blockinline

block 表示元素出现在视口时垂直方向与父容器的对齐方式,inline 表示元素出现在视口时水平方向与父容器的对齐方式。

他们同样都有四个值可选 startcenterend 、nearest。默认为 start;

scrollIntoView({
behavior: 'smooth',
block:'center',
inline:'center',
})

对于 block

  • start  将元素的顶部和滚动容器的顶部对齐。

  • center  将元素的中心和滚动容器的中心垂直对齐。

  • end  将元素的底部和滚动容器的底部对齐。

对于 inline

  • start 将元素的左侧和滚动容器的左侧对齐。

  • center  将元素的中心和滚动容器的中心水平对齐。

  • end  将元素的右侧和容器的右侧对齐。

nearest 不论是垂直方向还是水平方向,只要出现在视口任务就完成了。可以理解为以最小移动量让元素出现在视口,(慵懒移动)。如果元素已经完全出现在视口中,则不会发生变化。

通过下面动图来感受这个变化,下面滚动容器中有四行五列,包含了从字母 AT。点击 出现在视口 的按钮会取三个下拉框的值作为参数来调用 scrollIntoView() 方法。

再来看看设置为 nearest 后的滚动情况

当字母 G 在视口内时,调用方法滚动容器不会发生变化。当 G 不完全在视口内,则会滚动到完全出现在视口内为止。

在这里可以查看这个完整例子 scrollIntoView 可选项参数实践(codepen)

而且 scrollIntoView 兼容性也很好

IntersectionObserver 介绍

Intersection Observer API(交叉观察器 API) 提供了一种异步检测目标元素与祖先元素或顶级文档的视口相交情况变化的方法。也就是能判断元素是否在视口中,并且能监听元素在视口中出现的可见部分的比例,从而可以执行我们自定义的逻辑。

由于是异步,也就不会阻塞主线程,性能自然比之前的频繁执行 getBoundingClientRect() 判断元素是否在视口中要好。

创建一个 IntersectionObserver

let options = {
root: document.querySelector(selector),
rootMargin: "0",
threshold: 1.0,
}; let observer = new IntersectionObserver(callback, options); let target = document.querySelector(selector);
observer.observe(target); //监听目标元素

通过调用 IntersectionObserver 构造函数可以创建一个交叉观察器,构造函数接收两个参数,一个回调函数和一个可选项。上面例子中,当元素完全出现(100%)在视口中时会调用回调函数。

可选项

  • root 用作视口的元素,必须是目标的祖先。默认为浏览器视口。

  • rootMargin 根周围的边距,也就是可以限制根元素检测视口的大小。值的方向大小和平常用的 margin 一样,例如 "10px 20px 30px 40px"(上、右、下、左)。只不过正数是增大根元素检测范围,负数是减小检测范围。

比如设置一个可以滚动的 div 容器为根元素,宽高都为1000px。 此时设置 rootMargin:0 表示根元素检测视口大小就是当前根元素可视区域大小,也就是 1000px * 1000px。设置 rootMargin:25% 0 25% 0 表示上下边距为 25%,那么检测视口大小就是 1000px * 500px。

  • threshold 一个数字或一个数字数组,表示目标出现在视口中达到多少百分比时,观察器的回调就应该执行。如果只想在能见度超过 50% 时检测,可以使用 0.5 的值。如果希望每次能见度超过 25% 时都执行回调,则需要指定数组 [0, 0.25, 0.5, 0.75, 1]。默认值为 0(这意味着只要有一个像素可见,回调就会运行)。值为 1.0 意味着在每个像素都可见之前,阈值不会被认为已通过。

回调函数

当目标元素匹配了可选项中的配置后,会触发我们定义的回调函数

let options = {
root: document.querySelector(selector),
rootMargin: "0",
threshold: 1.0,
}; let observer = new IntersectionObserver(function (entries) {
entries.forEach(entry => { })
}, options);

entries 表示被监听目标元素组成的数组,数组里面每个 entry 都有下列一些值

  • entry.boundingClientRect 返回目标元素的边界信息,值和 getBoundingClientRect() 形式一样。

  • entry.intersectionRatio 目标元素和根元素交叉的比例,也就是出现在检测区域的比例。

  • entry.intersectionRect 返回根和目标元素的相交区域的边界信息,值和 getBoundingClientRect() 形式一样。

  • entry.isIntersecting 返回true或者fasle,表示是否出现在根元素检测区域内

  • entry.rootBounds 返回根元素的边界信息,值和 getBoundingClientRect() 形式一样。

  • entry.target 返回出现在根元素检测区域内的目标元素

  • entry.time 返回从交叉观察器被创建到目标元素出现在检测区域内的时间戳

比如,要检测目标元素有75%出现在检测区域中就可以这样做:

entries.forEach(entry => {
if(entry.isIntersecting && entry.intersectionRatio>0.75){ }
})

监听目标元素

创建一个观察器后,对一个或多个目标元素进行观察。

let target = document.querySelector(selector);
observer.observe(target); document.querySelectorAll('div').forEach(el => {
observer.observe(el)
})

IntersectionObserver 的兼容性也很好:

掌握了 IntersectionObserver + scrollIntoView 的用法,实现电梯导航就简单了。

简单写一个电梯导航的 htmlcss

<div class="a" style="background:aqua;">第一章</div>
<div class="b" style="background: blueviolet;">第二章</div>
<div class="c" style="background: chartreuse;">第三章</div>
<div class="d" style="background: darkgoldenrod;">第四章</div>
<div class="e" style="background: firebrick;">第五章</div>
<div class="f" style="background: gold;">第六章</div>
<div class="g" style="background: hotpink;">第七章</div>
<ul class="rightBox">
<li class="aLi">第一章</li>
<li class="bLi">第二章</li>
<li class="cLi">第三章</li>
<li class="dLi">第四章</li>
<li class="eLi">第五章</li>
<li class="fLi">第六章</li>
<li class="gLi">第七章</li>
</ul>
html,
body {
width: 100%;
height: 100%;
background-color: #fff;
} ul,li{list-style: none;} body {
padding: 20px 0;
} div{
width: 60%;
height: 70%;
border-radius: 10px;
margin-left: auto;
margin-right: auto;
opacity: 0.4;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
font-weight: bold;
color: #000;
} div+div {
margin-top: 20px;
} .rightBox {
position: fixed;
right: 20px;
top: 50%;
color: teal;
transform: translatey(-50%);
} li {
cursor: pointer;
box-sizing: border-box;
border: 1px solid #fff;
border-radius: 4px;
padding: 8px 12px;
} li:hover {
background: #f5d2c4;
} .active {
background: #f5d2c4;
}

预览如下:


第一步:点击右边的导航菜单,利用 scrollIntoView 方法使内容区域对应的元素出现在可视区域中。

    let rightBox = document.querySelector('.rightBox')
rightBox.addEventListener('click', function (e) {
let target = e.target || e.srcElement;
if (target && !target.classList.contains('rightBox')) {
document.querySelector('.' + target.className.replace('Li', '')).scrollIntoView({
behavior: 'smooth',
block: 'center'
})
}
}, false)


第二步:页面容器滚动时,当目标元素出现在检测区域内则联动改变对应导航的样式。

这里 threshold 被设置为 1,也就是当目标元素完全显示在可视区域时执行回调,改变导航菜单的样式。

let observer = new IntersectionObserver(function (entries) {
entries.forEach(entry => {
let target = document.querySelector('.' + entry.target.className + 'Li')
if (entry.isIntersecting) { // 出现在检测区域内
document.querySelectorAll('li').forEach(el => {
if(el.classList.contains('active')){
el.classList.remove('active')
}
})
if (!target.classList.contains('active')) {
target.classList.add('active')
}
}
})
}, {
threshold: 1
}) document.querySelectorAll('div').forEach(el => {
observer.observe(el)
})

效果如下:


基本要求达到了,不过在滚动过程中,还有些问题。比如连续两个元素来回切换时,第二个元素比第一个元素在检测区域显示的比例更高,虽然没达到 100%,这时候导航菜单显示还是第一个元素的。见下图:


所以这里可以控制的更细,两个元素之间谁显示的比例更高时就高亮谁的导航菜单。

let observer = new IntersectionObserver(function (entries) {
entries.forEach(entry => {
if (entry.isIntersecting && entry.intersectionRatio > 0.65) { }
})
}, {
threshold: [0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]
})

这里设置了 threshold: [0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8],当目标元素出现在检测区域的比例达到 20%,30%,40%,50%,60%,70%,80% 的时候会执行回调函数,在回调函数里,目标元素可见并且在检测区域显示的比例达到 65% 时高亮导航菜单。这样效果就好些了:

在这里可以查看这个完整例子 IntersectionObserver + scrollIntoView 实现电梯导航

当然,具体还是看实际元素块大小和业务需求来定。

如有帮助,帮忙点点赞,感谢~

IntersectionObserver + scrollIntoView 实现电梯导航的更多相关文章

  1. jQuery实现电梯导航特效

    功能描述: 当滚动条滑到某个位置时,显示电梯导航: 当用户滚动滚动条时,让电梯导航的选中状态和当前滚动到的区域保持一致: 当用户点击电梯导航时,滚动条滚动到被点击导航对应的区域 准备工作: 首先将jQ ...

  2. JQuery电梯导航

    // .zjong .dag_id 内容区// .zuoyou .dao_hang a 电梯按钮 $(function() { $(".zjong .dag_id").each(( ...

  3. js基础第一天

    js作用:网页特效(电梯导航).交互.表单特效.就是可以用来控制结构和样式. 常用的三个输出语句都属于js的内置对象,提供我们直接使用的功能就是内置对象功能. web三标准:结构.样式.行为.而js主 ...

  4. 浮动【电梯】或【回到顶部】小插件:iElevator.js

    iElevator.js 是一个jquery小插件,使用简单,兼容IE6,支持UMD和3种配置方式,比锚点更灵活. Default Options _defaults = { floors: null ...

  5. jQuery/CSS3类似阿里巴巴的商品导航菜单实现教程

    有两天没发表文章了,今天来说说利用jQuery和CSS3制作一款类似阿里巴巴左侧商品菜单导航,这款菜单看起来非常大气,可以展示非常多的产品类目,如果你在设计电子商务网站,不妨可以拿来参考,一下是效果图 ...

  6. JS的scrollIntoView学习

    scrollIntoView(alignWithTop)  滚动浏览器窗口或容器元素,以便在当前视窗的可见范围看见当前元素.如果alignWithTop为true,或者省略它,窗口会尽可能滚动到自身顶 ...

  7. 微信小程序 - scroll-view的scroll-into-view属性 - 在页面打开后滚动到指定的项

    需求: 这是一个可横向滚动的导航条,现在要求我,从别的页面reLaunch回到首页这里,刷新页面内容的同时,菜单项要滚动出来 (如果该菜单项不在可视区域),而不是让他被挡住. 代码:<scrol ...

  8. vue2实现tabs侧边导航栏点击内容跳转到对应位置,且内容滚动导航栏切换对应tab

    vue2实现tabs侧边导航栏点击内容跳转到对应位置,且内容滚动导航栏切换对应tab 1.tabs使用了 element的插件tabs, 省的自己写滑动动画 2.左侧有用到了 element的插件 N ...

  9. “四核”驱动的“三维”导航 -- 淘宝新UI(需求分析篇)

    前言 孔子说:"软件是对客观世界的抽象". 首先声明,这里的"三维导航"和地图没一毛钱关系,"四核驱动"和硬件也没关系,而是为了复杂的应用而 ...

  10. ABP文档 - 导航

    文档目录 本节内容: 创建菜单 注册导航供应器 显示菜单 每个web应用都有一些菜单用来在页面/屏幕之间导航,ABP提供了一个通用的基础框架创建并显示菜单给用户. 创建菜单 一个应用可能由不同模块组成 ...

随机推荐

  1. Linux虚拟网卡TUN和TAP

    简介 在 Linux 网络管理中,虚拟网卡(Virtual Network Interface)是一个重要的概念,广泛应用于虚拟化.网络仿真和隧道技术中.本文将重点介绍 TUN(Network TUN ...

  2. Linux增加系统调用(亲测成功)

    我使用的操作系统是CentOS,其他的操作系统类似. 相关软件和Linux的基础操作这里不再赘述. 实验环境              VMWare Workstation.CentOS-7 实验步骤 ...

  3. 基于Python和TensorFlow实现BERT模型应用

    本文分享自华为云社区<使用Python实现深度学习模型:BERT模型教程>,作者: Echo_Wish. BERT(Bidirectional Encoder Representation ...

  4. logo3

  5. 【java深入学习第2章】Spring Boot 结合 Screw:高效生成数据库设计文档之道

    在开发过程中,数据库设计文档是非常重要的,它可以帮助开发者理解数据库结构,方便后续的维护和扩展.手动编写数据库设计文档不仅耗时,而且容易出错.幸运的是,可以使用Spring Boot和Screw来自动 ...

  6. TP3.2与TP5.0的区别

    1. 控制器输出return $this->fetch(); ----5$this->display(); ----3.2单字母函数去掉了 如:M() D() U() S() C() 3. ...

  7. PHP中引用的详解(引用计数、写时拷贝)

    转载:https://blog.csdn.net/ljguo212/article/details/8972865 1. PHP中引用的特性 PHP中引用意味着用不同的名字访问同一个变量内容,引用不是 ...

  8. char字符_C

    字符的表示  字符类型由单引号' '包围,字符串由双引号" "包围. //正确的写法 char a = '1'; char b = '$'; char c = 'X'; char ...

  9. Nginx 工作原理简介

    在了解Nginx工作原理之前,我们先来了解下几个基本的概念 以及常见的I/O模型. 基本概念 同步:就是指调用方发起一个调用,在没有得到调用结果之前,该调用不返回.换句话说,也就是调用方发起一个调用后 ...

  10. gitbook 入门教程之比较代码块差异 diff 插件

    在 markdown 文档中显示代码之间的差异的 Gitbook 插件 English | 中文 主页 Github : https://snowdreams1006.github.io/gitboo ...