【前端实战】如何让用户回到上次阅读的位置?

news2025/7/30 9:35:34

目录

【前端实战】如何让用户回到上次阅读的位置?

一、总体思路

1、核心目标

2、涉及到的技术

二、实现方案详解

1、基础方法:监听滚动,记录 scrollTop(不推荐)

2、Intersection Observer + 插入探针元素

3、基于 URL Hash 锚点跳转

三、总结

1、不同方案间对比总结

2、结语


        作者:watermelo37

        CSDN万粉博主、华为云云享专家、阿里云专家博主、腾讯云、支付宝合作作者,全平台博客昵称watermelo37。

        一个假装是giser的coder,做不只专注于业务逻辑的前端工程师,Java、Docker、Python、LLM均有涉猎。

---------------------------------------------------------------------

温柔地对待温柔的人,包容的三观就是最大的温柔。

---------------------------------------------------------------------

【前端实战】如何让用户回到上次阅读的位置?

        在阅读类、资讯类、博客类网站中,记忆用户上次阅读到的位置,并在下次访问时自动滚动回那个位置,可以大大提升用户体验感。

        今天我们就来详细讲一讲:前端如何实现用户回到上次阅读的位置,包括基础scroll方法+优化、 Intersection Observer API + 探针追踪、锚点 URL HASH 定位跳转等策略实现一个流畅且高效的方案。

一、总体思路

1、核心目标

        在用户滚动时记录当前位置

        在页面重新加载时恢复到记录的位置

2、涉及到的技术

        可以根据下列技术进行知识补充,或者直接选择自己已经熟悉的技术来实现:

  • scroll 事件监听

  • localStorage 本地存储

  • requestAnimationFrame 节流优化

  • Intersection Observer API 观察元素进入视口

  • Vue3 响应式组件(如果需要框架版本)

二、实现方案详解

1、基础方法:监听滚动,记录 scrollTop(不推荐)

        在用户滚动时,实时记录 window.scrollY(页面滚动的垂直距离),保存到 localStorage 中。页面加载时,从 localStorage 读取,并 scrollTo 恢复。

        这可能是很多人的第一直觉,但是这种方法存在一个问题,就是scroll事件触发太频繁了。高频滚动下每秒触发一百多次都是非常正常的情况,常规的节流方法也不合适,比如添加一个节流时间。因为滚动可以是一个非常快速的过程,一秒钟可能可以操作滚动条从顶到尾。所以这里选择使用 requestAnimationFrame 方法来节流。

// 用于保存最新滚动位置
let lastKnownScrollY = 0;
// 用于控制 requestAnimationFrame
let ticking = false;

// 监听滚动事件
window.addEventListener('scroll', () => {
  lastKnownScrollY = window.scrollY;

  // 防止过度频繁存储,使用requestAnimationFrame节流
  if (!ticking) {
    window.requestAnimationFrame(() => {
      // 将滚动位置保存在localStorage中
      localStorage.setItem('scrollPosition', lastKnownScrollY);
      ticking = false;
    });
    ticking = true;
  }
});

// 页面加载时,恢复之前保存的位置
window.addEventListener('DOMContentLoaded', () => {
  const savedPosition = localStorage.getItem('scrollPosition');
  if (savedPosition !== null) {
    window.scrollTo(0, parseInt(savedPosition));
  }
});

        requestAnimationFrame 是浏览器提供的用于执行高效动画的 API,它会在下一次重绘前调用指定的回调函数,确保动画与屏幕刷新率同步(通常为 60Hz),从而实现平滑、流畅的视觉效果,同时避免不必要的性能开销。

        它的执行频率比 scroll 要小一些,同时不滚动的时候也不会触发,相比与 scroll 的高频触发,起到了一个节流效果。

2、Intersection Observer + 插入探针元素

        Intersection Observer在确定页面位置的时候有奇效,效率比scroll事件监听高了不止一星半点,但是如果存在大块、不宜分割或者杂乱的元素,那么监听元素的选择就会成为一个问题。

        添加探针元素可以有效解决这个问题,探针元素只需要小小一个 div,可以设置为 visibility: hidden,不影响页面布局。他们就像一个个哨兵,负责观察你的视口到了什么位置。

        (1)页面插入探针元素

        可以在重要段落、章节、标题前插入隐形的小 div。

