尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
前端教学5:视口 vs 文档 —— 滚动改变不了视口大小
!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title案例四视口 vs 文档 —— 滚动改变不了窗户大小/title style /* 教学要点视口 窗户可见区域文档 窗外风景可滚动范围 1. window.innerWidth / innerHeight → 视口尺寸媒体查询看的就是它 2. document.documentElement.clientWidth → 视口宽度不含滚动条 3. 两者之差 滚动条宽度Windows 上约 17px 4. document.documentElement.scrollHeight → 文档总高度可滚动范围 5. 滚动只改变 scrollY风景的位置永远不改变视口尺寸 6. 100vh 视口高度不是文档高度 演示方法滚动页面 → 看顶部数据板视口三项纹丝不动 只有滚动三项在变缩放窗口 → 视口三项才会变。 移动端注意地址栏收起/展开会改变 innerHeight属正常现象 */ * { margin: 0; padding: 0; box-sizing: border-box; } :root { --ink: #111; --ink-2: #777; --line: #e5e5e5; --accent: #2b6cff; /* 蓝色 与视口有关的量 */ --accent-2: #ff4d00; /* 橙色 与滚动有关的量 */ } body { font-family: PingFang SC, Microsoft YaHei, Helvetica Neue, sans-serif; color: var(--ink); line-height: 1.6; -webkit-font-smoothing: antialiased; /* ★ 用文档总高远大于视口才能演示滚动 */ } /* ---------- 顶部数据板固定悬浮滚动时始终可见 ---------- */ .hud { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(255, 255, 255, .96); border-bottom: 1px solid var(--line); padding: 10px 16px; display: flex; flex-wrap: wrap; gap: 8px 20px; font-size: 12px; backdrop-filter: blur(6px); } .hud .chip { display: flex; flex-direction: column; min-width: 88px; } .hud .chip b { font-size: 16px; font-variant-numeric: tabular-nums; /* 数字等宽跳动时不抖 */ font-weight: 600; } .hud .chip span { color: var(--ink-2); } .hud .v b { color: var(--accent); } /* 蓝色 视口量 */ .hud .s b { color: var(--accent-2); } /* 橙色 滚动量 */ /* 滚动进度条贴在数据板下沿 */ .progress { position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 101; background: transparent; } .progress i { display: block; height: 100%; width: 0; background: var(--accent-2); } main { padding-top: 96px; } /* 给固定数据板让位 */ section { padding: 0 24px; max-width: 900px; margin: 0 auto; } /* ---------- 说明区 ---------- */ .intro { padding-top: 48px; } .intro h1 { font-size: 32px; font-weight: 700; line-height: 1.3; margin-bottom: 16px; } .intro p { font-size: 16px; color: #444; max-width: 38em; margin-bottom: 1em; } .intro code { font-family: Consolas, Monaco, monospace; font-size: 14px; background: #f4f4f4; border-radius: 4px; padding: 2px 6px; } .intro .console { background: #f7f7f7; border: 1px solid var(--line); border-radius: 8px; padding: 16px 20px; margin: 24px 0 48px; font-family: Consolas, Monaco, monospace; font-size: 14px; line-height: 2; } .intro .console .c1 { color: var(--accent); } .intro .console .c2 { color: var(--accent-2); } /* ---------- 四个满屏区块制造可滚动距离 ---------- */ .screen { max-width: none; min-height: 100vh; /* 每屏正好一个视口高 */ display: flex; flex-direction: column; justify-content: center; padding: 0 48px; border-bottom: 1px dashed var(--line); transition: background .3s; } .screen:nth-child(odd) { background: #fafafa; } .screen:nth-child(even) { background: #fff; } /* 当前所在屏高亮由 JS 切换 */ .screen.active { box-shadow: inset 4px 0 0 var(--accent); } .screen .no { font-size: 15px; letter-spacing: 4px; color: var(--ink-2); margin-bottom: 8px; } .screen h2 { font-size: 36px; font-weight: 700; line-height: 1.2; max-width: 14em; } .screen p { margin-top: 12px; color: #555; max-width: 34em; font-size: 15px; } /* ---------- 100vh 演示盒 ---------- */ .vh-box { max-width: 900px; margin: 96px auto; height: 100vh; /* ★ 高度100vh等于视口高度 */ border: 3px dashed var(--accent); border-radius: 12px; display: flex; flex-direction: column; justify-content: center; padding: 48px; } .vh-box h2 { font-size: 28px; margin-bottom: 12px; } .vh-box p { color: #555; max-width: 34em; } /* ---------- 底部标记 ---------- */ .bottom-mark { max-width: 900px; margin: 0 auto; padding: 64px 24px 120px; text-align: center; } .bottom-mark .flag { display: inline-block; font-size: 14px; letter-spacing: 2px; color: var(--accent-2); border: 1px solid var(--accent-2); border-radius: 999px; padding: 8px 24px; margin-bottom: 16px; opacity: .35; /* 滚到底部才点亮JS 控制 */ transition: opacity .4s; } .bottom-mark .flag.on { opacity: 1; } .bottom-mark p { color: var(--ink-2); font-size: 14px; } .bottom-mark .formula { font-family: Consolas, Monaco, monospace; font-size: 15px; color: var(--ink); margin-top: 8px; } /* ---------- 右侧文档全景图迷你地图 ---------- */ .minimap { position: fixed; right: 20px; top: 50%; transform: translateY(-50%); width: 10px; height: 40vh; /* 迷你地图代表整个文档 */ background: #eee; border-radius: 5px; z-index: 100; } .minimap i { position: absolute; left: 0; width: 100%; background: var(--accent); /* 蓝色窗口 视口在文档中的位置 */ border-radius: 5px; opacity: .85; } .minimap-label { position: fixed; right: 38px; top: calc(50% 20vh 12px); transform: translateY(-100%); font-size: 11px; color: var(--ink-2); writing-mode: vertical-rl; letter-spacing: 2px; z-index: 100; } media (max-width: 900px) { .minimap, .minimap-label { display: none; } .screen { padding: 0 24px; } .screen h2 { font-size: 28px; } } /style /head body !-- 滚动进度条 -- div classprogressi idbar/i/div !-- 实时数据板 -- div classhud div classchip vb idm-vw–/bspan视口宽度 innerWidth/span/div div classchip vb idm-cw–/bspan客户区宽度(不含滚动条)/span/div div classchip vb idm-sbw–/bspan滚动条宽度 二者之差/span/div div classchip vb idm-vh–/bspan视口高度 innerHeight/span/div div classchip sb idm-sh–/bspan文档总高 scrollHeight/span/div div classchip sb idm-sy–/bspan已滚动 scrollY/span/div div classchip sb idm-rem–/bspan剩余可滚动/span/div div classchipb idm-pg–/bspan滚动进度 %/span/div div classchipb idm-sc–/bspan当前所在屏/span/div /div main section classintro h1窗户与风景视口 ≠ 文档/h1 p这个页面很长足够你滚动四屏多。请盯着顶部数据板b stylecolor:var(--accent)蓝色数字是窗户视口/bb stylecolor:var(--accent-2)橙色数字是风景的位置滚动状态/b。/p pb滚动页面/b只有橙色数字在变视口尺寸纹丝不动 —— 媒体查询判断的正是那三个蓝色量所以滚多远永远不影响 codemedia (max-width: 768px)/code 的结果。/p pb缩放浏览器窗口/b这次蓝色数字变了 —— 视口尺寸只有窗口变化时才会变。/p div classconsole span classc1window.innerWidth / innerHeight/spannbsp;nbsp;// 视口尺寸媒体查询看的就是它br span classc2document.documentElement.scrollHeight/spannbsp;nbsp;// 文档总高度可滚动范围br span stylecolor:#999document.documentElement.clientWidth/spannbsp;nbsp;// 不含滚动条的视口宽度与 innerWidth 之差 滚动条宽度 /div /section section classscreen ids1 div classnoSCREEN 1 / 4/div h2第 1 屏此刻你看到的就是视口的全部/h2 p往右看那条竖直的迷你地图蓝色滑块代表视口它此刻贴着文档顶端。滚动时移动的是滑块风景位置而不是地图本身文档。/p /section section classscreen ids2 div classnoSCREEN 2 / 4/div h2第 2 屏已滚过一整屏视口还是原来的大小/h2 p回看顶部数据板innerWidth / innerHeight 与第 1 屏完全相同。变化的只有 scrollY —— 它记录的是风景被窗框移过了多远。/p /section section classscreen ids3 div classnoSCREEN 3 / 4/div h2第 3 屏100vh 也只是窗户的高度/h2 p页面再往下的虚线盒子 CSS 高度是 100vh —— 它跟着窗口高度走而不是跟着文档总高走。窗口多高它就多高。/p /section section classscreen ids4 div classnoSCREEN 4 / 4/div h2第 4 屏快到头了/h2 p注意剩余可滚动这个数字在变小。当它变成 0 时页脚的橙色徽章会点亮并且满足等式scrollY innerHeight scrollHeight。/p /section div classvh-box h2这个盒子height: 100vh/h2 p它的高度永远等于b stylecolor:var(--accent)视口高度/b和文档总高度无关。试着把浏览器窗口拉高/压矮再回来量它 —— 它始终和窗口一样高。/p /div div classbottom-mark span classflag idflag已到达文档底部/span p此刻恒等式成立/p p classformula idformulascrollY innerHeight scrollHeight/p /div /main !-- 文档全景迷你地图蓝窗 视口灰条 整个文档 -- div classminimap idmapi idmapWin/i/div span classminimap-label文档全景/span script const $ id document.getElementById(id); const screens document.querySelectorAll(.screen); function update() { const vw window.innerWidth; // 视口宽度含滚动条 const vh window.innerHeight; // 视口高度 const cw document.documentElement.clientWidth; // 视口宽度不含滚动条 const sh document.documentElement.scrollHeight; // 文档总高度 const sy window.scrollY; // 已滚动距离 const rem Math.max(0, sh - vh - sy); // 剩余可滚动 const max Math.max(1, sh - vh); // 最大可滚动范围 const pct (sy / max * 100).toFixed(1); // 数据板 $(m-vw).textContent vw px; $(m-cw).textContent cw px; $(m-sbw).textContent (vw - cw) px; // Windows 上约 17px $(m-vh).textContent vh px; $(m-sh).textContent sh px; $(m-sy).textContent sy px; $(m-rem).textContent rem px; $(m-pg).textContent pct %; // 当前所在屏取视口中线落在哪个区块内 let current –; screens.forEach(s { const r s.getBoundingClientRect(); const hit r.top vh / 2 r.bottom vh / 2; s.classList.toggle(active, hit); if (hit) current s.id.replace(s, 第 ) 屏; }); $(m-sc).textContent current; // 进度条 底部徽章 等式实况 $(bar).style.width pct %; $(flag).classList.toggle(on, rem 0); $(formula).textContent sy vh sh (rem 0 ? ✓ : ); // 迷你地图蓝窗高度/位置 视口占文档的比例 const win $(mapWin); win.style.height (vh / sh * 100) %; win.style.top (sy / sh * 100) %; } window.addEventListener(scroll, update, { passive: true }); window.addEventListener(resize, update); update(); /script /body /html
RELATED

相关推荐

微信小程序学习笔记|案例 3.10 文件引用 include 标签

微信小程序学习笔记|案例 3.10 文件引用 include 标签

一、案例简介 本案例学习小程序 include 文件引入语法,把页面拆分成多个独立 wxml 片段(头部标题、正文、页脚、拓展阅读),实现代码复用。 课后扩展需求:增加两个按钮,控制拓展阅读内容的显示与隐藏。 二、…

📅 2026/10/26 1:54:07
和小马一起学Python(五)——字符串编码转换、字符串运算符、字符串常见内建函数

和小马一起学Python(五)——字符串编码转换、字符串运算符、字符串常见内建函数

01 字符串编码转换字符串编码的本质可以看作是二进制数据与语言文字之间的一种映射或对应关系。它是将字符(不仅仅是语言文字,还包括标点符号、控制字符等)转换成一种标准化的二进制表示形式,以便计算机能够存储、处理和传输这些信息。1.1 编码格式&…

📅 2026/10/26 1:54:07
5个信号识别垃圾账号:anti-slop用户检查如何过滤AI slop贡献者

5个信号识别垃圾账号:anti-slop用户检查如何过滤AI slop贡献者

【免费下载链接】anti-slop A GitHub action that detects and automatically closes low-quality and AI slop PRs. 项目地址: https://gitcode.com/gh_mirrors/an/anti-slop 点击查看 免费下载 开源维护者正被大量低质量 PR 淹没,其中不少是 AI 自动生…

📅 2026/10/26 1:49:07
MORE NEWS

更多资讯

📰

多模态大模型中的System 2认知架构与工程实践

1. 多模态大模型中的System 2认知架构解析当GPT-4V能够看着一张破损自行车图片说"需要更换链条"时,它真的理解机械原理吗?这触及了当前多模态大模型最关键的认知鸿沟——感知能力(System 1)与真实推理(Syste…

📰

AI驱动学术写作:智能文献管理6大核心技术解析

1. 学术写作的智能化转型契机去年帮一位博士生修改论文时,我发现他花在文献整理上的时间竟占总工时的40%。这绝非个例——Nature最新调研显示,科研人员平均每周要耗费7.2小时处理文献引用。当传统EndNote遇上GPT-4时代,我们终于迎来了革命性的…

📰

C语言插入排序算法:从考古到现代化修复

1. 项目概述:41号代码的考古与新生在某个尘封的代码仓库角落,编号41的C语言文件静静躺了十余年。当我用现代编译器打开这份实现插入排序的代码时,满屏的warning如同考古现场的警示带——这不是一份能直接运行的代码,而是一个时代的…

📰

从90年代C语言插入排序代码看算法优化与编程规范演进

1. 项目背景与核心价值这个41号代码案例来自某高校计算机系90年代的算法实验课存档,是一份典型的早期C语言插入排序实现。我在整理旧资料时发现这份代码虽然能编译通过,但存在几处隐蔽的逻辑错误和性能缺陷。作为早期C语言教学的活化石,修复并…

📰

多模态大模型中的System 2推理机制与实现

1. 认知架构的本质与System 2推理的定位认知架构在多模态大模型中扮演着"操作系统"的角色,它决定了模型如何处理和整合来自不同模态的信息。System 2推理作为认知架构中的高级处理模块,与人类大脑的慢思考系统相对应,负责需要集中注…

📰

2026年easyread阅读工具深度解析:正文提取与本地排版的生存逻辑

2026年还在折腾easyread这类阅读辅助工具的人,多少有点“非主流”了。浏览器自带的阅读模式越来越普及,内容平台也都在拼命优化自己的沉浸式排版,按理说这玩意儿早该被淘汰了。但事实是,在技术社区、独立博客、还有不少研究型用户…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