场景

对于某一个页面内容繁多,
如果我们滚动的时间较长,为了增加用户体验。
我们需要实现点击某一个按钮,然后滚动到对应的区域。
滚动的时候,右侧对应的分类实现高亮
其实,这个功能就2个步骤:
1.点击元素高亮,滚动到对应区域
2.滚动的时候,右侧导航的分类高亮

点击当前元素高亮的实现

1.我们利用事件委托的原理:给被点击子元素的父元素绑定点击事件
2.然后移除 li 元素的激活类
3.给当前被点击的子元素添加上激活类 事件委托也称为事件代理:就是利用事件冒泡,把子元素的事件都绑定到父元素上。
<style>
:root {
--h:931px;
}
*{
padding: 0;
margin: 0;
}
.demo1{
height:var(--h);
background-color: antiquewhite;
}
.demo2{
height:var(--h);
background-color: aqua;
}
.demo3{
height:var(--h);
background-color: blue;
}
.demo4{
height:var(--h);
background-color:chartreuse;
}
.fix-post{
position: fixed;
right: 50px;
bottom: 100px;
width: 200px;
}
li{
height: 40px;
line-height: 40px;
text-align: center;
list-style: none;
border-top: 1px solid #fff;
border-left: 1px solid #fff;
border-right: 1px solid #fff;
}
li:last-child{
border-bottom: 1px solid #fff;
}
.liactive{
background-color: cornsilk;
}
</style> <body>
<div>
<div class="demo1">1</div>
<div class="demo2">2</div>
<div class="demo3">3</div>
<div class="demo4">4</div>
</div>
<ul class="fix-post" id="nav">
<li class="liactive">1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</body>
<script>
let domNav= document.getElementById('nav')
let liList=document.querySelectorAll("#nav li")
console.log('liList', liList)
// 给父级元素注册点击事件(利用事件委托)
domNav.addEventListener('click',(event)=>{
for(let i=0;i<liList.length;i++){
// 移除所有元素的类名liactive
liList[i].classList.remove('liactive')
}
console.log('event.target', event.target)
// 给当前元素添加激活这个类名
event.target.classList.add('liactive');
})
</script>

点击右侧按钮,滚动到对应区域

1.给右侧的按钮添加上索引。
2.点击索引的时候 * 每个区域的高度
3.使用window.scrollTo({ })进行滚动
<script>
let domNav= document.getElementById('nav')
let liList=document.querySelectorAll("#nav li")
console.log('liList', liList)
// 给父级元素注册点击事件(利用事件委托)
domNav.addEventListener('click',(event)=>{
for(let i=0;i<liList.length;i++){
// 移除所有元素的类名liactive
liList[i].classList.remove('liactive')
// 给右侧的按钮添加上索引
liList[i]['index']=i
}
console.log('event.target', event.target.index)
// 给当前元素添加激活这个类名
event.target.classList.add('liactive'); // 点击按钮的时候,滚动到相应的区域
window.scrollTo({
top:event.target.index * 931,
behavior:"smooth" // 平滑的滚动
})
})
</script>

滑动到对应区域右侧按钮自动高亮

// 实现滚动的时候,右侧区域自动高亮
window.addEventListener('scroll',()=>{
// 兼容
let top = document.documentElement.scrollTop || document.body.scrollTop
// 获取当前区域的下标
let index = Math.floor(top/931)
console.log('top,index',top,index)
for(let i=0;i<liList.length;i++){
// 移除所有元素的类名liactive
liList[i].classList.remove('liactive')
}
// 给当前元素添加激活这个类名
liList[index].classList.add('liactive');
},false)

发现2个问题

问题1:出现这个问题的原因是:距离顶部的高度仅仅超过第一层区域的一点,
这个时候页面显示绝大部分区域是第二层的,
但是右侧按钮显示的是当前是第1层。
怎么解决这个问题?我们给当前区域手动新增一个高度。
这个高度一般为 3/10 问题2:我们每次滚动的时候,都在移除元素激活类,然后新增。
这样不太好,没有必须要。
我们需要判断一下

