页面的滚动及scrollIntoView的穿透效果和解决

朋友今天遇到一个奇怪的问题,我觉得很有意思就记录一下。现象是这样的,页面有一个按钮,点击按钮以后会请求一个接口拿到一个iframe的地址然后创建一个iframe并渲染到页面上,iframe的页面加载完毕后会滑动到对应的某一个元素的位置,本来滑动是没问题的,但是父页面也跟着滑动了。现场的好几个领导就站在他后面盯着他解决,可想而知有多可怕了。他认为可能是iframe有锚点导致父页面受了影响,不过我并不认同这个想法,iframe的hash都对父元素有影响你这是看不起w3c还是看不起google。不过我也对这块不是很了解,正好今天不是很忙就想着动手试试复现,下面我简单记录一下问题的解决过程。

问题复现

在了解问题的现象以后我第一步就是按朋友的想法去复现问题,首先我创建了ab两个页面:

  <!-- a -->  
<body><div class="home"><button>点击生成iframe</button></div></body><script>const home = document.querySelector(".home");const btn = document.querySelector("button");btn.addEventListener("click", function (e) {const child = document.createElement("iframe");child.src = "http://127.0.0.1:5500/b.html";child.width = "800";child.height = "700";home.insertAdjacentElement("afterend", child);});</script><!-- b --><body><div class="box"><div class="aaa"></div><div id='target' class="bbb"></div></div></body>

样式我就不说了,大致就是点击按钮会生成iframe添加到a页面中,页面我用的live server还蛮方便的,我发现和我想的一样,和锚点根本没关系,说我google连这都处理不好?没道理的。不过既然滚动和锚点没关系那说明是iframe中有js让页面滚动了,于是我在b中添加js代码去滚动元素。

元素的滚动

