js flyout 2: VScroll

news/2024/5/5 20:56:21/文章来源:https://blog.csdn.net/big_cheng/article/details/130101031

目录

  • 版权
  • 描述
  • 测试页面
  • showFlyout
  • 问题1 - scroll 实现可能不准?
  • 问题2 - 容器内容重排可导致浮层错位
    • 关于重排
  • 小结
  • 附录 - 完整代码

版权

本文为原创, 遵循 CC 4.0 BY-SA 版权协议, 转载需注明出处: https://blog.csdn.net/big_cheng/article/details/130101031.
文中代码属于 public domain (无版权).

描述

上一篇博文 js flyout 介绍了浮层, 实践中发现浮层弹出时如果超出范围会导致容器出现滚动条. 本文研究如何自动消除VScroll (希望页面整洁, 只有body 有VScroll).

首先重整一下原来的代码. 在head 定义样式:

    <style>.hide {display: none;}._flyout {position: absolute; padding: 10px;border: 1px solid lightslategray; border-radius: 8px;}</style>

定义一个fw 对象, 封装一些基础功能:

    <script>var fw = {noop() {return false; },get(id) {return document.getElementById(id); },show(id) {fw.get(id).classList.remove("hide"); },hide(id) {fw.get(id).classList.add("hide"); }};

显示/隐藏通过操纵"hide" class 实现.
将Flyout 移入fw 对象并稍做调整:

        /** Flyout 行为: OutsideClick/ESC => 隐藏. */fw.decorateFlyout = function(id, onHide) {var ele = fw.get(id);ele.onHide = (onHide || fw.noop);var fnH = function() {window.removeEventListener('click', fnH); // del hookfw.hide(id);ele.onHide();};ele.onkeydown = function(evt) {if (evt.keyCode == 27) fnH() }; // ESCele.onclick = function(evt) {evt.stopPropagation() }; // 屏蔽InsideClickele.fShow = function() {window.removeEventListener('click', fnH); //(可能连续fShow, 先del old hook)fw.show(id);window.setTimeout(function() { // add hook, 需异步window.addEventListener('click', fnH);}, 100);};ele.fHide = fnH;};

主要修改是将onHide 暴露为ele 的一个属性, 便于在隐藏时增加逻辑.

测试页面

测试页面效果图:
在这里插入图片描述
容器加黑框, 内有文字/按钮等, 浮层也定义在此容器内. 点按钮显示浮层时, 如果浮层高度超出容器下边缘会导致容器自动出现VScroll. 页面代码:

<body><a href="xxx">another link</a><p></p><style>.ct {border: 1px solid; min-height: 200px; overflow-x: auto;position: relative;}#ov {width: 110%; border: 1px solid blue;}#f2 {white-space: nowrap; height: 50px;}</style><div id="ct" class="ct">A brown fox quickly jump over a fence.<br><div id="ov">overflow content</div><p></p><button onclick="ov.innerText += ov.innerText;">Add ov</button><p></p><button onclick="showFlyout(f1, event, 0, 0);">Show f1</button><button id="btn2" onclick="showFlyout(f2, event, 40, 20);">Show f2</button><div id="f1" class="_flyout hide">flyout content: f1</div><script> fw.decorateFlyout("f1"); </script><div id="f2" class="_flyout hide">flyout content: f2</div><script> fw.decorateFlyout("f2"); </script></div><!-- ct --></body>
</html>

子容器ov 宽度110% 导致容器有HScroll.

showFlyout

由于常常需要相对鼠标点击位置来显示浮层, 增加一个showFlyout 方法:

        // 显示flyout.function showFlyout(fly, evt, dx, dy) {var v = evt.pageX - ct.offsetLeft + ct.scrollLeft + dx;if (v < 1) v = 1;fly.style.left = v+"px";v = evt.pageY - ct.offsetTop + ct.scrollTop + dy;if (v < 1) v = 1;fly.style.top = v+"px";fly.fShow();

鼠标事件的pageX/pageY 是相对于document 的左上角. 例如查到测试页里容器距离document 上边缘 = ct.offsetTop = 46px, 要换算成相对于容器左上角的坐标, 需要减去此距离.
如果容器目前有VScroll 且部分内容滚出了容器上边缘(高度即ct.scrollTop), 要换算成相对于容器左上角的坐标, 需要加上此高度.

设置了浮层左上角坐标并显示出来后, 如果浮层高度超出容器下边缘会导致容器出现VScroll. 例如点击"Show f2" 按钮(右边缘处) 效果图:
在这里插入图片描述
参考"css scroll 上手试验" 博文的"要消除VScroll, 还需考虑HScroll" 小节, 要消除容器VScroll 需要设置高度 = scrollHeight + HScroll的高度:

        // 显示flyout.function showFlyout(fly, evt, dx, dy) {......// 消除VScrollif (ct.scrollHeight > ct.clientHeight) {var oldV = ct.style.minHeight;ct.style.minHeight =(ct.scrollHeight + (ct.offsetHeight - 2 - ct.clientHeight))+"px";var oldOnH = fly.onHide;fly.onHide = function() {ct.style.minHeight = oldV;fly.onHide = oldOnH;oldOnH();};}}</script>

scrollHeight > clientHeight 判断为有VScroll.
offsetHeight - 2:容器上下边框线 - clientHeight = HScroll的高度.
注意这里要设置"min-height" 而非"height", 因为容器的(文字)内容重排(例如用户压缩屏幕宽度时) 可能导致纵向溢出, 如果高度固定则又会出现VScroll.

如果修改了高度, 那么也修改ele.onHide: 在调用原有onHide 之前自动恢复修改前的高度.

现在f2 浮层出现后容器自动增高, 不出现VScroll; 浮层隐藏后容器高度又恢复.

问题1 - scroll 实现可能不准?

从上往下逐渐点击"Show f2" 按钮的右边缘, 偶尔会出现几个问题(Chrome 112.0.5615.50, 64 位):
a) scrollHeight == clientHeight 但出现了VScroll, 而且内容可以滚动约1px.
b) scrollHeight == clientHeight 也没有VScroll, 但浮层下边框线未显示.