优化代码[每次滚动的时候都在移除元素的激活类]

// 实现滚动的时候,右侧区域自动高亮
let index=0
window.addEventListener('scroll',()=>{
console.log(222)
// 兼容
let top = (document.documentElement.scrollTop || document.body.scrollTop) + 279 //手动新增一个值
// 如果索引不变,则不取新增或者移除类名
if( Math.floor(top/931) != index){
// 获取当前区域的下标
let index = Math.floor(top/931)
for(let i=0;i<liList.length;i++){
// 移除所有元素的类名liactive
liList[i].classList.remove('liactive')
}
// 给当前元素添加激活这个类名
liList[index].classList.add('liactive');
}
},false)

scroll 事件不滚动也会触发

我们每次刷新页面的时候,滚动事件都会被触发。
因为:刷新的时候可视区域距离顶部有距离。所以滚动事件会被触发;【现象】
这样就会导致初始化(可视区域距离顶部有距离)刷新页面的时候。
右侧的指示灯会切换2次(第一次html上写激活类,第二次是由于有距离触发了滚动事件)。
这样不太好。我们需要优化一下:
删除html上的激活类。如果距离为0





处理右侧的指示灯在距离顶部有距离的时候,快速切换了2次

let index
let topValue = document.documentElement.scrollTop || document.body.scrollTop
// 距离为0.显示第一个指示灯
if(topValue==0){
liList[0].classList.add('liactive');
}

scroll 事件特别说明

在 iOS UIWebViews 中,
滚动进行时不会触发 scroll 事件;
只有当滚动结束后事件才会被触发。
参见 Bootstrap issue #16202。Safari 和 WKWebViews 则没有这个问题。
ps:刷新的时候可视区域距离顶部有距离。滚动事件也会被触发;不一定滚动才会触发

每个区域固定高度实现导航【全部代码】

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
:root {
--h:931px;
}
*{
padding: 0;
margin: 0;
}
.demo1{
height:var(--h);
background-color: antiquewhite;
}
.demo2{
height:var(--h);
background-color: aqua;
}
.demo3{
height:var(--h);
background-color: blue;
}
.demo4{
height:var(--h);
background-color:chartreuse;
}
.fix-post{
position: fixed;
right: 50px;
bottom: 100px;
width: 200px;
}
li{
height: 40px;
line-height: 40px;
text-align: center;
list-style: none;
border-top: 1px solid #fff;
border-left: 1px solid #fff;
border-right: 1px solid #fff;
}
li:last-child{
border-bottom: 1px solid #fff;
}
.liactive{
background-color: cornsilk;
}
</style>
</head>
<body>
<div>
<div class="demo1">1111</div>
<div class="demo2">2</div>
<div class="demo3">3</div>
<div class="demo4">4</div>
</div>
<ul class="fix-post" id="nav">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</body>
<script>
let domNav= document.getElementById('nav')
let liList=document.querySelectorAll("#nav li")
console.log('liList', liList)
// 给父级元素注册点击事件(利用事件委托)
domNav.addEventListener('click',(event)=>{
for(let i=0;i<liList.length;i++){
// 移除所有元素的类名liactive
liList[i].classList.remove('liactive')
// 给右侧的按钮添加上索引
liList[i]['index']=i
}
console.log('event.target', event.target.index)
// 给当前元素添加激活这个类名
event.target.classList.add('liactive'); // 点击按钮的时候,滚动到相应的区域
window.scrollTo({
top:event.target.index * 931,
behavior:"smooth" // 平滑的滚动
})
}) let index
let topValue = document.documentElement.scrollTop || document.body.scrollTop
// 离为0.显示第一个指示灯
if(topValue==0){
liList[0].classList.add('liactive');
}
// 实现滚动的时候,右侧区域自动高亮
window.addEventListener('scroll',()=>{
console.log('scroll-触发')
// 兼容
let top = (document.documentElement.scrollTop || document.body.scrollTop) + 279 //手动新增一个值
// 如果索引不变,则不取新增或者移除类名
if( Math.floor(top/931) != index){
// 获取当前区域的下标
index = Math.floor(top/931)
for(let i=0;i<liList.length;i++){
// 移除所有元素的类名liactive
liList[i].classList.remove('liactive')
}
// 给当前元素添加激活这个类名
liList[index].classList.add('liactive');
}
},false)
</script>
</html>