<article>
  <div id="section-1" class="observer-marker"></div>
  <h2>第一章 标题</h2>
  <p>正文内容...</p>

  <div id="section-2" class="observer-marker"></div>
  <h2>第二章 标题</h2>
  <p>正文内容...</p>

  <!-- 更多内容 -->
</article>

        (2)设置 Intersection Observer,并在页面加载时,滚动到探针位置

// 创建 IntersectionObserver 实例
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      // 如果探针元素进入可视区,记录它的id
      localStorage.setItem('lastVisibleSectionId', entry.target.id);
    }
  });
}, {
  threshold: 0.5 // 元素至少50%可见时触发
});

// 监听所有探针元素
document.querySelectorAll('.observer-marker').forEach(marker => {
  observer.observe(marker);
});

// 页面加载时,恢复到上次记录的探针
window.addEventListener('DOMContentLoaded', () => {
  const lastId = localStorage.getItem('lastVisibleSectionId');
  if (lastId) {
    const element = document.getElementById(lastId);
    if (element) {
      element.scrollIntoView({ behavior: 'smooth' }); // 平滑滚动到探针
    }
  }
});

3、基于 URL Hash 锚点跳转

        给每一节内容设置唯一 id,用户阅读到某个位置时,自动更新 URL 的 hash(锚点 #id),页面加载时,浏览器根据 hash 自动滚动到对应位置。这种方式实现的跳转甚至可以实现分享,因为位置信息是保存在 URL 里面的。

        是不是很熟悉?CSDN的目录跳转就是这么实现的。

// 监听页面滚动,动态更新 URL Hash
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      // 动态替换地址栏 hash,不刷新页面
      history.replaceState(null, '', `#${entry.target.id}`);
    }
  });
}, {
  threshold: 0.5
});

// 监听所有需要作为锚点的元素
document.querySelectorAll('.observer-marker').forEach(marker => {
  observer.observe(marker);
});

// 页面刷新后,浏览器会自动滚动到hash对应的元素

三、总结

1、不同方案间对比总结

方法优点缺点适用场景

scrollTop 记录

通用、简单粗糙、动态内容页面误差大小型项目、静态页面
Intersection Observer 探针精准、性能好要布置探针,稍复杂长内容、章节型页面
URL Hash 锚点轻便、天然支持浏览器跳转地址栏变化,需考虑SEO文章分享、文档导航

2、结语

        实现回到上次阅读位置,并不只有一种方式,关键是根据你的项目特点选择:

  • 内容简单 ➔ scrollTop 就够了。

  • 内容结构清晰 ➔ Intersection Observer是最佳。

  • 需要分享/跳转 ➔ 用 URL Hash 最自然。

        总之,真正优秀的细节体验,源自对用户行为的深刻理解和用心打磨。

        只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

        其他热门文章,请关注:

        极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图

        你真的会使用Vue3的onMounted钩子函数吗?Vue3中onMounted的用法详解

        DeepSeek:全栈开发者视角下的AI革命者

        通过array.filter()实现数组的数据筛选、数据清洗和链式调用

        通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能

        TreeSize:免费的磁盘清理与管理神器,解决C盘爆满的燃眉之急

        通过MongoDB Atlas 实现语义搜索与 RAG——迈向AI的搜索机制

        深入理解 JavaScript 中的 Array.find() 方法:原理、性能优势与实用案例详解

        el-table实现动态数据的实时排序,一篇文章讲清楚elementui的表格排序功能

        前端实战:基于Vue3与免费满血版DeepSeek实现无限滚动+懒加载+瀑布流模块及优化策略

        MutationObserver详解+案例——深入理解 JavaScript 中的 MutationObserver

        JavaScript中通过array.map()实现数据转换、创建派生数组、异步数据流处理、DOM操作等

        高效工作流:用Mermaid绘制你的专属流程图;如何在Vue3中导入mermaid绘制流程图

        干货含源码!如何用Java后端操作Docker(命令行篇)

        在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境

        Dockerfile全面指南:从基础到进阶,掌握容器化构建的核心工具

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2406308.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