效果图:
在这里插入图片描述
将"min-height" 调高(1px或2px):

                ct.style.minHeight =(ct.scrollHeight + (ct.offsetHeight - 2 - ct.clientHeight) + 1)+"px";

问题均仍可能出现. 甚至加到5 后仍观察到b) 现象. 改成异步调高也不解决.

“css scroll 上手试验” 博文里也观察到较明确的scroll 实现可能不准的现象.

问题2 - 容器内容重排可导致浮层错位

屏宽调到261px: 容器(ct) 含框宽245 = 261 - 16:body margin. 子容器(ov) 含框宽269 = (245 - 2:左右边框线) * 110% + 2:子容器左右边框线 = 243*1.1 + 2.

点击"Add ov" 按钮4次 => 再点击"Show f2", 效果对比图如下:
在这里插入图片描述
浮层f2 显示(fly.fShow() ) 后, 子容器发生内容重排撑高了ct 容器, 导致f2 相对于按钮本应在下变成了在上. 在判断是否有VScroll (“if (ct.scrollHeight > ct.clientHeight) {”) 这一行加断点, 执行到这里暂停可以看到当时的界面就等于最终效果图的界面.

关于重排

刷新页面, 调整ct 的样式: 去掉"min-height", 增加"height: 100px;". 查到ov 含框宽是251:
在这里插入图片描述
(244.84:容器宽 - 2 - 16:VScroll)*1.1 + 2 = 251.524. 推测是这样:
a) fw.show 显示f2 (在按钮下方 - 纵向空间不够)
b) ct 出现VScroll
c) ov 110% 的基数要减去VScroll, ov 变窄 => 发生重排
d) 按钮被挤到f2 下方, 发生错位

