场景

对于某一个页面内容繁多,
如果我们滚动的时间较长,为了增加用户体验。
我们需要实现点击某一个按钮,然后滚动到对应的区域。
滚动的时候,右侧对应的分类实现高亮
其实,这个功能就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. ODOO升级可能遇到问题

    a,找不到模块或视图不存在. 解决方案:查看是否将相应py文件加入到__init__文件中,或xml文件加入到manifest->data文件中. b, 找不到关联模块. 解决方案:这种一般要么 ...

  2. 2023年最新sentinel-dashbord部署安装(保姆级别)

    目录 Sentinel-dashboard安装下载 前景提要 一. 构建环境 二.下载安装与配置 1.进入百度搜索:Sentinel 或访问地址:面向云原生微服务的高可用流控防护组件 2.进入git主 ...

  3. Dubbo负载均衡策略之 一致性哈希

    本文主要讲解了一致性哈希算法的原理以及其存在的数据倾斜的问题,然后引出解决数据倾斜问题的方法,最后分析一致性哈希算法在Dubbo中的使用.通过这篇文章,可以了解到一致性哈希算法的原理以及这种算法存在的 ...

  4. Burp+Xray的联动使用

    Burp+Xray的联动使用 步骤如下, 1)首先,我们启动Xray的url监听功能,我们设置监听地址为127.0.0.1,端口为7777.监听的报告输出到xray文件夹根目录下的proxy_test ...

  5. IDEA中去除竖线

    IDEA中去除竖线 使用IDEA时突然多出了一条竖线 , 进入设置取消竖线

  6. [随笔]记一此更新win10后mysql服务消失的问题

    十几天前系统自动更新 没在意 几天前用php连mysql的时候 报错 Fatal error: Uncaught PDOException: SQLSTATE[HY000] [2002] 由于目标计算 ...

  7. k3s 基础 —— 配置 loki

    官方文档 核心组件 3 个 chart: promtail 这是一个 agent 代理客户端,用于收集日志,将日志传送给 loki loki 核心组件,主要功能是日志数据的写入与分析.包含 gatew ...

  8. 【C#/.NET】RESTful风格的Post请求与CreateAtAction

    ​  目录 引言 实现步骤 概念介绍 创建控制器 总结 引言 在构建Web应用程序时,遵循RESTful风格的API设计原则能够使我们的系统更加灵活.可扩展和易于维护.其中,Post请求在创建资源时起 ...

  9. ASP.NET MVC4 学习笔记-2

    渲染网页-Randering Web Pages 前面示例的输出结果不是HTML,而是一个"Hello World"的字符串.为了响应浏览器的请求产生一个HTML网页,我们需要创建 ...

  10. Map集合_HashMap_TreeMap_等_小记

    Map是一种依照键值对数据存储元素的容器. Map中的元素是两个对象,一个对象作为键,一个对象作为值.一个键(key)和它对应的值构成map集合中的一个元素.Map集合的数据结构只跟键有关,键不可以重 ...