页面的滚动及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.mzph.cn/news/33457.shtml

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

相关文章

Elastic Stack 8.9:更快的跨集群搜索和指标聚合

作者&#xff1a;Tyler Perkins, Gilad Gal, Teresa Soler, Shani Sagiv, Bernhard Suhm, George Kobar Elastic Stack 8.9 在多个方面实现了显着的性能改进&#xff1a;Kibana 中更快的跨集群搜索、Elasticsearch 更快的聚合&#xff0c;以及更快、更相关的向量搜索&#xff0…

Linux下快速创建大文件的4种方法总结

1、使用 dd 命令创建大文件 dd 命令用于复制和转换文件&#xff0c;它最常见的用途是创建实时 Linux USB。dd 命令是实际写入硬盘&#xff0c;文件产生的速度取决于硬盘的读写速度&#xff0c;根据文件的大小&#xff0c;该命令将需要一些时间才能完成。 假设我们要创建一个名…

【云原生】kubernetes在Pod中init容器的作用和使用

目录 Pod 中 init 容器 1 init 容器特点 2 使用 init 容器 Pod 中 init 容器 Init 容器是一种特殊容器&#xff0c;在Pod 内的应用容器启动之前运行。Init 容器可以包括一些应用镜像中不存在的实用工具和安装脚本。 1 init 容器特点 init 容器与普通的容器非常像&#xf…

自动化测试:你根本不懂自动化测试的快乐

接触了不少同行&#xff0c;由于他们之前一直做手工测试&#xff0c;现在很迫切希望做自动化测试&#xff0c;其中不乏工作5年以上的人。 本人从事软件自动化测试已经近6年&#xff0c;从server端到web端&#xff0c;从API到mobile&#xff0c;切身体会到自动化带来的好处与痛楚…

CEC2013(MATLAB):遗传算法(Genetic Algorithm,GA)求解CEC2013的28个函数

一、遗传算法GA 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;起源于对生物系统所进行的计算机模拟研究&#xff0c;是一种随机全局搜索优化方法&#xff0c;它模拟了自然选择和遗传中发生的复制、交叉(crossover)和变异(mutation)等现象&#xff0c;从任…

前端笔试题1

HTML/CSS 题1&#xff1a; 1&#xff0e;使用CSS 让该节点不可见&#xff0c;方法越多越好。 <div class"hidden">Hi</div> 使用CSS 让节点不可见的方法有以下几种&#xff1a; 把 visibility 属性设置为 hidden&#xff0c;这样元素框不会被绘制&…

CTFSHOW php命令执行

目录 web29 过滤flag web30 过滤system php web31 过滤 cat|sort|shell|\. 这里有一个新姿势 可以学习一下 web32 过滤 &#xff1b; . web33 web34 web35 web36 web37 data伪协议 web38 短开表达式 web39 web40 __FILE__命令的扩展 web41 web42 重定向…

SpringBoot自定义拦截器interceptor使用详解

Spring Boot拦截器Intercepter详解 Intercepter是由Spring提供的Intercepter拦截器&#xff0c;主要应用在日志记录、权限校验等安全管理方便。 使用过程 1.创建自定义拦截器&#xff0c;实现HandlerInterceptor接口,并按照要求重写指定方法 HandlerInterceptor接口源码&am…

[深度学习入门]PyTorch深度学习[Numpy基础](上)

目录 一、前言二、Numpy概述三、生成Numpy数组3.1 从已有数据中创建数组3.2 利用random模块生成数组3.3 创建特定形状的多维数组3.4 利用arange和linspace函数生成数组 四、获取元素五、Numpy的算术运算5.1 对应元素相乘5.2 点积运算 六、后记 本文的目标受众&#xff1a; 对机…

C++进阶 智能指针

本篇博客简介&#xff1a;介绍C中的智能指针 智能指针 为什么会存在智能指针内存泄露内存泄漏定义内存泄漏的危害如何检测内存泄漏如何避免内存泄漏 智能指针的使用及其原理RAII设计一个智能指针C官方的智能指针 定制删除器智能指针总结 为什么会存在智能指针 我们首先来看下面…