dvwa11——XSS(Reflected)

LOW 分析源码&#xff1a;无过滤 和上一关一样&#xff0c;这一关在输入框内输入&#xff0c;成功回显 <script>alert(relee);</script> MEDIUM 分析源码&#xff0c;是把<script>替换成了空格&#xff0c;但没有禁用大写 改大写即可&#xff0c;注意函数…

【Axure高保真原型】图片列表添加和删除图片

今天和大家分享图片列表添加和删除图片的原型模板&#xff0c;效果包括&#xff1a; 点击图片列表的加号可以显示图片选择器&#xff0c;选择里面的图片&#xff1b; 选择图片后点击添加按钮&#xff0c;可以将该图片添加到图片列表&#xff1b; 鼠标移入图片列表的图片&…

XXE漏洞知识

目录 1.XXE简介与危害 XML概念 XML与HTML的区别 1.pom.xml 主要作用 2.web.xml 3.mybatis 2.XXE概念与危害 案例&#xff1a;文件读取&#xff08;需要Apache >5.4版本&#xff09; 案例&#xff1a;内网探测&#xff08;鸡肋&#xff09; 案例&#xff1a;执行命…

mq安装新版-3.13.7的安装

一、下载包&#xff0c;上传到服务器 https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.13.7/rabbitmq-server-generic-unix-3.13.7.tar.xz 二、 erlang直接安装 rpm -ivh erlang-26.2.4-1.el8.x86_64.rpm不需要配置环境变量&#xff0c;直接就安装了。 erl…

DL00871-基于深度学习YOLOv11的盲人障碍物目标检测含完整数据集

基于深度学习YOLOv11的盲人障碍物目标检测&#xff1a;开启盲人出行新纪元 在全球范围内&#xff0c;盲人及视觉障碍者的出行问题一直是社会关注的重点。尽管技术不断进步&#xff0c;许多城市的无障碍设施依然未能满足盲人出行的实际需求。尤其是在复杂的城市环境中&#xff…

华硕电脑,全新的超频方式,无需进入BIOS

想要追求更佳性能释放 或探索更多可玩性的小伙伴&#xff0c; 可能会需要为你的电脑超频。 但我们常用的不论是BIOS里的超频&#xff0c; 还是Armoury Crate奥创智控中心超频&#xff0c; 每次调节都要重启&#xff0c;有点麻烦。 TurboV Core 全新的超频方案来了 4不规…

安全领域新突破:可视化让隐患无处遁形

在安全领域&#xff0c;隐患就像暗处的 “幽灵”&#xff0c;随时可能引发严重事故。传统安全排查手段&#xff0c;常常难以将它们一网打尽。你是否好奇&#xff0c;究竟是什么神奇力量&#xff0c;能让这些潜藏的隐患无所遁形&#xff1f;没错&#xff0c;就是可视化技术。它如…

Vue.js教学第二十一章:vue实战项目二,个人博客搭建

基于 Vue 的个人博客网站搭建 摘要: 随着前端技术的不断发展,Vue 作为一种轻量级、高效的前端框架,为个人博客网站的搭建提供了极大的便利。本文详细介绍了基于 Vue 搭建个人博客网站的全过程,包括项目背景、技术选型、项目架构设计、功能模块实现、性能优化与测试等方面。…

[KCTF]CORE CrackMe v2.0

这个Reverse比较古老&#xff0c;已经有20多年了&#xff0c;但难度确实不小。 先查壳 upx压缩壳&#xff0c;0.72&#xff0c;废弃版本&#xff0c;工具无法解压。 反正不用IDA进行调试&#xff0c;直接x32dbg中&#xff0c;dump内存&#xff0c;保存后拖入IDA。 这里说一下…

Ubuntu 安装 Mysql 数据库

