场景

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

    render() 作用:render是渲染变量(结合一个给定的模板和一个给定的上下文字典)在模板中,通俗点将context的内容,加载进模板中定义的文件,通过浏览器渲染呈现. render()方法常用 ...

  2. 代码随想录算法训练营Day39 动态规划

    代码随想录算法训练营 代码随想录算法训练营Day38 动态规划|62.不同路径 63. 不同路径 II 62.不同路径 题目链接:62.不同路径 一个机器人位于一个 m x n 网格的左上角 (起始点 ...

  3. 代码随想录算法训练营Day18 二叉树

    代码随想录算法训练营 代码随想录算法训练营Day18 二叉树| 513.找树左下角的值 112. 路径总和 113.路径总和ii 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构 ...

  4. 2023-05-29:给你一个由 n 个正整数组成的数组 nums 你可以对数组的任意元素执行任意次数的两类操作 如果元素是 偶数 ,除以 2 例如,如果数组是 [1,2,3,4] 那么你可以对最后一

    七.设计算法,仅使用三次实数乘法即可完成复数 a+bi和c+di 相乘.算法需接收a.b.c和d 为输入,分别生成实部 ac-bd 和虚部ad+bc. 文心一言: 可以使用如下算法来计算复数 a+bi ...

  5. 一步步教你如何搭建K8S集群

    一.环境配置 三台CentOS7虚拟机,默认配置,内存2GB.处理器2核心. 先更新下系统 1 sudo yum update 2 sudo yum upgrade 二.安装并启动 docker 1 ...

  6. 让你的 conda “回滚”到以前版本的环境

    我现在使用 Anaconda 作为我的主要 Python 发行版,同样,我们公司也将它用于所有开发人员机器以及他们的服务器.然而,前几天我在浏览一些论坛技术文章时遇到了一个我以前从未知道的 conda ...

  7. 前端八股文everybody准备好了没

    引言 由于最近比较忙活没时间学习新东西,现在得空想着能不能好好整理出一些有用的东西,让记忆深刻一点,免得到时候实习找工作面试的时候一问三不知,也希望大家能指正出错误和对大家有点帮助,一起进步,加油奥里 ...

  8. Dapr v1.11 版本已发布

    Dapr是一套开源.可移植的事件驱动型运行时,允许开发人员轻松立足云端与边缘位置运行弹性.微服务.无状态以及有状态等应用程序类型.Dapr能够确保开发人员专注于编写业务逻辑,而不必分神于解决分布式系统 ...

  9. 【Netty】02-入门

    二. Netty 入门 1. 概述 1.1 Netty 是什么? Netty is an asynchronous event-driven network application framework ...

  10. kafka学习之三_信创CPU下单节点kafka性能测试验证

    kafka学习之三_信创CPU下单节点kafka性能测试验证 背景 前面学习了 3controller+5broker 的集群部署模式. 晚上想着能够验证一下国产机器的性能. 但是国产机器上面的设备有 ...