最终出现一个类似"问题1" 的情况: scrollHeight == clientHeight 但出现了VScroll - 但disable了/不可滚动.

尝试在判断是否有VScroll (“if (ct.scrollHeight > ct.clientHeight) {”) 后面增加else 分支: 写死设置minHeight = 效果图里最终的高. 结果VScroll 确实消失 => ov 110% 的基数变大, 重排回去! => 按钮返回浮层上方, 不再错位 => 容器在浮层下方多出一段空间:
在这里插入图片描述
重排后再调整高度可能触发再次重排, 这种情况下如何消除VScroll 需要再探索.

小结

问题1 不算严重.

问题2 在很多页面布局里不会遇到. 例如本例里调整ov 的样式: 去掉"width: 110%;“, 增加"display: grid; grid-template-columns: 1fr;”, 则ov 宽度不受f2 显示/隐藏的影响, 就不会发生重排.

附录 - 完整代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>flyout2</title><style>.hide {display: none;}._flyout {position: absolute; padding: 10px;border: 1px solid lightslategray; border-radius: 8px;}</style><script>var fw = {noop() {return false; },get(id) {return document.getElementById(id); },show(id) {fw.get(id).classList.remove("hide"); },hide(id) {fw.get(id).classList.add("hide"); }};/** Flyout 行为: OutsideClick/ESC => 隐藏. */fw.decorateFlyout = function(id, onHide) {var ele = fw.get(id);ele.onHide = (onHide || fw.noop);var fnH = function() {window.removeEventListener('click', fnH); // del hookfw.hide(id);ele.onHide();};ele.onkeydown = function(evt) {if (evt.keyCode == 27) fnH() }; // ESCele.onclick = function(evt) {evt.stopPropagation() }; // 屏蔽InsideClickele.fShow = function() {window.removeEventListener('click', fnH); //(可能连续fShow, 先del old hook)fw.show(id);window.setTimeout(function() { // add hook, 需异步window.addEventListener('click', fnH);}, 100);};ele.fHide = fnH;};// 显示flyout.function showFlyout(fly, evt, dx, dy) {var v = evt.pageX - ct.offsetLeft + ct.scrollLeft + dx;if (v < 1) v = 1;fly.style.left = v+"px";v = evt.pageY - ct.offsetTop + ct.scrollTop + dy;if (v < 1) v = 1;fly.style.top = v+"px";fly.fShow();// 消除VScrollif (ct.scrollHeight > ct.clientHeight) {var oldV = ct.style.minHeight;ct.style.minHeight =(ct.scrollHeight + (ct.offsetHeight - 2 - ct.clientHeight) + 0)+"px";var oldOnH = fly.onHide;fly.onHide = function() {ct.style.minHeight = oldV;fly.onHide = oldOnH;oldOnH();};} else {// ct.style.minHeight = "465px"; // 写死}}</script>
</head>
<body><a href="xxx">another link</a><p></p><style>.ct {border: 1px solid; min-height: 200px; overflow-x: auto;position: relative;}#ov {width: 110%; border: 1px solid blue;}#f2 {white-space: nowrap; height: 50px;}</style><div id="ct" class="ct">A brown fox quickly jump over a fence.<br><div id="ov">overflow content</div><p></p><button onclick="ov.innerText += ov.innerText;">Add ov</button><p></p><button onclick="showFlyout(f1, event, 0, 0);">Show f1</button><button id="btn2" onclick="showFlyout(f2, event, 40, 20);">Show f2</button><div id="f1" class="_flyout hide">flyout content: f1</div><script> fw.decorateFlyout("f1"); </script><div id="f2" class="_flyout hide">flyout content: f2</div><script> fw.decorateFlyout("f2"); </script></div><!-- ct --></body>
</html>

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

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

相关文章

数据结构与算法01 稀疏数组

稀疏数组问题 当一个二维数组中大部分数据都是0&#xff0c;对这个数组直接进行存储会很浪费空间&#xff0c;因此利用稀疏数组进行压缩&#xff0c;稀疏数组第一行的第一个元素是原二维数组行数。&#xff0c;第一行的第二个元素是原二维数组的列数&#xff0c;如图为11行11列…

6.S081——虚拟内存部分——xv6源码完全解析系列(4)

0.briefly speaking 点击跳转到上一篇博客 好&#xff0c;现在进入下一个话题&#xff0c;就是物理内存分配器(kernel/kalloc.c)。在简单介绍完内核态的物理内存分配器之后&#xff0c;之后简单带过一下两个头文件riscv.h和memorylayout.h这两个头文件&#xff0c;因为它们都…

2.5d风格的游戏模式如何制作

文章目录一、 介绍二、 绘制瓦片地图三、 添加场景物体&#xff0c;添加碰撞器四、 创建玩家五、 创建玩家动画六、 玩家脚本七、 2d转换成2.5d八、 “Q”键向左转动视角、“E”键向右转动视角九、 下载工程文件一、 介绍 制作一个类似饥荒风格的2.5d游戏模板。 2.5D游戏是指以…

表id自增的方法

数据库主键id自增的方法&#xff0c;列举了几种如下 一、数据库自增&#xff08;部分数据库支持&#xff09; 创建表的时候设置id自增即可&#xff0c;或者后期修改表id自增 # mysql 语法 create table your_table_name(id bigint(20) not null auto_increment primary key …

Markdown 语法大全

Markdown是一种轻量级标记语言&#xff0c;常用于撰写博客、文档、论文等。它可以让你使用易读易写的纯文本格式来编写文档&#xff0c;然后通过转换成有效的HTML文档进行发布。以下是Markdown常用的语法&#xff1a; 这里写目录标题标题列表引用一级引用嵌套引用粗体和斜体删除…

Java集合——Set接口学习总结

一、HashSet实现类 1.常用方法 增加&#xff1a;add(E e)删除&#xff1a;remove(Object o)、clear()修改&#xff1a;查看&#xff1a;iterator()判断&#xff1a;contains(Object o)、isEmpty()常用遍历方式&#xff1a;Set<String> set new HashSet<String>()…

Spark 对hadoopnamenode-log文件进行数据清洗并存入mysql数据库

一.查找需要清洗的文件 1.1查看hadoopnamenode-log文件位置 1.2 开启Hadoop集群和Hive元数据、Hive远程连接 具体如何开启可以看我之前的文章&#xff1a;(10条消息) SparkSQL-liunx系统Spark连接Hive_难以言喻wyy的博客-CSDN博客 1.3 将这个文件传入到hdfs中&#xff1a; hd…

windows系统管理_windows server 2016 用户管理

用户账户的概述 **计算机用户账户&#xff1a;**由将用户定义到某一系统的所有信息组成的记录,账户为用户或计算机提供安 全凭证&#xff0c;包括用户名和用户登陆所需要的密码&#xff0c;以及用户使用以便用户和计算机能够登录到网络并 访问域资源的权利和权限。不同的身份拥…

【Obsidian】基础使用手册(包括如何将Obsidian页面设置为中文)

&#x1f497; 未来的游戏开发程序媛&#xff0c;现在的努力学习菜鸡 &#x1f4a6;本专栏是我关于工具类软件的笔记 &#x1f236;本篇是Obsidian的基础使用 Obsidian的基础使用将页面设置为中文常用的默认快捷键常用的格式标题代码块表格字体样式列表任务列表官方下载地址&am…

【音视频第11天】GCC论文阅读(2)

A Google Congestion Control Algorithm for Real-Time Communication draft-alvestrand-rmcat-congestion-03论文理解 看中文的GCC算法一脸懵。看一看英文版的&#xff0c;找一找感觉。 目录Abstract1. Introduction1.1 Mathematical notation conventions2. System model3.Fe…

获取淘宝商品分类详情API,抓取淘宝全品类目API接口分享(代码展示、参数说明)

商品分类技巧 淘宝店铺分类怎么设置&#xff1f;我们登录卖家账号的时候&#xff0c;我们看到自己的商品&#xff0c;会想要给商品进行分类&#xff0c;一个好的分类可以帮助提高商品的曝光率。那么在给商品分类前&#xff0c;如果您毫无头绪&#xff0c;以下几点可以给您带来…

车载网络 - Autosar网络管理 - 网络管理简介

一、什么是CAN网络管理及它的作用 现在的车辆是由大量的ECU节点组成的&#xff0c;为了能使各ECU能够正确并及时地进行CAN通信&#xff0c;需要有一套机制来统一协调总线上各节点的休眠唤醒&#xff0c;这套机制就是CAN网络管理&#xff08;NM&#xff09;。 网络管理的目的是保…

【算法题解】24. 模拟机器人行走

这是一道 中等难度 的题 https://leetcode.cn/problems/walking-robot-simulation/description/ 题目 机器人在一个无限大小的 XY 网格平面上行走&#xff0c;从点 (0, 0) 处开始出发&#xff0c;面向北方。该机器人可以接收以下三种类型的命令 commands &#xff1a; -2 &am…

WPF mvvm框架Stylet使用教程-基础用法

Stylet框架基础用法 安装Nuget包 在“管理Nuget程序包”中搜索Stylet&#xff0c;查看Stylet包支持的net版本&#xff0c;然后选择第二个Stylet.Start包进行安装&#xff0c;该包会自动安装stylet并且生成基本的配置 注意事项&#xff1a;安装时要把需要安装的程序设为启动项…

PyCharm2021安装教程

PyCharm是一种Python IDE&#xff0c;带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具&#xff0c;比如调试、语法高亮、Project管理、代码跳转、智能提示、自动完成、单元测试、版本控制。此外&#xff0c;该IDE提供了一些高级功能&#xff0c;以用于支持Djang…

IntersectionObserver与无限滚动加载

学习链接 IntersectionObserver MDN Api IntersectionObserver API详解 Intersection observer 的概念和用法 过去&#xff0c;要检测一个元素是否可见或者两个元素是否相交并不容易&#xff0c;比如实现图片懒加载、内容无限滚动等功能时&#xff0c;都需要通过​getBound…

[Date structure]时间/空间复杂度

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现&#xff0c;有时候有C/C代码。 ⭐如果觉得文章写的…

linux文件类型和根目录结构

目录 一、Linux文件类型 二、Linux系统的目录结构 1. FHS 2. 路径以及工作目录 &#xff08;1&#xff09;路径 &#xff08;2&#xff09;工作目录 一、Linux文件类型 使用ls -l命令查看到的第一个字符文件类型说明-普通文件类似于Windows的记事本d目录文件类似于Windo…

[NOIP2000 提高组] 进制转换

[NOIP2000 提高组] 进制转换 题目描述 我们可以用这样的方式来表示一个十进制数: 将每个阿拉伯数字乘以一个以该数字所处位置为指数,以 10为底数的幂之和的形式。例如 123 可表示为 10^22*10^13*10^0 这样的形式。 与之相似的&#xff0c;对二进制数来说&#xff0c;也可表示成…

WordPress添加阿里云OSS对象云储存配置教程

背景&#xff1a;随着页面文章增多&#xff0c;内置图片存储拖连网站响应速度&#xff0c;这里对我来说主要是想提升速度 目的&#xff1a;使用第三方云存储作为图片外存储(图床)&#xff0c;这样处理可以为服务器节省很多磁盘空间&#xff0c;在网站搬家的时候减少文件迁移的工…