每个区域高度不一致怎么滚动到对应的区域

虽然我们的滚动可以正确显示右侧的高亮。
点击右侧区域也可以显示到对应的区域。
但是我们每个区域的高度是一致的。
在有些情况,每个区域的高度不一致,
怎么滚动到对应的区域,这个问题怎么处理呢?
我们可以判断当前区域在哪个区间。

<body>
<div id="cont">
<div class="demo1">1111</div>
<div class="demo2">2</div>
<div class="demo3">3</div>
<div class="demo4">4</div>
</div>
<ul class="fix-post" id="nav">
<li id="demo1">1</li>
<li id="demo2">2</li>
<li id="demo3">3</li>
<li id="demo4">4</li>
</ul>
</body>
<script>
let contDivList= document.querySelectorAll('#cont div')
let liList=document.querySelectorAll("#nav li")
liList.forEach(link =>{
// 给每个元素注册点击事件
link.addEventListener('click',(event)=>{
// 获取被点击元素的类名
let currentClickElement= event.target.getAttribute('id')
// 获取对应的区域元素dom
let currentTargetTop= document.querySelector('.' + currentClickElement)
// 获取当前这个点击元素的距离顶部的距离
let eleTop= currentTargetTop.offsetTop
// 点击按钮的时候,滚动到相应的区域
window.scrollTo({
top:eleTop,
behavior:"smooth" // 平滑的滚动
})
})
})
// 实现滚动的时候,右侧区域自动高亮
window.addEventListener('scroll',()=>{
let top = window.scrollTop || document.documentElement.scrollTop || document.body.scrollTop;
console.log('top', top)
contDivList.forEach(element => {
// 获取每个元素距离顶部的距离
const offsetTop = element.offsetTop;
// 获取每个元素的高度
const offsetHeight = element.offsetHeight;
// 判断当前内容区块是否在可视范围内
if (top >= offsetTop && top < offsetTop + offsetHeight) {
liList.forEach(function (link) {
if (link.getAttribute('id') === element.getAttribute('class')) {
link.classList.add('liactive');
} else {
link.classList.remove('liactive');
}
});
}
});
},false)
</script>

咋们这种判断方式有没有问题?

有的。
const offsetTop = element.offsetTop;
console.log('offsetTop', offsetTop)
// 获取每个元素的高度
const offsetHeight = element.offsetHeight;
// 判断当前内容区块是否在可视范围内
if (top >= offsetTop && top < offsetTop + offsetHeight) { }
这个判断是不准确的。容易出问题。
比如说:某一个区域的高度大于屏幕的可用区域并且下一个区域小于上一个区域的高度。
就可能出现问题。
下一楼层无法正确高亮(在滚动的时候)区域与高亮区域不匹配