这里我设置的.box高度是600,里面两个div的高度也都是600,上面.aaa是红色下面.bbb是天蓝色,这里我一共试了四种方案分别是:scrollTopscrollToscrollscrollIntoView,在我测试过程中发现只有**scrollIntoView**会出现朋友说的现象。

  <script>window.onload = function () {document.querySelector(".bbb").scrollIntoView({ behavior: "smooth" });// document.querySelector(".box").scrollTop = 600;// document.querySelector(".box").scrollTo({//   top: 600,//   behavior: "smooth",// });// document.querySelector(".box").scroll({//   top: 600,//   behavior: "smooth",// });};</script>

这里还是能很明显的看到问题所在的,在插入iframe以后首先是父页面滚动了一下然后iframe的内容滚动到目标位置。这个现象很奇怪,我查了MDN以及csswg都并没有说明这个现象,倒是在别处看到scrollIntoView不仅仅会让父容器滚动,父容器的容器有滚动条也会滚动的说法,这和我们看到的现象正好是一致的。所以我个人将这种现象称之为scrollIntoView的滚动的逆向穿透

解决方案

既然问题找到了下一步当然就是解决问题了,现在的问题是iframe是外部提供的我们没办法去更改iframe的代码,我朋友也是第一时间找了提供方的技术人员,那最合适的方式就是让那边的技术人员把**scrollIntoView的写法更改为其余三种的一种,不过我个人不推荐设置scrollTop**,没有过渡效果看起来不美观。

当然解决方案并不是只有这些,上面说的是最理想的情况了,不管是因为说炫技又或者说是iframe提供方支持不及时又或者其他原因,那我们能不能在父页面解决这个问题呢?下面简单分享一下解决的方法

一、事件监听(❌)

刚开始看效果我们很容易联想到一个事件相关的词——事件捕获,虽然我们没设置事件但是我想的是是否可以通过监听事件来控制对应的行为,使用 e.stopPropagation();去阻止按钮的默认行为我相信大多数人都是用过的,这里应该也是同理。

    document.onscroll = function () {e.stopPropagation();};

然而事情并没有如料想的一样,滑动无法被阻止,后来我查了一下发现scroll是一个UI事件,UI事件是不能被阻止的,因为这个只有变化完成了才会触发这个事件,我们没办法做到时空回溯。其实这里仔细想想就算是这个事件可以做到阻止滑动这里也有很大的局限性,因为如果阻止了页面的滑动就代表这个页面的一切滑动都被禁止了,包括原本父页面应该有的滑动,就算我们监听事件给的是一次性监听,但是对于事件和其他情况的兼容也是很复杂的,所以这个方法无论是理论还是实际其实都是不可行的。

二、重写滑动行为

其实在上个想法还没有得到验证的时候我脑海里就有了第二个想法——父页面写js去覆盖原有行为。这里经过朋友的提醒我这里分了两种情况分别是同步和异步。

1.同步情况覆盖

假设这个iframe的代码和我上面的案例是一样的都是在onload事件中去调用的scrollIntoView方法,那我们在父元素对iframe多添加一个onload事件,让后面的行为去覆盖前面的行为,最起码理论上是可行的

child.onload = function () {child.contentWindow.document.querySelector(".box").scrollTo({top: 600,behavior: "smooth",});
};

这里iframe的获取,因为我原生写的所以直接就可以拿到,Vue和React用户就自己用ref吧,拿到iframe的DOM实例以后我们可以通过contentWindow属性拿到iframe的window对象,因为我们添加的脚本一定是晚于iframe的自己的监听的,所以如果是相同的行为肯定是我们的行为去覆盖他原有的行为,我们看看效果如何:

不能说很好,只能说完美。

2.异步情况覆盖

本来到上面我都觉得已经大功告成,但是朋友提出了一个疑问,如果他的定位是在查询一个接口以后才进行的,你如何处理呢?这小子还挺会找问题,确实,因为我们是onload事件去写的脚本,所以如果他定位的代码是异步的,我们的脚本就失去意义了。朋友是想通过通信解决,让iframe要滑动的时候通信这边再改,我觉得有点本末倒置了,既然你都改iframe为啥不直接换个滑动方式,何必还通信多此一举呢,有点本末倒置了对吧。

那不然没办法解决了吗?其实问题的核心就在于我们不知道iframe的滑动代码是什么时候执行的,那有什么办法可以知道呢,其实通过一个监听就可以完成,还记得上面我说scrollIntoView的这个行为有点像捕获吗?父页面先滑动iframe的内容后滑动,所以我们给父页面添加一个scroll的监听,一旦监听触发就代表iframe的滑动代码执行了,然后我们更改滑动行为即可:

  document.onscroll = function () {child.contentWindow.document.querySelector(".box").scrollTo({top: 600,behavior: "smooth",});};

当然这个监听是一次性的了,毕竟这个方法副作用还是很大的,设置监听的时候可以用addEventListener然后给once属性即可。当然既然这个事件触发也就代表父页面其实是滚动了的,但是滚动时间触发的间隔其实是很短的 ,第一次触发的时候我们就覆盖了原有行为所以这里父页面的滚动只会触发一次,这一次估计也就几像素不仔细盯着看是看不出问题的。

3.补充

其实除了更改iframe滑动的行为方式以外,更多的解决方案是父元素滚动到原有位置

window.scrollTo({top:0
})

单就结果来说其实两种方式差别都不大,更多的是解决思路不同,不过无论是哪种解决思路最本质的都是在scrollIntoView的影响出现时去消除影响。如果影响的时机是已知的那就可以很方便的解决,但是如果是未知的可能就要采取监听滑动的方式去处理了。由监听滑动引出的一个比较重要的问题就是父页面滑动的触发方式,假设iframe的滑动行为要三秒后才执行,期间用户滑动了页面那我们的脚本要不要执行就是一个很大的问题,所以这里对于触发方式的判断也是很重要的。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Transformer学习笔记

Transformer学习笔记 前言前提条件相关介绍Transformer总体架构编码器&#xff08;Encoder&#xff09;位置编码&#xff08;Positional Encoding&#xff09;get_attn_pad_mask函数&#xff08;Padding Mask&#xff09;EncoderLayerMultiHeadAttentionScaledDotProductAttent…

前端开发常见效果

目录 css实现图像填充文字 css实现手风琴效果 css实现网站变灰色 elementUi的导航栏效果 css实现滚动吸附效果 鼠标经过&#xff0c;元素内部放大 css实现图像填充文字 效果图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html><head><meta c…

day17 enum abstract interface 枚举 抽象 接口

一、枚举 enum 枚举本来的面目 创建Season类&#xff0c; 所有类都默认继承Object&#xff0c;写不写都一样 声明属性 &#xff1a;季节的名字、 季节的描述&#xff0c; 因为枚举的对象是看的见的客观事物&#xff0c; 想让它的属性不可修改 使用 final修饰表示最终的 &am…

4.DNS和负载均衡

文章目录 coreDNS概念部署croeDNS测试 kubernetes多master集群结构master节点部署 负载均衡配置部署nginx做四层反向代理安装高可用 keepalivednginx监控脚本修改k8s中组件的配置文件 coreDNS 概念 coreDNS是kubernetes的默认DNS实现。可以为集群中的service资源创建一个资源名…

SQL Server 查询数据并汇总相关技巧 23.08.08

GROUPING 是一个聚合函数,它产生一个附加的列&#xff0c;当用 CUBE 或 ROLLUP 运算符添加行时&#xff0c;附加的列输出值为1&#xff0c;当所添加的行不是由 CUBE 或 ROLLUP 产生时&#xff0c;附加列值为0。 仅在与包含 CUBE 或 ROLLUP 运算符的 GROUP BY 子句相联系的选择…

安卓证书生成教程

1.下载安装JDK文件&#xff08;如已安装请跳过&#xff09; 根据电脑系统版本下载JDK版本文件 下载地址&#xff1a;[https://www.oracle.com/java/technologies/downloads/](https://www.oracle.com/java/technologies/downloads/) 如果电脑上安装过JDK文件可以跳过2.生成密钥…

怎么制作gif动态图?gif图片在线制作攻略分享

现在许多品牌和营销活动也使用gif动态图来吸引用户注意力、提升品牌形象或传递特定的信息&#xff0c;那么gif制作的过程到底难不难呢&#xff1f;其实只需要使用gif图片在线制作工具就非常简单了&#xff0c;下面以图片制作gif&#xff08;https://www.gif.cn&#xff09;为例…

Unity游戏源码分享-塔防游戏保卫兔子的食物CarrotFantasy

Unity游戏源码分享-塔防游戏保卫兔子的食物CarrotFantasy 经典塔防游戏&#xff0c;可发布PC、Andoid、IOS、Web等 下载地址&#xff1a;https://download.csdn.net/download/Highning0007/88189987

计算机网络体系结构

计算机网络体系结构 常用的网络体系结构 OSI体系结构 为了使不同体系结构的计算机网络都能互连&#xff0c;国际标准化组织于1977年成立了专门机构研究该问题&#xff0c;不就他们就提出了一个试图使各种计算机在世界范围内互连成网的标准框架&#xff0c;也就是著名的开放系…

rancher + k3s + docker私有仓库搭建K8环境(百分之百成功)

1.环境准备&#xff08;4台机器&#xff09; 文档准备了4台虚拟机&#xff0c;系统是ubuntu server: ubuntu-22.04.1-live-server-amd64.iso 然后进行安装4台虚拟机&#xff0c;如图&#xff1a; 其中一台用于rancher、其余三台跑K8&#xff0c;一台master&#xff0c;两台工作…

代谢组学市场分析,制药及生物制药行业正在推动全球代谢组学产业的发展

代谢组学是对某一生物或细胞所有小分子量代谢产物进行定性和定量分析的一门新兴学科&#xff0c;其揭示的小分子代谢产物变化是机体内基因、蛋白质/酶等功能变化的一系列事件的最终结果&#xff0c;直接反映了生物体系的最终状态&#xff0c;可以反映机体特定病理生理状态下整体…

面试热题(反转链表)

给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 链表的题&#xff0c;大部分都可以用指针或者递归可以做&#xff0c;指针如果做不出来的话&#xff0c;…

JMeter 查看 TPS 数据,详细指南

TPS 是软件测试结果的测量单位。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时&#xff0c;收到服务器响应后结束计时&#xff0c;以此来计算使用的时间和完成的事务个数。在 JMeter 中&#xff0c;我们可以使用以下方法查看 T…

【腾讯云Cloud Studio实战训练营】React 快速构建点餐页面

前言&#xff1a; Cloud Studio是一个在线的云集成开发环境&#xff08;IDE&#xff09;&#xff0c;可以让开发人员在浏览器中轻松地开发、测试、调试和部署应用程序。它提供了基于云的计算资源和工具&#xff0c;例如代码编辑器、编译器、调试器、版本控制系统和项目管理工具…

最大子数组和【力扣53】

一、解题思路 Max[i]表示&#xff1a;以nums[i]为开头的所有连续子数组和的最大值。 由此可以推出Max[i-1]和Max[i]的关系&#xff1a; 若Max[i]>0&#xff1a;Max[i-1]nums[i-1]Max[i]&#xff1b; 否则&#xff1a;Max[i-1]nums[i-1]&#xff1b; 则ansMAX&#xff0…

SAP从入门到放弃系列之BOM行项目-虚拟装配-Part4

文章目录 虚拟组件&#xff08;Phantom assemblies&#xff09;&#xff1a;作用&#xff1a;BOM中虚拟件维护的方式&#xff1a;物料主数据维度BOM组件维度&#xff08;数据优先级最高&#xff09;BOM组件的展开类型&#xff1a;BOM组件的特殊采购类 数据测试示例&#xff1a;…

flutter 初识(开发体验,优缺点)

前言 最近有个跨平台桌面应用的需求&#xff0c;需要支持 windows/linux/mac 系统&#xff0c;要做个更新应用的小界面&#xff0c;主要功能就是下载更新文件并在本地进行替换&#xff0c;很简单的小功能。 花了几分钟构建没做 UI 优化的示例界面&#xff1a; 由于我们的客…

设计模式篇

工厂方法模式 简单工厂模式 工厂方法模式 抽象工厂模式 策略模式 责任链模式

【面试八股文】每日一题:谈谈你对异常的理解

每日一题-Java核心-谈谈你对异常的理解【面试八股文】 异常是程序在运行过程中出现的错误或不正常的情况。当程序执行过程中遇到无法处理的错误或者不符合预期的情况&#xff0c;就会抛出异常。异常可以分为两种类型&#xff1a;受检异常和非受检异常。 受检异常是指在程序编译…

【LeetCode】买卖股票的最佳时机含冷冻期

买卖股票的最佳时机含冷冻期 题目描述算法分析程序设计 链接: 买卖股票的最佳时机含冷冻期 题目描述 算法分析 程序设计 class Solution { public:int maxProfit(vector<int>& prices) {int n prices.size();//天数vector<vector<int>> dp(n,vector&l…