Spring5 AOP 默认使用 JDK

这是博主在使用dubbo实现远程过程调用的时候遇到的问题&#xff1a; 我们如果在服务提供者类上加入Transactional事务控制注解后&#xff0c;服务就发布不成功了。原因是事务控制的底层原理是为服务提供者类创建代理对象&#xff0c;而默认情况下Spring是基于JDK动态代理方式创…

SpringBoot 整合Swagger2

一、Swagger简介 Swagger是一套开源工具和规范&#xff0c;用于设计、构建和文档化RESTful Web服务。它允许开发人员定义API的各个方面&#xff0c;并生成易于理解的API文档和交互式API探索界面。同时&#xff0c;Swagger还提供代码生成工具&#xff0c;可自动生成与API交互的客…

MySQL和钉钉单据接口对接

MySQL和钉钉单据接口对接 数据源系统:钉钉 钉钉&#xff08;DingTalk&#xff09;是阿里巴巴集团打造的企业级智能移动办公平台&#xff0c;是数字经济时代的企业组织协同办公和应用开发平台。钉钉将IM即时沟通、钉钉文档、钉闪会、钉盘、Teambition、OA审批、智能人事、钉工牌…

dingding机器人

“自定义机器人”只支持消息发送&#xff0c;自动回复需要“企业内部机器人” 消息发送 import requests import jsonres requests.post(https://oapi.dingtalk.com/robot/send?access_token036a339axxx,data json.dumps({"text": {"content":"h…

医疗保健中的 NLP:实体链接

一、说明 HEalthcare和生命科学行业产生大量数据&#xff0c;这些数据是由合规性和监管要求&#xff0c;记录保存&#xff0c;研究论文等驱动的。但随着数据量的增加&#xff0c;搜索用于研究目的的必要文件和文章以及数据结构成为一个更加复杂和耗时的过程。例如&#xff0c;如…

消息队列(11) - 通信协议的设计

目录 通信协议设计代码实现 通信协议设计 对于我们客户端与服务器之间的通信协议我们约定如下&#xff1a; 具体的协议设计: 之后我们传递的参数也是这些 关于 type其实是在描述当前这个请求 、 响应是在调用那个API 约定如下 对于channel ,是tcp链接中的一个逻辑上的链接,…

策略模式实战应用

场景 假设做了个卖课网站&#xff0c;会员等级分为月vip、年vip、终生vip&#xff0c;每个等级买课的优惠力度不一样&#xff0c;传统的写法肯定是一堆的 if-else&#xff0c;现在使用策略模式写出代码实现 代码实现 策略模式的核心思想就是对扩展开放&#xff0c;对修改关闭…

应用案例|基于三维机器视觉的机器人纸箱拆码垛应用解决方案

Part.1 项目背景 在现代物流和制造行业中&#xff0c;纸箱的拆码垛操作是一项重要且频繁的任务。传统的纸箱拆码垛工作通常由人工完成&#xff0c;这种方式存在劳动强度大、生产效率低以及人为操作容易导致错误等问题&#xff0c;严重影响物料的安全运输和质量。为了满足物流行…

大模型“瘦身”进手机 下一个iPhone时刻将至?

一股“端侧大模型”浪潮正在涌来。华为、高通等芯片巨头正探索将AI大模型植入端侧&#xff0c;让手机实现新一代物种进化。 相比ChatGPT、Midjourney等AI应用依赖云端服务器提供服务&#xff0c;端侧大模型主打在本地实现智能化。它的优势在于能够更好地保护隐私&#xff0c;同…

有没有推荐的golang的练手项目?

前言 下面是github上的golang项目&#xff0c;适合练手&#xff0c;可以自己选择一些项目去练习&#xff0c;整理不易&#xff0c;希望能多多点赞收藏一下&#xff01;废话少说&#xff0c;我们直接进入正题>>> 先推荐几个教程性质的项目&#xff08;用于新手学习、巩…