首先更新apt-get工具&#xff0c;执行命令如下&#xff1a; apt-get upgrade安装Mysql&#xff0c;执行如下命令&#xff1a; apt-get install mysql-server 开启Mysql 服务&#xff0c;执行命令如下&#xff1a; service mysql start并确认是否成功开启mysql,执行命令如下&am…

Java高级 |【实验八】springboot 使用Websocket

隶属文章&#xff1a;Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 系列文章&#xff1a;Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…

设计模式-3 行为型模式

一、观察者模式 1、定义 定义对象之间的一对多的依赖关系&#xff0c;这样当一个对象改变状态时&#xff0c;它的所有依赖项都会自动得到通知和更新。 描述复杂的流程控制 描述多个类或者对象之间怎样互相协作共同完成单个对象都无法单独度完成的任务 它涉及算法与对象间职责…

实现p2p的webrtc-srs版本

1. 基本知识 1.1 webrtc 一、WebRTC的本质&#xff1a;实时通信的“网络协议栈”类比 将WebRTC类比为Linux网络协议栈极具洞察力&#xff0c;二者在架构设计和功能定位上高度相似&#xff1a; 分层协议栈架构 Linux网络协议栈&#xff1a;从底层物理层到应用层&#xff08;如…

第2篇:BLE 广播与扫描机制详解

本文是《BLE 协议从入门到专家》专栏第二篇,专注于解析 BLE 广播(Advertising)与扫描(Scanning)机制。我们将从协议层结构、广播包格式、设备发现流程、控制器行为、开发者 API、广播冲突与多设备调度等方面,全面拆解这一 BLE 最基础也是最关键的通信机制。 一、什么是 B…

开源 vGPU 方案:HAMi,实现细粒度 GPU 切分

本文主要分享一个开源的 GPU 虚拟化方案&#xff1a;HAMi&#xff0c;包括如何安装、配置以及使用。 相比于上一篇分享的 TimeSlicing 方案&#xff0c;HAMi 除了 GPU 共享之外还可以实现 GPU core、memory 得限制&#xff0c;保证共享同一 GPU 的各个 Pod 都能拿到足够的资源。…

盲盒一番赏小程序:引领盲盒新潮流

在盲盒市场日益火爆的今天&#xff0c;如何才能在众多盲盒产品中脱颖而出&#xff1f;盲盒一番赏小程序给出了答案&#xff0c;它以创新的玩法和优质的服务&#xff0c;引领着盲盒新潮流。 一番赏小程序的最大特色在于其独特的赏品分级制度。赏品分为多个等级&#xff0c;从普…

边缘计算设备全解析:边缘盒子在各大行业的落地应用场景

随着工业物联网、AI、5G的发展&#xff0c;数据量呈爆炸式增长。但你有没有想过&#xff0c;我们生成的数据&#xff0c;真的都要发回云端处理吗&#xff1f;其实不一定。特别是在一些对响应时间、网络带宽、数据隐私要求高的行业里&#xff0c;边缘计算开始“火”了起来&#…

Linux实现线程同步的方式有哪些?

什么是线程同步&#xff1f; 想象一下超市收银台&#xff1a;如果所有顾客&#xff08;线程&#xff09;同时挤向同一个收银台&#xff08;共享资源&#xff09;&#xff0c;场面会一片混乱。线程同步就是给顾客们发"排队号码牌"&#xff0c;确保&#xff1a; 有序访…

python学习day39

图像数据与显存 知识点回顾 1.图像数据的格式&#xff1a;灰度和彩色数据 2.模型的定义 3.显存占用的4种地方 a.模型参数梯度参数 b.优化器参数 c.数据批量所占显存 d.神经元输出中间状态 4.batchisize和训练的关系 import torch import torchvision import torch.nn as nn imp…

年度峰会上,抖音依靠人工智能和搜索功能吸引广告主

上周早些时候举行的第五届年度TikTok World产品峰会上&#xff0c;TikTok推出了一系列旨在增强该应用对广告主吸引力的功能。 新产品列表的首位是TikTok Market Scope&#xff0c;这是一个全新的分析平台&#xff0c;为广告主提供整个考虑漏斗的全面视图&#xff0c;使他们能够…