全部代码:每个区域高度不确定导航

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
:root {
--h:931px;
}
*{
padding: 0;
margin: 0;
}
.demo1{
height: 800px;
background-color: antiquewhite;
}
.demo2{
height: 450px;
background-color: aqua;
}
.demo3{
height: 1200px;
background-color: blue;
}
.demo4{
height: 660px;
background-color:chartreuse;
}
.demo5{
height: 1000px;
background-color:rgb(33, 58, 7);
}
.fix-post{
position: fixed;
right: 50px;
bottom: 100px;
width: 200px;
}
li{
height: 40px;
line-height: 40px;
text-align: center;
list-style: none;
border-top: 1px solid #fff;
border-left: 1px solid #fff;
border-right: 1px solid #fff;
}
li:last-child{
border-bottom: 1px solid #fff;
}
.liactive{
background-color: cornsilk;
}
</style>
</head>
<body>
<div id="cont">
<div class="demo1">1111</div>
<div class="demo2">2</div>
<div class="demo3">3</div>
<div class="demo4">4</div>
<div class="demo5">5</div>
</div>
<ul class="fix-post" id="nav">
<li id="demo1">1</li>
<li id="demo2">2</li>
<li id="demo3">3</li>
<li id="demo4">4</li>
<li id="demo5">5</li>
</ul>
</body>
<script>
let contDivList= document.querySelectorAll('#cont div')
let liList=document.querySelectorAll("#nav li")
liList.forEach(link =>{
// 给每个元素注册点击事件
link.addEventListener('click',(event)=>{
// 获取被点击元素的类名
let currentClickElement= event.target.getAttribute('id')
// 获取对应的区域元素dom
let currentTargetTop= document.querySelector('.' + currentClickElement)
// 获取当前这个点击元素的距离顶部的距离
let eleTop= currentTargetTop.offsetTop
// 点击按钮的时候,滚动到相应的区域
window.scrollTo({
top:eleTop,
behavior:"smooth" // 平滑的滚动
})
})
}) // 实现滚动的时候,右侧区域自动高亮
window.addEventListener('scroll',()=>{
let top = window.scrollTop || document.documentElement.scrollTop || document.body.scrollTop;
console.log('top', top)
contDivList.forEach(element => {
// 获取每个元素距离顶部的距离
const offsetTop = element.offsetTop;
console.log('offsetTop', offsetTop)
// 获取每个元素的高度
const offsetHeight = element.offsetHeight;
// 判断当前内容区块是否在可视范围内
if (top >= offsetTop && top < offsetTop + offsetHeight) {
liList.forEach(function (link) {
if (link.getAttribute('id') === element.getAttribute('class')) {
link.classList.add('liactive');
} else {
link.classList.remove('liactive');
}
});
}
});
},false)
</script>
</html>

详细讲解js实现电梯导航的更多相关文章

  1. 简单详细讲解js闭包(看完不懂你砍我!!!)

    <javascript高级程序设计>中闭包的概念: 闭包,其实是一种语言特性,它是指的是程序设计语言中,允许将函数看作对象,然后能像在对象中的操作般在函数中定义实例(局部)变量,而这些变量 ...

  2. node+vue进阶【课程学习系统项目实战详细讲解】打通前后端全栈开发(1):创建项目,完成登录功能

    第一章 建议学习时间8小时·分两次学习      总项目预计10章 学习方式:详细阅读,并手动实现相关代码(如果没有node和vue基础,请学习前面的vue和node基础博客[共10章]) 视频教程地 ...

  3. head标签详细讲解

    head标签详细讲解 head位于html网页的头部,后前的标签,并以开始以结束的一html标签. Head标签位置如图: head标签示意图 head包含标签 meta,title,link,bas ...

  4. 详细讲解nodejs中使用socket的私聊的方式

    详细讲解nodejs中使用socket的私聊的方式 在上一次我使用nodejs+express+socketio+mysql搭建聊天室,这基本上就是从socket.io的官网上的一份教程式复制学习,然 ...

  5. jquery插件分类与编写详细讲解

    jquery插件分类与编写详细讲解 1. 插件种类 插件其实就是对现有的方法(或者叫函数)做一个封装,方便重用提高开发效率.   jQeury主要有2种类型   1)实例对象方法插件 开发能让所有的j ...

  6. jQuery实现电梯导航特效

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

  7. vue-cli 目录结构详细讲解

    https://juejin.im/post/5c3599386fb9a049db7351a8 vue-cli 目录结构详细讲解 目录 结构预览 ├─build // 保存一些webpack的初始化配 ...

  8. Promise入门到精通(初级篇)-附代码详细讲解

    Promise入门到精通(初级篇)-附代码详细讲解 ​     Promise,中文翻译为承诺,约定,契约,从字面意思来看,这应该是类似某种协议,规定了什么事件发生的条件和触发方法. ​     Pr ...

  9. 30 道 Vue 面试题,内含详细讲解(涵盖入门到精通,自测 Vue 掌握程度)

    前言 本文以前端面试官的角度出发,对 Vue 框架中一些重要的特性.框架的原理以问题的形式进行整理汇总,意在帮助作者及读者自测下 Vue 掌握的程度.本文章节结构以从易到难进行组织,建议读者按章节顺序 ...

  10. iOS KVC详细讲解

    iOS KVC详细讲解 什么是KVC? KVC即NSKeyValueCoding,就是键-值编码的意思.一个非正式的 Protocol,是一种间接访问对象的属性使用字符串来标识属性,而不是通过调用存取 ...

随机推荐

  1. 使用taro+canvas实现微信小程序的图片分享功能

    业务场景 二轮充电业务中,用户充电完成后在订单详情页展示订单相关信息,用户点击分享按钮唤起微信小程序分享菜单,将生成的图片海报分享给微信好友或者下载到本地,好友可通过扫描海报中的二维码加群领取优惠. ...

  2. 分布式协调服务之Zookeeper

    1. 认识Zookeeper ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用 ...

  3. ARM DMA Controller PL330 使用经验分享

    总体简介 DMAC提供一个AXI主接口来执行DMA传输,并提供两个APB从接口来控制其操作.DMAC采用TrustZone技术,其中一个APB接口运行在secure状态,另一个运行在非secure状态 ...

  4. EL表达式访问JavaBean

    前景提要 刚才有个朋友问我,赵大哥这个实验怎么做?我说哪个实验,给我发了几张截图.我一看,嗷,原来是今天,有个Java实验啊,他说大哥,能不能教教我,我说可以.我一说 他 啪的就站起来了, 很快啊 , ...

  5. switch条件语句2

    /*switch条件语句*/ #include<stdio.h> int main() { while (1) { int num; printf("请输入你的选择编号:\n&q ...

  6. 06、HSMS协议介绍

    本章的内容主要参考了 SECS半导体设备通讯-2 HSMS通信标准 ,外加上自己看的一些其他的文档.也加上了一些自己的理解,特此记录.若有侵权,请联系删除,谢谢. 再次特别感谢 SECS半导体设备通讯 ...

  7. Flash Download failed -“Cortex-M3”

    STM32下载程序时经常出现如下问题,即    error:Flash Download failed -"Cortex-M3" 经查找网络攻略,总结如下: (1). (2). ( ...

  8. uniapp主题切换功能的方式终结篇(全平台兼容)

    前面我已经给大家介绍了两种主题切换的方式,每种方式各有自己的优势与缺点,例如"scss变量+vuex"方式兼容好但不好维护与扩展,"scss变量+require" ...

  9. pyhton - parallel - programming - cookbook(THREAD)

    基于线程的并行 通常,一个应用有一个进程,分成多个独立的线程,并行运行.互相配合,执行不同类型的任务. 线程是独立的处理流程,可以和系统的其他线程并行或并发地执行.多线程可以共享数据和资源,利用所谓的 ...

  10. 【python基础】类-类属性

    在初始类中,我们介绍了如何访问类属性,除了访问类属性外还有其他操作类属性的情况,我们将在这里做详细介绍: 1.给类属性指定默认值 类中的每个属性都必须有初始值,哪怕这个值是0或者空字符串.在有些情况下 ...