// ==UserScript== // @name 哔哩哔哩电脑网页版手机端适配优化脚本简洁版 // @namespace http://tampermonkey.net/ // @version 132.88 // @description B站电脑版网页手机端适配(CSS 为主,JS 只补 CSS 做不到的事)。通用:隐藏 PC 头部/侧栏/页脚,内置 52px 搜索栏(综合·直播·专栏·用户互切),全站两列卡片,禁止横向溢出。搜索页:+必含 / -排除 屏蔽语法并显示过滤条数,翻页自动回顶。视频·播放列表·番剧页:播放器 16:9 满宽、控制栏手机化、右滑呼出侧边列表、网页全屏钉回播放器内。动态·话题·opus:卡片两列、评论区对齐视频页。UP主空间:两列、信息卡屏内、关注/粉丝页单列。直播间:只留播放器与聊天区,双击进出原生全屏,返回键先退全屏、控制栏精简。另含看图器、登录弹层、音乐与专栏页适配。 // @author YourName // @match *://*.bilibili.com/* // @grant none // @run-at document-start // @license All Rights Reserved // ==/UserScript== (function () { 'use strict'; /* ============================================================ 变更记录(精简后移到这里,@description 只留功能简介) ---- 用户搜索页每页卡片数回归官方 24 张(同一病,v132.88)---- 现象:用户垂搜页(/upuser)官方 24 个用户卡,开脚本后一页变 36 个, 翻到第 2 页重复 12 个 —— 与 v132.87 视频页 24/42 完全同源。 探针实测对比(用户页数量开了脚本36个.txt vs 用户页数量不开脚本24个.txt): · 开脚本(360):24 个 .col_6.col_md_4.mb_x60 [344x86] + 12 个 .to_hide_md.col_6.col_md_4.mb_x60 [344x86](第 25~36 项) · 不开脚本(1100):只有 24 个 .col_6.col_md_4 [502x86], 第 25~36 项的 .to_hide_md 全部 0x0(站内响应式隐藏生效) 即用户页同样是「前 24 本页 + 后 12 预渲染占位」,靠 to_hide_md 隐藏。 真因:与视频页不是同一条规则。视频页的凶手是「列表项两列」那组, 用户页的凶手是「番剧区统一边距」那组里的 .media-list [class*="col_6"] / [class*="col_md_4"] / .mb_x40 → display:block !important + width:100% + mb:16px, 把 12 个 to_hide_md 一起放了出来(实测 mb=16 / width 100% 与之一一吻合)。 v132.87 那几条隐藏规则写的是 [class*="video-list"],而用户页容器叫 .media-list.row.mt_x40,一个字都对不上,所以上一版完全没覆盖到这里。 修法: ① 源头排除 —— 那组显示规则的每个选择器末尾加 :not([class*="to_hide"]), 占位项根本不吃这条规则,回落到站内的隐藏(比事后隐藏更干净); ② 双保险 —— v132.87 的隐藏组扩两个泛化选择器: [class*="-list"] > div[class*="to_hide"] [class*="-list"] div[class*="to_hide"][class*="col_"] (特异性 (1,4,2), 稳压显示规则的 (1,3,3),不依赖书写顺序) 用 -list 泛化容器名,media-list / video-list / card-list 一次覆盖, 站内以后再换列表容器类名也不会再漏。 ③ JS 兜底 —— searchToHideSweep 的选择器从 '.video-list [class*="to_hide"]' 换成 '[class*="-list"] [class*="to_hide"]',用户页 / 番剧页一并纳入。 ④ 附带修 —— 占位卡隐藏后,用户页「最后一个卡片 mb→0」那条 CSS 里的 :last-child 落到了隐藏项上(DOM 里 to_hide 排在末尾),真正可见的 最后一张卡没被归零 → 底部凭空多 16px(v131.71 调过的 32 vs 8 复现)。 CSS 表达不了「最后一个不含某类的子元素」,新增 searchListTailPad 按「最后一个 visible 子项」用 JS 补一次,只在该列表存在时跑。 改后:用户页每页回到官方 24 张,翻页不再重复,底部间距仍是 8px。 未改:用户卡 344x86 排版(头像 86 + 间距 15 + 内容 243)、分页条逻辑。 ---- 搜索页每页卡片数回归官方 24 张(去掉下一页预渲染占位)(v132.87)---- 现象:官方搜索页每页 24 个视频卡,开脚本后一页变 42 个, 翻到第 2 页就与前页重复 18 个。 探针实测(开脚本的搜索页.txt,视口 360):42 个 .bili-video-card 全部 [168x172] 正常显示;按父项类名分两拨 —— 前 24 个:div.video-list-item.col_3.col_xs_1_5.col_md_2.col_xl_1_7.mb_x40 后 18 个:div.to_hide_xs.video-list-item.col_3.col_xs_1_5.col_md_2.col_xl_1_7 (nth-of-type 从第 25 项起,类名与不开脚本时完全一致) 即:站内 SSR 一次吐 42 个项,前 24 个是本页,后 18 个是「下一页预渲染占位」, 靠 to_hide_xs 这个响应式类在窄屏下隐藏为 0x0(不开脚本时确实一个都不显示)。 真因:脚本「搜索页列表项两列」那组选择器里写着 .video-list.row > .to_hide_xs / .to_hide_md / .to_hide_xl, 连同 .col_3 / .col_xs_1_5 / .col_md_2 一起 display:block !important + width:100% !important + flex:1 1 100% —— 等于替站内把这 18 个占位卡 放了出来,每页凭空多 18 张;而翻页时站内仍按每页 24 的偏移取数据, 于是第 2 页的前 18 张正好是第 1 页的后 18 张 → 看到重复。 修法:① 那组选择器删掉 .to_hide_xs / _md / _xl 三条,不再强行 block; ② 补一条显式隐藏:.video-list.row > div[class*="to_hide"](含双属性 [class*="video-list-item"] 版本提高特异性)→ display:none + 尺寸 / 边距 / 定位全归零,并顺带隐藏其内的 .bili-video-card / __wrap(防 absolute 逃逸); 特异性比 div[class*="col_"] 那条多一层,不依赖书写顺序。 ③ JS 兜底 searchToHideSweep:换页 / 无限滚动时若站内用行内 !important 重新插入或改写 → setProperty important 反写;computed display 已是 none 则零写入,页面上无 to_hide 元素时一次 querySelector 即返回(注册在统一 DOM 守卫里,非搜索页每次只花一次查询)。 改后:每页回到官方 24 张,翻页不再重复。 未改:卡片本身的 168x172 排版、两列、分页条、过滤(+必含 / -排除)逻辑。 ---- 空间内搜索页(route_search)分页条上下间距统一 16px(v132.86)---- 需求:UP 主空间页 → 搜索结果页,底部「上一页 数字 下一页」上下间距改 16px。 探针实测(bili_probe_2026-10-03_13-38-05,视口 360,已滚到底): .search-content [360x1869] top=-1069 bottom=**800** pb=**16** └─ .video [344x1853] top=-1069 bottom=**784** ├─ .video-body [344x1787] bottom=**718** ← 末行卡片底 └─ .video-footer[344x**66**] top=718 bottom=784 pt=16 pb=16 └─ .vui_pagenation [344x34] └─ --btns [344x34] top=**734** bottom=**768** ← 按键本体 算现状: 上间距 = 按键顶 734 − 末行卡片底 718 = **16** ✓(本来就是对的) 下间距 = 按键底 768 → .video 底 784 = 16 再 + .search-content 的 pb 16 → 内容真正结束在 **800** = **32** 多出来的 16 是脚本自己 v131.20 那条 .search-content { padding: 0 8px 16px 8px } 的底部留白, 与 footer 自己的 pb 16 **叠加**了 —— 这就是"下边比上边宽一倍"。 修法(沿用 v132.77:间距收归一处,不再分摊): ① .video-footer 显式 padding:16px 0 + margin:0 —— 上下间距由它一个数决定; ② .search-content 的 padding-bottom 归零 —— 去掉叠加的那 16; ③ footer 内分页器的 padding 归零 —— 防站内哪天又给它 padding,把 16 顶成 24。 改后:按键 734~768,.video 底 784,.search-content 底 784 → 上 16、下 16。 ⚠ 只作用于 route_search(空间内搜索,main.space-main.route_search), 不动全站搜索页(body.bili-search-page .search-content 是另一套,别处已验证)。 JS 兜底扩展 v132.77 的 spaceVideoFooterPad:原来只归零父级 div.video 的 pb, 而本次多出的 16 在**再往上一层的 .search-content**,故改成沿祖先链向上找 (最多 4 层),命中 search-content / upload-content / space-list-details 就归零 padding-bottom —— v132.77 的投稿页与本次搜索页一并受益。 ---- 直播卡 UP 主行位置对齐视频卡(v132.85)---- 需求:直播卡(如「直播中 准留子断供被迫卖艺 / 简Gene」)的 UP 主那一行, 位置比视频卡的 UP 主行高一点 —— 改成完全一致。 探针实测(bili_probe_2026-10-03_12-28-50,首页同行左右对照,两卡 top 均 363): 左·视频卡 右·直播卡(改前) __info [168x77] top=458 pt=8 __info [168x77] top=458 pt=**6** └─ __info--right [152x**61**] └─ __info--text [152x**63**] top=466 top=464 ├─ tit 466~502 (36) ├─ tit 464~500 (36) └─ bottom **506**~527 (21) mt=4 └─ uname **504**~525 (21) mt=4 即:直播卡 UP 主行 504 vs 视频卡 506,**高了 2px**(相对卡片顶 141 vs 143)。 这 2px 正是 v132.83 为凑「6+63+8=77」把上边距从 8 收到 6 造成的 —— 当时为了让 172 的高度塞得下,牺牲了行的位置。 内容与视频卡其实一模一样(tit 36 + mt 4 + 行 21 = 61),直播卡 text 之所以是 63,是 `` 按 inline 排、基线下方自带的 2px 降部空白(uname 底 525、 text 底 527)。所以这次**从底部回收那 2px,不动上边距**: ① __info padding-top 改回 8(与视频卡一致); ② __info--text 定高 61 —— 实测 uname 底 525 = text 顶 464 + 61,正好贴底, 一行不裁;uname 补 white-space:nowrap 防名字换行撑高; ③ __info--uname 改 display:block + line-height 21 —— 消掉 inline 基线留白, 从根上去掉那 2px,不单靠裁切。 改后:text 顶 = 458+8 = 466,tit 466~502,uname 506~527 —— 与视频卡的 right / tit / bottom **逐像素重合**(视频卡 bottom 正是 506~527)。 JS 兜底同步改(padding-top 6→8,并补 text 定高 61 + uname block), 另加第 ④ 段几何复核:以**同页视频卡的 UP 主行相对卡片顶的偏移**为基准 (实测 143 = 95 封面 + 8 + 36 + 4),量到差 1px 以上就按差值反推上内边距、 并从文字区底部回收,卡片总高不变。基准不写死 —— 视频卡将来改了会跟着走。 未改:卡片总高 172、封面 95、标题区 36、三点隐藏 —— 全部保持 v132.83/84。 ---- 修 v132.83 回归:直播卡内容整块被推到卡片下方只露一条边(v132.84)---- 现象(用户反馈):「月幕对决打假赛?」右侧那张直播卡,内容只在卡片内下方 显示一小行。 新探针实测(bili_probe_2026-10-03_12-17-37,首页,左右同一行对照): 左·视频卡(正常) 右·直播卡(异常) .bili-video-card [168x172] top=251 .bili-live-card [168x172] top=251 └─ __wrap [168x172] top=**251** └─ __wrap [168x172] top=**390** ├─ image--link 251~346 ├─ image--link 390~484 └─ __info 346~423 └─ __info 484~561 卡片 172 高(v132.83 的高度修正已生效 ✓)、wrap 也是 172, 但 wrap 比卡片**低了 139px**(第二张直播卡低 138px,同样现象)。 卡片 overflow:hidden,于是只有 390~423 这 33px 落在卡片里 —— 正是用户看到的"卡内下方只显示一小行"。 ⚠ 139 不是巧合:v132.83 之前卡片的旧高度就是 139。 说明**卡片里还藏着一个 139 高的块级元素排在 wrap 前面**。证据在探针里: wrap 的 path 是 '…__wrap:nth-of-type(2)',即它前面还有一个 div 兄弟; 而探针只输出带类名的节点(全文 0 个无类名 div 被输出),所以这个兄弟 查不到,只能靠几何量反推出来 —— 它是个**无类名 div**(占位 / 懒渲染壳)。 为什么 v132.83 之前没事:那时 wrap 是 absolute(脱离文档流), 这个兄弟独占一行、把卡片高度撑成 139,肉眼看着正常; 一旦把 wrap 改回 static,它就乖乖排到那个兄弟**后面** → 下移 139。 修法:把卡片里**除 wrap 以外的 div 兄弟全部隐藏**(它是无类名占位壳, 内容全在 wrap 里,隐藏零副作用)。隐藏后 wrap 自然回到卡片顶部(top=251), 卡片高度也由 wrap 撑成 95 + 77 = 172,与视频卡完全一致。 JS 兜底 liveCardFit 用几何量复核(wrap.top − card.top > 2 即判定仍被压), 分两步走,不依赖 CSS 那条是否生效: a) 卡片内除 wrap 外的兄弟全部隐藏(尺寸 / 边距 / 定位归零 + absolute); b) 若仍偏 → 把 wrap 钉成 absolute 贴回卡片左上角 + 卡片补显式高度 (absolute 不撑高父级,高度必须显式给)。 判断全部用 rect 差值,不做任何猜测。 ---- 首页直播卡(如「直播中 左右开弓」):隐藏右侧三点 + 高度对齐 172(v132.83)---- 需求两条:① 隐藏卡片右侧竖排三个点的按键(不感兴趣);② 高度与其它视频卡一致。 探针实测(bili_probe_2026-10-03_12-08-23,首页,视口 360,左右对照): 直播卡(要改的) 视频卡(基准) .bili-live-card [168x**139**] .bili-video-card [168x**172**] └─ __wrap [168x**174**] └─ __wrap [168x172] pos=**static** pos=**absolute** z=1 ├─ __image--link [168x95] ├─ __image--link [168x95] └─ __info [168x**79**] └─ __info [168x**77**] pt=8 pb=8 pt=8 pb=8 └─ __info--text [152x**63**] └─ __info--right [152x**61**] ├─ __info--no-interest [22x21] ← 三点(要隐藏) 【高度不一致的真因】不是文字多少,而是 **__wrap 是 absolute**: absolute 元素脱离文档流、不撑开父级,所以父级 height:auto 时只剩 139 (由 grid 行高等兜出来的),而 wrap 实际内容有 174。 视频卡的 wrap 是 static,自然撑开父级 → 172。 所以第一步是把直播卡的 wrap 改回 static,让它照抄视频卡的结构。 【174 → 172 那 2px】来自 __info:直播卡 79(pt8 + text63 + pb8), 视频卡 77(pt8 + right61 + pb8),差的正是 text 63 vs 61。 把 __info 定高 77px,并把 padding-top 由 8 收到 6:6 + 63 + 8 = 77 ✓ 这样**内容一点都不用压缩**(text 仍是实测的 63,UP 主名底部 421 完整保留), 只让文字整体上移 2px、更靠近封面,视觉无感。 (另一条路是把 text 压成 61,但实测 63 里含 2px 行高空白,定 61 会正好 卡在 421 的边界上,内核取整差 1px 就可能裁到字脚;收 padding 零裁切风险。) 改后:95 + 77 = 172,与视频卡完全一致。 【三点按键】.bili-live-card__info--no-interest [22x21] left=322,压在卡片最右侧 (右缘 344 贴边),点开是「不感兴趣」菜单。视频卡那个同名元素实测是 **0x0** (hover 才显,手机碰不到),只有直播卡这个一直露着,故重点处理直播卡, 视频卡的同名元素顺带一并隐藏(反正它本来就不显示)。 ⚠ 只隐藏按钮本身,**不动 __info--text / __info--right 父容器** —— 里面还装着标题和 UP 主名,整块隐藏会把正文一起干掉。 JS 兜底 liveCardFit 补两件 CSS 做不到的事:① wrap 若被写成行内 !important 的 absolute → 反写 static(这是撑开父级的关键);② 首页无限滚动,新卡片插入后 补一次。基准高度**以同页视频卡的实际高度为准**(不写死 172 —— 万一将来视频卡 高度改了,直播卡会跟着走),量不到才退回 172;差 2px 以内不写,避免无谓写入。 覆盖 home / search / space 三页 —— 直播卡在这三页是同一套 DOM。 ---- 关注 / 粉丝页:隐藏卡片右侧「⋮ 竖三点」按键(v132.82)---- 需求:隐藏每张关注卡片右侧竖排三个点的按键(点开是发私信等菜单)。 探针实测(bili_probe_2026-10-03_12-00-24 与 11-53-04,多张卡完全一致): .relation-card-info-option [235x35] ← 卡片右下那一排 ├─ div.follow-btn(nth 1) ← 关注 / 已关注按钮(保留) │ └─ div.menu-popover [235x35] └─ div.menu-popover:nth-of-type(2) [36x25] left=308 └─ i.vui_icon.sic-BDC-more_vertical_fill.icon [24x25] left=320 这个 36x25 的小块就是竖三点(⋮):手机上用不到,而且它压在卡片最右侧 (left=308、右缘 344 正好贴卡片边),很容易误触。 ⚠ 关键约束:同一层有**两个** div.menu-popover,第一个是关注按钮, 只能藏第二个 —— 写错就会把「已关注」按钮一起干掉。故用三层保险: ① div.menu-popover:nth-of-type(2) —— 按位置直选; ② :has(> i[class*="more_vertical"]) —— 按内部图标特征选 (第 1 个里面装的是 .follow-btn__trigger,不含这个类名,不会命中); ③ 图标本身 i[class*="more_vertical"] 也隐藏 —— 防站内改结构后父容器留下一块 36x25 的空白。 ⚠ 不隐藏 .relation-card-info-option 父容器 —— 里面还有关注按钮, 整块隐藏会连按钮一起没了。 作用域按 .relation-card-info-option 限定:这个类名只在关注 / 粉丝列表卡里 出现,不会误伤其它页面(空间主页的视频卡、动态卡都不是这套 DOM)。 JS 兜底 relationCardMoreHide 补两件事:① 列表异步渲染 + 无限滚动, 新卡片插入后补一次;② 站内若把尺寸写成行内 !important → 反写。 识别同样按「内部含 more_vertical 图标」而不是位置,避免误伤关注按钮; 命中即打 data 标记跳过。三个兜底(aside / 名片 / 三点)已合并成一轮 sweep,另加 scroll 监听(三点随新卡片一起生成),一轮只多几次 querySelector。 未改:关注按钮本身的宽度(199 / 235 两种,由站内按有无三点算出)—— 隐藏三点后按钮宽度不会自动变化,若觉得右侧空得难看可再单独指定。 ---- 关注页:隐藏 5 个分组胶囊 + 隐藏 UP 主名片浮层(v132.81)---- 先确认 v132.80 已生效(bili_probe_2026-10-03_11-53-04): .relation-aside [360x36] top=0 ← 36px 到位 .follow-main [360x1136] top=36 ← 第一个卡片距顶 36 ✓ 【① 隐藏 5 个胶囊】 实测 5 个胶囊是:全部关注12 / 特别关注0 / 悄悄关注0 / 默认分组12 / 我的粉丝1, 全部在 .relation-aside > .vui_collapse 内(4 个在前一个 vui_collapse_item, 「我的粉丝」在第二个),所以直接把 .vui_collapse 整块隐藏,不用逐个点名。 ⚠ 关键:**只藏内容、不动容器** —— v132.80 把 aside 钉成了 36px 高, 这正是"卡片距顶 36px"的实现方式。胶囊没了、aside 还在,那 36px 就变成 一段纯留白,间距自动保持 36;若顺手把 aside 一起隐藏,卡片会顶到屏幕边(top=0)。 【② 隐藏 UP 主名片浮层】 实测 bili-user-profile [360x224] / rect **left=-315** / pos=fixed / z=1004 —— 它是站内"悬停头像弹出的 UP 主信息卡"(PC 的 hover 名片)。手机上没有 hover, 却会被触屏点击触发;弹出后位置由站内按 PC 坐标算,跑到 left=-315, 只在屏幕左缘露出 0~45px 的一条,看着就是"莫名弹出的框"。 ⚠ 注意 z 实测是 1004 而不是 v131.28 那条规则写的 2147483647 —— 说明那条"钉在视口内 top:36px"的既有规则没能压过站内的定位 (站内很可能写在行内且带 !important,作者样式压不过)。 既然定位压不住、而关注页点头像本来就是进主页(名片在此页无用), 直接整块隐藏最干净。 ⚠ 作用域用 :has 只限关注 / 粉丝页 —— 空间动态页等其它页面的名片是 v131.28 特意适配过的有用功能,不能一起干掉。 JS 兜底 followAsideCollapseHide 补三件 CSS 做不到的事:① 胶囊容器若被异步 重新插入 → 补隐藏;② 名片是"点头像才建"的动态浮层,且站内定位疑似行内 !important → setProperty important 反写;③ aside 若被站内一起收掉(高度 ≠36)就补回来。点击后额外补 0/120/400ms 三拍(名片是点击才建的)。 只在关注 / 粉丝页运行,别处一次查询即返回。 未改:列表卡片排版、分页条、胶囊本身的样式规则(仍在,只是被隐藏了)—— 若将来想恢复显示,删掉 .relation-aside .vui_collapse 那一条即可。 ---- 关注页:隐藏「新建分组」与站内搜索框 + 卡片距顶改 36px(v132.80)---- 需求三条:① 隐藏「新建分组」;② 隐藏搜索框;③ 第一个卡片距屏幕顶部 36px。 探针实测(bili_probe_2026-10-03_11-42-51,视口 360x1246,自己的关注页): .relation-aside [360x76] top=0 pt=8 pb=8 └─ .vui_collapse [344x60] top=8 └─ .vui_collapse_item [290x60] → .vui_collapse_item_content [290x60] ├─ .relation-collapse-create [112x52] mb=8 ← 「新建分组」(要隐藏) └─ .vui_sidebar [178x60] ← 分组导航(保留) .relation-content-header [344x34] top=76 ├─ __left [0x34] (空) └─ __right [239x34] → .vui_input.fav-list-header-filter__search [239x34] .items > 第一个 .item [344x86] top=110 算一下:卡片 top = aside 76 + header 34 = 110,目标 36。 关键数字:aside 那 76 里,60 来自 .relation-collapse-create(52 + mb 8)—— 它是 aside 内最高的元素,把整条撑成 8 + 60 + 8 = 76。 所以隐藏它之后 aside 会自己缩到 8 + 32(胶囊高)+ 8 = 48, header 归零后卡片 top = 48,离 36 还差 12。 与其去凑 12 这个数(会随胶囊高度变化而失效),不如直接给 aside 一个 确定高度 36px + 内容垂直居中:胶囊 32 高上下各留 2,卡片 top 稳定是 36, 不依赖任何内部高度计算。 三处改动: ① .relation-collapse-create 整块隐藏(尺寸 / 边距 / 边框归零 + pointer-events:none); ② .relation-content-header 整行隐藏而非只藏搜索框 —— 探针实测 __left 是 0 宽、 整行只装了搜索框,留着就是一条 34px 的空条; ⚠ 这是站内 .fav-list-header-filter__search,与自建 #my-bili-searchbar 无关 (v132.21 / v132.23 已单独处理自建那一条); ③ .relation-aside 钉 height 36px + flex 居中 + overflow hidden。 JS 兜底 followAsideFit 补两件 CSS 压不过的事:aside 高度若被写成行内 !important(作者样式最高优先级)→ setProperty important 反写; 「新建分组」/ 搜索框若被站内异步重新插入 → 补一次隐藏。 全部按实测计算值判断(高度不等于 36、元素仍可见才写),已正确的零写入。 未改:分组导航(全部关注 / 特别关注 / 悄悄关注 / 默认分组)胶囊样式、 列表卡片排版、分页条——一律沿用既有规则。 ---- 动态卡片四角圆角补齐(v132.79)---- 需求两条:① 直播卡左上角 / 左下角是圆角,右上角 / 右下角是直角 → 四角都要圆; ② 投稿卡左下角 / 右下角加上圆角。 探针实测(修改后的直播卡片.txt,v132.78 生效后): 直播卡 a.bili-dyn-card-live [310x259] ovf=**visible** ├─ __header [310x175] → __cover [310x175] → img [310x175] └─ __body [310x84] pt=8 投稿卡 a.bili-dyn-card-video [310x244] ovf=hidden ├─ __header [310x175] → __cover [310x175] → img [310x175] └─ __body [310x69] pt=8 (v132.78 已确认生效:两张卡都变成 310 宽、封面 16:9 在上、文字在下。) 成因:v132.78 之前这两张卡都是**横向**排布(直播卡是左封面右文字), 站内那套圆角也是按横向写的 —— 直播卡封面在左,于是只给左上 / 左下两个角; 投稿卡封面在上,于是只给上方两角。v132.78 把排布改成纵向后, 圆角声明没有跟着改,就留下了"一半圆角一半直角"。 修法(不猜站内具体写成什么,直接把四角重新声明一遍,三层): ① 整卡 border-radius 8px + overflow:hidden —— 总闸,任何子元素的直角都会被 父级裁成圆角,四角一定齐。直播卡原本 ovf=visible,必须补上 (投稿卡本来就是 hidden);顺带写 position:relative,让 overflow 对内部 absolute 角标(直播中 __tag / 时长 duration-time)也能生效。 ② 封面链(__header / __cover / 内层 b-img / img / picture)单独给 8px 8px 0 0, 让封面图自己就是上圆下方,不依赖父级裁剪(部分内核下纯裁剪会留 1px 毛边)。 ③ 文字区 __body 给 0 0 8px 8px,底边两角收圆。 取值 8px:与本脚本既有口径一致 —— .bili-dyn-item 整卡白底 8px、 .opus-card__cover 8px 8px 0 0。改大小只需改这一个数字(三处,行内已标注)。 覆盖范围:空间页与动态页各写一份,逐项一致。纯 CSS,无需 JS 兜底。 ---- 空间页 / 动态页「直播了」卡片改为与「投稿了视频」卡片完全一致(v132.78)---- 需求:up 主空间页「心眼qxy 直播了」那张卡,样式改成与「心眼qxy 两天前 · 投稿了视频」 完全一致。 探针实测(up主空间页探针.txt,main.space-main.route_dynamic,列表第 1 / 2 项): 直播卡 a.bili-dyn-card-live.rcmd [310x132] ├─ __header [236x132] ← 封面只占 236,右边 74 给文字 │ ├─ __tag.state--1 [58x19] absolute「直播中」 │ └─ __cover.b-img [236x132] → picture → img [236x132] └─ __body [74x132] pt=12 pb=10 ← 文字区只有 74 宽 ├─ __title [41x52] ← 标题被挤成多行 └─ __desc [41x18] 投稿卡 a.bili-dyn-card-video [310x244] ├─ __header [310x175] ← 封面占满 310(16:9) │ └─ __cover [310x175] → picture → img [310x175] └─ __body [310x69] pt=8 ← 文字区在封面下方,同样占满 310 ├─ __title [210x21] └─ __desc [78x34] mt=6 差在哪:直播卡是「左封面 + 右文字」的横向排布,投稿卡是「上封面 + 下文字」 的纵向排布 —— 这就是两张卡看起来不一样的原因。 真因:脚本此前只给 -video 写过这套纵向规则,-live 一条都没覆盖, 所以直播卡一直保持站内原生的横向排布(236 + 74)。 修法:把 -video 那套逐项原样套到 -live 上 —— 整卡 flex column + 100% + height auto __header 100% + height auto + flex 0 0 auto __cover 100% + aspect-ratio 16/9(内层的 .b-img / img / picture 同步处理, 保证封面等比放大到 310x175 不变形) __body 100% + flex 1 1 auto + padding 8px 0 0 0(落在封面下方) __desc 补 margin-top 6px(投稿卡 __desc 的 mt 就是 6) 标题 / 简介清边框(含可能画成伪元素的竖条)+ text-align center 标题与简介随即拿到 310 的完整宽度,不再被挤成多行。 保留 __tag「直播中」角标:它是直播状态信息(投稿卡的 duration-time 也是同类 角标,同样压在封面内角),去掉会丢信息;封面变满宽后它随封面一起等比放大、 相对位置不变,不需要额外处理。想要去掉的话,加一条 body.bili-space-page .bili-dyn-card-live__tag { display:none !important } 即可。 覆盖范围:空间页(body.bili-space-page)与动态页(body.bili-dynamic-page) 各写一份,两者规则体逐项一致 —— 直播卡在这两个页面用的是同一套 DOM。 纯 CSS,无需 JS 兜底(与既有 -video 规则一致,实测 CSS 足以生效)。 ---- 空间页投稿→视频:底部分页条上下间距改为 16px(v132.77)---- 需求:底部「上一页 / 数字 / 下一页」那一排,上下各留 16px。 新探针实测(投稿→视频页 route_uploadVideo): .video-footer [344x34] top=758 bottom=792 mt=8 mb=-8 .vui_pagenation--jump [344x34] 与 --btns [344x34] 完全重合(top 都是 758) .upload-content > .video > .video-footer 关键异常:分页器高度是 34 而不是 34+8+8=50 —— 说明 v131.24 那套 「分页器 padding 8px 0」在这份页面里**没生效**,两个盒子完全重合。 于是实际间距只剩:上 = footer mt 8、下 = mb(-8) + 父容器 pb 16 = 8, 比要的 16 各少一半。根因是间距被三处(footer margin / 分页器 padding / 父容器 padding)分摊,任何一处不生效就整体走样。 修法:把间距收归 footer 一个元素,其余全部清零 —— .video-footer padding:16px 0 + margin:0 父容器 .upload-content / .video 的 padding-bottom 归零(避免下边叠成 32) footer 内的 .vui_pagenation 系列 padding 归零(防它哪天生效把 16 顶成 24) ⇒ 上 16 / 下 16,只由一个值决定,不再依赖叠加。 选择器除通用 .video-footer 外,补一条完整链路 (main.space-main.route_uploadVideo > .space-upload > .upload-content > .video > .video-footer)提高特异性,防站内晚加载的同权重 !important。 JS 兜底 spaceVideoFooterPad:只在 computed padding-top/bottom 不等于 16 时 用 setProperty important 反写(防行内 !important 压过样式表), 已正确的零写入,页面无 .video-footer 时一次查询即返回。 未改:按钮尺寸、水平 gap、横向滚动、跳转区隐藏等沿用既有规则,一律不动。 ---- 个性装扮页:隐藏右侧扫码区 + 手机屏适配(v132.76)---- 探针实测(bili_probe_2026-10-03_10-24-57,视口 360x800,全页只有 15 个节点): body / #app [360x800] └─ main._mainPage_zfm7p_1 [715x800] left=0 ovf=auto ← PC 定宽 715 ├─ section._leftPanel_ [375x760] left=0 top=20 │ └─ iframe#mall-iframe [375x760] absolute ← 装扮预览(正文) └─ section._rightPanel_ [340x760] left=375 ← 扫码区(要隐藏的) ├─ img._logo_ / div._title_「个性装扮」 ├─ div._outside_ > canvas._qrcode_ [300x300] ├─ div._openClient_「打开客户端扫一扫」 └─ div._subTitle_「找寻属于你的个性装扮」 算一下:375 + 340 = 715,正是 main 的宽度。这是「左预览 + 右扫码」的 PC 两栏, 在 360 视口下 main 横向溢出 355:右边扫码区整块落在屏幕外(left=375), 左边预览 iframe 也只露出 360/375、右边还被切掉 15px。 真因:该页没有任何窄屏分支,两栏宽都是写死的。 修法四步: ① 右栏整块隐藏(display/visibility + 宽高 + 内外边距 + 边框全归零 + pointer-events:none,防隐藏后仍占点击热区); ② main 收回 100% 宽 + overflow-x:hidden,禁止横向滚动; ③ 左栏吃满 360,高度按视口给 calc(100vh - 20px) —— 保留现有的呼吸位 (原 bottom=780 = 800-20),并把 top 归零让预览多拿到那 20px; ④ iframe 以 absolute 铺满左栏 —— ⚠ 必须给左栏定高,否则 height:100% 没有 参照会塌成 0(v132.55 那次灯箱塌成 0x0 就是同一类"父高未定"的坑)。 ⚠ 关键约束:这一页的类名全是 CSS Module 哈希(_mainPage_zfm7p_1, 中间的 zfm7p 是构建哈希、每次发版都变),**不能写死完整类名**, 一律用 [class*="_mainPage_"] 这类前缀匹配,只认哈希前的稳定语义名。 JS 兜底 garbPageFit 补两件事:① #mall-iframe 这个 id 是稳定的, 据此判定页面并给 body 打 bili-garb-page(样式表有对应兜底规则), 类名前缀将来失效时仍能命中;② main 若被写成行内 !important 导致仍横向溢出, 按几何量探测后 setProperty important 反写(含 iframe 拉满)。 未做:iframe 里是独立文档,本脚本的样式表进不去, 其内部若还有 PC 排版,无法在这一层一并处理。 ---- 视频页推荐列表里的推广卡「直击2026诺贝尔奖>>」(v132.75)---- 探针实测(bili_probe_2026-10-03_10-15-41,视频页,抽屉打开态): .rec-list [282x1869] top=282 ├─ .video-page-special-card-small [282x80] top=282 ← 推广卡,要隐藏的这条 │ ├─ .pic-box [141x80](封面) │ └─ .info > a > p.title 「直击2026诺贝尔奖>>」 │ └─ .desc 「专家&UP主独家解读,上B站看懂诺贝尔奖!」 └─ .video-page-card-small [282x82] top=374 ← 正常推荐卡,保留 关键:它用的是 **special** 而不是 ad 类名。脚本此前那几条广告规则写的都是 .ad-report / .ad-floor-cover / .video-card-ad-small,一条都没命中它 —— 这就是这条广告一直露着的原因。 改法(两条腿): ① CSS 按类名 .video-page-special-card-small 整块隐藏 —— display/visibility/宽高/内外边距/边框全归零 + overflow:hidden + pointer-events:none(防隐藏后仍占点击热区)。 ⚠ 特意把 margin 一起归零:探针里这张卡带 mb=12px,只写 display:none 的话高度确实没了,但它上方的 12px 仍会留在流里,推广位会留一条莫名其妙的缝。 ② JS 兜底 bindVideoSpecialAdGuard —— 类名历史上改过多次,将来若再换, 类名规则会整体失效;而推广位的**文案特征稳定**:标题末尾带「>>」 (正常推荐视频的标题不会用这个符号)。 扫 .rec-list / .recommend-list-v1 的直接子级,命中(类名含 special / ad- / advert / promo,或文本含 >>)就打上稳定的 .bili-video-special-ad, 由样式表负责隐藏 —— 与直播间「点击取消静音」提示(v132.57)同一套路。 降载:扫过即打 data 标记跳过,只看直接子级不递归整棵推荐树。 范围:只在视频页 / 列表页 / 番剧页运行;正常推荐卡一律不动。 未做:不做"按 UP 主 / 按标题关键词"这类黑名单 —— 换一期活动就得改一次脚本, 类名 + 文案符号这两条是推广位自己带的结构特征,能长期生效。 ---- 动态页轮播图左侧空白:v132.73 无效,换根因重修(v132.74)---- v132.73 加了特异性更高的 width:100%,实测【完全没变化】—— 这条线索本身就是 诊断:我们已经写了 width:100% !important + min-width:100% !important, 若轨道的包含块真是 310,min-width 就不可能让它停在 222。 所以"宽度声明被压过"是错的方向,真因在**包含块本身就只有 222**。 把新探针的数字重新算一遍(新的动态页探针10030903.txt): .bili-dyn-gallery__window [310x425] left=25 └─ .bili-dyn-gallery__track [222x425] left=113 └─ 图片 71 宽 × 3 列 + 4px 间隙 = 221 ≈ 222 → 310 − 222 = 88,113 − 25 = 88:轨道右缘 335 与窗口右缘 335 严丝合缝, 即"宽度刚好等于内容宽,且被推到最右"。 能让 width:100% 解析出 222 的只有两种情形: ① 宿主(__window)有 padding-left:88 → 内容盒 222,百分比参照它 → 222; ② 宿主是 grid 且列宽 auto → 列按内容宽(222)定型,百分比参照列宽 → 222, 再配 justify-content/justify-items:end 右对齐 → left 113。 两者都解释得通,且都能被同一组声明一次打掉,故不再二选一: · 三层容器(__pics / __gallery / __window)补 display:block —— 直接消灭情形②(grid/flex 的 auto 列宽与右对齐); · 同一组补 padding:0(含 padding-inline 各写一遍)+ margin:0 + border-width:0 —— 消灭情形①; · 再补 grid-template-columns:none / justify-content:flex-start / justify-items:stretch / align-items:stretch,防站内换别的布局方式复发。 JS 兜底 dynGalleryFlatten 第 ⓪ 段整体重写: · 触发条件改为「宿主内容宽 vs 轨道实际宽」—— 内容宽 = rect.width 减 左右 padding,不再只看 rect.width(这正是 v132.73 漏掉情形①的原因); · 命中后先修宿主(padding / display:block / grid 列 / 对齐),再修轨道, 最后按宿主宽给每张图写一次具体 px(3 列 / 4px 间隙); · 轨道宽度百分比与 px **双写** —— px 兜住"百分比参照系仍被带偏"的情况; · 触发节奏加固:首屏 0/150/400/900/1800/3000ms 六拍强制跑 (新增 force 参数绕过 1.2s 节流,v132.73 只靠守卫的一次触发很可能压根没跑到), resize 也强制重跑并清标记。 未改:图片仍是 3 列等分、gap 4、居中,比例不变、不拉伸 —— 变的是参照宽从 222 回到 310,每张 71 → 100。 ---- 动态页轮播图左侧一大片空白(v132.73)---- 现象:v132.72 把轮播平铺后,图片整块靠右,左侧空出约 88px 的一条白。 探针实测(新的动态页探针10030903.txt,同一条置顶 opus): .bili-dyn-gallery__window [310x425] left=25(右缘 335) └─ .bili-dyn-gallery__track [222x425] left=113(右缘 335)← 贴着右边 └─ 图片 3 列,每张 71 宽 关键数字:222 = 3×71 + 2×4,正是「三张图加两条间隙」的内容宽; 310 − 222 = 88,就是左侧那条白。 即 track 的宽度不是"父容器宽",而是被压成了"内容宽",再靠右对齐, 于是右侧严丝合缝、左侧整块留白 —— 看起来像"图片被推到右边"。 真因:v132.72 那条 .bili-dyn-gallery__track { width:100% !important } 是 单类选择器(特异性 0,1,0),压不住站内同带 !important 的宽度声明; 同权重下同带 !important 时按层叠顺序,站内那条排在后面就赢了。 改法两步: ① CSS 提升特异性 —— 选择器改成父级链写法 .bili-dyn-gallery__window > .bili-dyn-gallery__track(0,2,0)与 .bili-dyn-gallery > .bili-dyn-gallery__window > .bili-dyn-gallery__track(0,3,0) 双写,并补三条防"被当成 flex/grid item 收缩到内容宽": min-width:100% —— flex item 的自动最小尺寸由内容决定,会拒绝收缩到 内容宽以下,但反过来也可能被 fit-content 卡住; flex:1 1 100% —— 父级若是 flex/grid,直接吃满一行; align-self:stretch —— 父级若是 grid(默认 stretch)也拉满。 ② JS 兜底 dynGalleryFlatten 新增第 ⓪ 段 —— 轨道没撑满父容器就补最后一道: 按几何量判断(track 比宿主窄 2px 以上,或比宿主左缘右偏 2px 以上), 用 setProperty important 写回 width / min-width / max-width / flex / align-self / margin / position / left / right / transform / box-sizing。 行内 !important 是作者样式里的最高优先级,站内再怎么写也压不过这条。 只按实测几何触发,已撑满的轨道一个字节都不写。 配合标记策略:本轮动过宽度就先不打 dataset 标记,下一轮复核一次, 避免"改完又被站内改回去"却因已标记而不再修正;其余情形照常打标记跳过。 未改:图片仍是 calc((100% - 8px)/3) 三列等分,只把它们的参照宽从 222 拉回 310, 每张 71 → 100.67,比例不变、不拉伸;gap 4px 与 justify-content:center 不动。 ---- 动态页轮播图(.bili-dyn-gallery)改为屏内完整平铺(v132.72)---- 需求:动态页里那种「横向轮播 / 滑动看多张图」的模块,不要再做成左右滑动、 一次只露一张:改成在手机屏内完整显示——不裁剪、不拉伸,超出屏宽就等比缩放, 一行放不下就换行。 探针实测(动态页的探针10030829.txt,置顶那条 opus 卡): .dyn-card-opus__pics [310x268] left=25 └─ .bili-dyn-gallery [310x268] └─ .bili-dyn-gallery__window [310x268] pos=relative ovf=hidden ← 裁剪元凶 ├─ .bili-dyn-gallery__nav.prev [0x0] ← 左箭头已隐藏 ├─ .bili-dyn-gallery__nav.next [36x36] left=273 └─ .bili-dyn-gallery__track [222x268] left=113,行内 tf=matrix(1,0,0,1,0,0) └─ .bili-dyn-pic.bili-dyn-gallery__image ×18 前 6 张 [201x268] 后 2 张 [222x268] img [201x0](未加载) 【真因】两层: ① __window 是 overflow:hidden 的定宽窗口(310×268),track 比它宽(18 张排开), 超出部分直接被切掉 —— 这就是"左右滑动、一次只露一张"的实现方式; ② 每张图的宽是站内按「窗口高 268」反算出来的(201 / 222),属"填满窗口高", 不是"适配容器宽",一旦容器收窄就会溢出。 【修法】把这条链从"定宽窗口 + 横向轨道"改成"可换行的 flex 网格": · __gallery / __window / .dyn-card-opus__pics 三层:width:100% + height:auto + min-height:0 + max-height:none + overflow:visible —— 窗口不再裁,高度交给内容。 ⚠ 不覆盖 position:__window 的 relative 是 __nav 的定位上下文,留着无害; 真正要治的是 overflow,不是定位。 · __track:display:flex + flex-wrap:wrap + justify-content:center + gap:4px + width:100% + height:auto + transform:none + position:static + margin/padding:0。 transform:none 与 position:static / margin:0 一并写,是因为探针里图片 rect left 是 -1713 这种大负值(relative 元素却整体左移近 2000px),说明位移可能 由行内 transform / 负 margin / left 三者之一施加,单写 transform 有可能漏。 · 每张 __image:flex:0 1 calc((100% - 8px)/3) + width 同值 + height:auto + min-height:0 + max-height:none + margin:0 + left/top:auto (0 **1** 允许极少量收缩,吸收高 DPI 下 100.66×3+8 可能算出 310.01 的舍入误差; 写 0 0 会把第 3 张挤到下一行又变回 2 列 —— v132.66 九宫格同款取舍)。 3 列是跟九宫格对齐的选择;想改 2 列把分母 3 换成 2 即可,别动别的。 · 内层 __pic__img / .b-img / picture.b-img__inner:display:block + width:100% + height:auto + min-height:0 + max-height:none;img 加 object-fit:contain 保底(万一某张图的原始比例与盒子不符,也只"留白"不"裁切")。 ⚠ 这里可以放心把 .b-img / picture.b-img__inner 改成 block —— v132.71 那次是 因为宽度由图片自身决定、窄图要靠 text-align:center 居中,才不敢动 display; 本次宽度已被 calc 钉死,居中交给 flex 的 justify-content,不存在那种场景。 · __nav(左右箭头)整块隐藏:轮播已废,箭头留着会盖在图上并吃掉点击热区。 尺寸 / 内外边距 / 边框全归零 + display:none + visibility:hidden + pointer-events:none(v132.64 隐藏「展开」按键那套写法)。 【JS 兜底】dynGalleryFlatten —— 只为防三件 CSS 压不住的事: ① 行内 !important 的 transform(内联特异性最高)—— 只在 transform 不是 none 也不是单位矩阵时写回 none,零位移时零写入; ② 行内 !important 的 height / max-height / min-height —— 用 getPropertyPriority 判断,只反写带 important 的那几项,普通行内样式 CSS 已压住不动; ③ 懒加载 img 高度塌成 0 —— naturalWidth/Height 就绪后写 aspect-ratio, 打 dataset 标记只做一次(本脚本通用 img 规则的 max-width:100% + height:auto 在没有 aspect-ratio 时,未加载的图就是 0 高,加载后靠 intrinsic 尺寸撑开)。 外加一条几何自检:若首张图的 rect.left 比 track 左缘还小 8px 以上(说明位移 没被清干净),把 track 与所有图的 transform / margin / left 一并 important 写 0。 降载:处理过的 track 打标记跳过、模块级 1.2s 节流、resize 清标记重跑; 注册走 registerDomGuard(test 收紧到 .bili-dyn-gallery__track)+ 捕获阶段 load (img 的 load 不冒泡,但捕获能收到),页面无轮播时一次 querySelector 就返回。 未做:① 不主动派发 click 去"翻页"—— CSS 已经把全部图片铺开,站内页码状态 无关紧要,去点还可能触发它自己那套 transition 状态机(v132.62 的教训); ② 不给容器写死高度 —— 让它由内容自然算出,图多就高、图少就矮, 代价是站内若改字体 / 间距时高度会跟着浮动,但绝不会把图切掉。 ---- 动态 / 相关页面:图片下方多出的一段空档(v132.71)---- 现象:动态里有些单图(宽图 / 视频封面一类)下方凭空多出 20~50px 空白, 图片与下方文字被撑开。用户点名的是「洛克王国10月9日更新速递」那条。 探针实测(bili_probe_2026-10-02_18-58-17 动态页): .bili-album__preview.single [310x203] ├─ __picture [310x203] ← 盒子 203 高 │ └─ __picture__img (.b-img) [310x203] │ └─ picture.b-img__inner [310x175] ← 图只占 175 │ └─ img [310x175] → 底部 203 − 175 = 28px 空档;引用转发卡(容器 270)是 203 − 152 = 51px。 【真因】站内给单图预留的尺寸是「按 360 宽」算的: 站内常量 —— 预览最大宽 360、最大高 280。 16:9 的图(ratio 175/310 = 0.565)受"宽度上限"约束: 360 × 0.565 = 203(宽度先撞上限 → 高度按 360 算出来) 而脚本把内容区收窄成 310(引用卡 270),再加上脚本自己那条通用 img 规则 (max-width:100% + height:auto)把图等比压到 310 → 310 × 0.565 = 175。 盒子高度是站内写死的 203,不会跟着一起缩 → 差出 28px。 跨探针验证:盒 203 在 310 与 270 两种容器下都是 203(与容器宽无关), 而图高 175 / 152 正好都等于 容器宽 × 0.565 —— 与上述推算完全吻合。 竖图 / 窄图不受影响:那种情况受"高度上限 280"约束,盒宽 = 280 / ratio, 本就等于容器内的实际渲染宽(如 210x280),盒图一致、没有空档。 【修法】让盒子高度跟随图片实际渲染高度: height:auto + max-height:none + min-height:0,写在 __preview / __picture / __picture__img / picture.b-img__inner 这一整条链上。 只动高度、不动宽度 —— 不会把图放大、不改裁切比例(图本就完整显示, 只是盒子多留了一截)。 【两个刻意不做的点】 ① 不在 .bili-album__preview 上写 display —— v132.65 那条 display:flex !important 与它特异性相同(都是 0,1,0),同权重下后写的 胜出,本条位置在它之后会把 flex 覆盖成 block,居中与换行全废。 ② 不把 picture.b-img__inner 改成 block —— 默认 inline, 靠 text-align:center 居中(opus / 空间页都这么排),改 block 会撑满整行、 窄图从"居中"变"靠左"。且探针那 28px 已由 203−175 完整解释, 没有额外基线间隙,不需要动 display。 【JS 兜底】albumPreviewTrimGap —— 只为防站内把盒高写成行内 !important (内联优先于样式表,CSS 压不过)。实测盒与图的渲染高度,差 > 2px 才把 盒高写回图片高度;没空档就零写入,正常情况应当是纯观察。 只按几何量判断,不猜站内算法、不碰宽度与裁切。 触发:init 一次 + 统一 DOM 守卫(test 收紧到 .bili-album__preview.single, 页面无相册时一次 querySelector 就返回)+ 捕获阶段 load (img 的 load 不冒泡,但捕获能收到 —— 图加载完高度才最终确定)。 ---- 代表作标题「多出一行被横切」(v132.70)---- v132.69 的回归。现象:标题下方露出半行文字,被横向切掉。 探针实测:.bili-video-card__title [152x61] / __details [168x77] (整卡 95 + 77 = 172 已对齐,问题只在标题内部)。 真因:v132.69 把 clamp 盒的高度直接写成 61(= 3 行 54 + 7 余量)。 -webkit-line-clamp:3 的语义只是"排到第 3 行末截断并加省略号", 它**不会清空盒子里剩下的 7px** —— 第 4 行照样往那 7px 里排字, 盒又带 overflow:hidden,于是第 4 行只露出 7px(行高 18 的一小半) 被横切一刀。即:clamp 管的是"行数",不是"盒高"。 修法:把「行数」和「盒高」解耦 —— · clamp 盒精确钉到 3 行净高 54(18×3),多出的 7px 改用 margin-bottom 补; margin 在盒外,第 4 行没有可排版的空间,自然不渲染; · 外盒 __title 用 display:block(它只是容器,不需要 -webkit-box), 内层 > a 才是真正 clamp 的那层(也是文本实际所在)。 → 54 + 7 = 61,details = 8 + 61 + 8 = 77,整卡 172 不变。 ⚠ margin-bottom 只能写在外层一条上 —— a 是 title 的子盒,若它也带 7 会叠成 68、卡高又变回去。 教训:-webkit-line-clamp 与 max-height 一起用时,两者必须严格对齐 (max-height = 行高 × clamp 行数),否则多出来的高度会漏出半行。 需要"盒比内容高"时,用 margin / padding,不要用 max-height 虚高。 ---- 空间页主页:代表作卡片高度回到 172,与其它卡片一致(v132.69)---- 现象:v132.68 把代表作标题从 2 行(36)放宽到 3 行(54)后,卡片比别人高 18px。 真因(算一遍就清楚): 代表作 __details = pt8 + 标题 + [subtitle mt4 + 21] + pb8 改前:8 + 36 + 25 + 8 = 77 → 整卡 95 + 77 = 172(与主页一致) 改后:8 + 54 + 25 + 8 = 95 → 整卡 95 + 95 = 190(高出 18) 即"标题多出来的第 3 行"没有替换掉任何东西,而是直接加在了卡上。 参照物(主页卡片探针 up主空间页…反正卡片高度也一致.txt): .bili-video-card [168x172] = 封面 [168x95](16:9)+ __info [168x77] __info = pt8 + 61 + pb8,其中 61 = 标题 36 + UP主行 21 + margin-top 4 (代表作封面同样是 95:缩略图 .b-img__inner 命中 8349 行的 16:9 规则) 修法:把主页那 61px 的内容盒整个让给标题 —— ① 隐藏代表作那行空 __subtitle / __info--bottom / __date (探针:文本为空、内嵌 span 0x0,不显示日期 —— 用户已确认); ② 标题盒 min/max-height 由 54 改 61(clamp 仍 3 行,3 行净高 54, 余 7px 落在第三行下方当作行间余量)。 结果:details = 8 + 61 + 8 = 77 → 整卡 95 + 77 = 172,与其它卡片对齐。 ⚠ 前提与退路:若某些 UP 的代表作那行确实显示内容,删掉隐藏规则、 把标题的 61 改回 36(退回 2 行),高度同样是 172。 未做:没有给 details 写死 height:77 —— 让它由内容自然算出, 字体 / 行高若被站内改动时不会把文字切掉,代价只是高度随之浮动。 ---- 空间页主页:代表作标题 3 行 + 隐藏「最近投币的视频」(v132.68)---- 探针实测(空间页主页 · 代表作区块 masterpiece-block): .bili-video-card__title [152x36] top=466 bottom=502 ← 2 行(18×2)后省略 .bili-video-card__subtitle [152x21] top=506 ← 文本为空,内嵌 span 是 0x0 代表作卡不显示「UP主名 / 日期」那一行的实际内容(subtitle 是空壳), 等于白占 21 + 4 = 25px —— 用户据此要求把标题从 2 行放宽到 3 行。 ① 只覆盖 clamp 行数与高度(54 = 18×3),其余沿用上面那条通用规则; 但 display:-webkit-box / -webkit-box-orient / overflow 三条一并重写 —— line-clamp **只在 display:-webkit-box 下生效**,若站内将来改了 display, 只写 clamp 会"截不断"(v132.64 话题页简介那次就是同款坑)。 选择器多加一层 .masterpiece-block(特异性 0,3,0 > 通用的 0,2,1), 只作用于代表作,主页其它两列卡片(投稿 / 合集)仍是 2 行。 ② 隐藏「最近投币的视频」整块(探针路径 .coin-section__content > .items): 用 [class*="coin-section"] 兜底 —— 外层类名可能带哈希后缀,写死会失效; 空间页没有第二处会命中这个子串的模块,泛化安全。 display:none 之外把 visibility / 尺寸 / padding / margin 一并清零, 防站内靠残留尺寸把空位撑出来。 未做:没有顺手清掉代表作那行空 subtitle 的 25px —— 它是空壳但 DOM 在, CSS 无法可靠判断"内容为空"(内嵌的 0x0 span 让 :empty 不成立), 硬清会牵连将来真有内容的情形。若确认永远为空,可以再单独加一条 body.bili-space-page .masterpiece-block .bili-video-card__subtitle { display:none !important; height:0 !important; margin:0 !important; } ---- 九宫格展开图片后,下方缩略图那一行居中(v132.67)---- 需求:点开九宫格看大图后,图片下方那一行缩略图(5 个)整体居中显示。 探针实测(居中缩略图.txt,动态页): .bili-album__watch__track [310x54] left=25 → 右缘 335 ├─ __slider [286x54] left=25 → 右缘 311 ← 右侧空 24px │ └─ __list translateX(-290),窗口内 5 张整的:left = 25/83/141/199/257 ├─ __prev [12x54] left=25 ├─ __next [12x54] left=299 → 286 是 v132.51 按实测步进算出的「整数张」窗口宽(5×58 − 4),但 slider 是 普通块流、默认左对齐,贴在 310 的轨道左边,右侧白留 24 —— 就是"整体偏左"。 修法:① CSS 给 slider 加 margin-left/right:auto —— 窗口在轨道内居中 (286 → left 37,两侧各留 12)。 ⚠ 不能改 width:那是 albumTrackFitWindow 按实测步进算出来并以 important 写回的,CSS 写死会和它打架;margin 与它不冲突。 ② JS 侧把「原点」从轨道左缘换成窗口左缘 —— 这是本次最容易漏的一步: slider 一居中,prev / next 箭头与"滚入可视区"的判断若仍按 tr.left 算, 就会整体错开半个余量(12px),表现为箭头与缩略图对不齐、 边缘那张被判成"已在窗口内"而不平移、只露一部分。 故 albumTrackSyncOne 在写完 width 后读一次 slider 的 rect 得到 winLeft (getBoundingClientRect 会触发同步布局,拿到的就是居中后的位置), 传给 albumTrackScrollActiveIn(sr 的原点)与 albumTrackPinArrows (相对 track 的 left 先补 off = winLeft − tr.left)。 取不到 slider 时退回 tr.left,等价于旧行为,不会比现在更差。 高亮框不受影响:它也是 track 的 absolute 子元素, 用 ir.left − tr.left 本就是相对包含块的绝对几何量,居中后自动跟随。 未做:没有改缩略图尺寸与张数 —— 5 张 54×54 是站内按窗口算的,用户只要居中。 ---- 九宫格回到 3 列 + 看图器控制栏按键居中(v132.66)---- ① 九宫格回到 3×3:v132.65 只做「居中 + 换行」,图仍是站内给的 132×132, 310 的容器一行只排得下 2 张(132×2 + 4 = 268),九宫格变 2×5 —— 用户要 3 列, 那就必须把格子缩到容器的三分之一: 格宽 = (容器宽 − 2×gap) / 3 = (310 − 8) / 3 ≈ 100.7 文章页 296 → 96,引用转发卡 270 → 87.3,全部 3 列、零裁剪。 两个细节: · flex 用 0 **1** calc(...)(允许极少量收缩)—— 三个 100.66… 加 8px 间隙 在高 DPI 下舍入可能算出 310.01 > 310,写 0 0(不收缩)会把第 3 张挤到 下一行又变回 2 列;允许收缩吸收这点误差,视觉差不到 0.4px。 · 图片只写 width:100% + height:auto,**不写死高度** —— 站内格子是正方形 (132×132),等比缩放后仍是正方形;不写 height 就没有「父高未定 → height:100% 塌成 0」的死循环(v132.55 灯箱塌成 0×0 就是这么来的), 也不依赖 aspect-ratio 的浏览器支持。 只作用于 .grid3:单图 single(210×280)与两图 grid2 保持 v132.65 原尺寸居中。 缩图选择器精确到「__picture__img > picture.b-img__inner > img」这一条链, 不用后代 img,免得把格子里的角标小图(GIF / 长图标记)一起拉满。 ② 看图器控制栏「收起 / 查看大图 / 向左旋转 / 向右旋转」居中: v132.51 只压紧了内容(四个按键约 260 宽)却没管对齐,flex 默认 flex-start → 按键左对齐,容器(opus 296 / 动态页 310)右侧空 36 ~ 50px, 看上去就是"偏左、左右间距不一致"。补 justify-content:center, 并加一行 safe center 渐进增强 —— 内容若宽过容器,safe 会退回 flex-start, 配合既有的 overflow-x:auto 仍滑得到,不会像普通 center 那样把左半截 顶到滚动区外再也滚不回来(flex 居中 + 溢出的经典坑);旧内核不认 safe 会整条丢弃,退回上一行的 center。另给容器 width:100% + margin:auto, 让"容器内居中"等于"整行居中"。 未做:没有改按键之间的间距 —— 它们靠各自 padding 撑开且彼此紧贴, 居中后左右两侧留白相等即达到用户要的效果。 ---- 动态 / 文章里的相册图片:居中 + 放不下换行(v132.65)---- 需求:动态页及其相关页面(文章页 opus、空间页、话题页)里的图片居中显示, 一行放不下就换行,别因为超出容器被裁掉。 探针实测(动态页 多张图片.txt / 动态页图片位置.txt、文章页 文章页面.txt): 九宫格 .bili-album__preview.grid3 [310x404] / left=25,三张 132x132 落在 left = -22 / 114 / 250 → 3×132 + 2×4 = 404,比容器 310 多 94, 站内网格把溢出均摊到两侧 → 第一张左裁 22、第三张右裁 22。 文章页 grid3 [296x540] / left=32,第三张 left=304 → 右边界 436, 超出 360 视口 76px,被 body 的 overflow-x:hidden 整块切掉。 单图 .bili-album__preview.single:210x280 在 270(引用转发卡)/ 310(普通) 容器里左对齐,右侧白留 60 ~ 100px —— 同样是没居中(另一处 239x280 亦然)。 处理(纯 CSS,无 JS —— 站内这套是样式表写的,探针里没有行内样式): 把预览容器从站内的「定宽 3 列网格」改成可换行的 flex 并居中 —— display:flex + flex-wrap:wrap + justify-content:center + gap:4px (4px 来自实测行距 136 − 132)+ grid-template-columns:none 显式作废那套列宽; 图片自身尺寸一个字节不动(132×132 / 210×280 全部保留), 故 310 / 296 / 270 三种容器都自然排 2 张(132×2 + 4 = 268)并居中、不裁。 图片项写 flex:0 0 auto(不压缩不拉伸,放不下整张换行)+ max-width:100% (极窄容器里只缩宽,绝不溢出)。 选择器不带 body class:相册在动态页 / 空间页 / 话题页 / opus 详情页是同一套 DOM;图片查看器是 .bili-album__watch(不是 __preview),不受影响。 未做:没有把图缩小去凑 3 列 —— 用户要的是"放不下就换行",缩小会改掉站内 给的尺寸;九宫格因此从 3×3 变 2×5(图更大、行数更多),若要回到 3 列, 把 .bili-album__preview > .bili-album__preview__picture 加一条 width:calc((100% - 8px)/3) !important 即可。 ---- 话题页简介默认展开 + 隐藏「展开 / 收起」按键(v132.64)---- 需求:话题页(.topic-detail)那句简介默认展开,别再截半行,并把 右下角那枚展开 / 收起按键去掉。 探针实测(同一话题「怀旧游戏推荐」,展开状态.txt vs 未展开文字被裁剪.txt): 未展开:.topic-desc [318x53] / .topic-desc__text [318x53] / overflow=hidden, 内部文字本体 span [312x79](约 4 行)→ 容器只给 53, 2 行 40 之后第三行只露 13px 就被切掉 —— 正是用户看到的"第三行截断"; label.topic-desc__text-btn [26x18] 是那枚按键(未展开 top=148)。 展开后:.topic-desc [318x80] / span [315x59] → 完整显示,按键 top=175 同步下移。 处理:① 高度解绑 —— .topic-desc 与 .topic-desc__text 写 display:block + height:auto + min-height:0 + max-height:none + overflow:visible + -webkit-line-clamp/line-clamp:unset + text-overflow:clip + white-space:normal。 ⚠ display 必须改回 block:line-clamp 只有在 display:-webkit-box 时才生效, 单清 clamp 但保留 -webkit-box,多行截断仍是老样子。 -webkit-box-orient 一并改 horizontal,避免 box 布局残留纵向排列。 ② 文字本体 .topic-desc__text > span 同样解一次 —— 展开态容器 80 而 span 59, 说明行数限制也可能写在 span 上(两条都清,谁生效都不怕)。 ③ 隐藏按键 .topic-desc__text-btn(尺寸 / 内外边距 / 边框全归零 + display:none + visibility:hidden + pointer-events:none), 免得它藏了还占着文字右下角的热区。 选择器沿用话题页既有的 body:has(.topic-detail) 前缀,不带 DOM 层级 (话题页是 SPA,节点会被 Vue 换掉)。 JS 兜底 topicDescAutoExpand(只防"站内把限制写成行内 !important"): 行内 !important 与样式表 !important 同级、但内联特异性最高,CSS 压不过, 故扫到 .topic-desc / .topic-desc__text / span 时逐项 setProperty important 反写 同一组属性,并隐藏按键;没有 .topic-desc 直接返回,非话题页一次查询都不做。 挂在 run() 上(首屏 0/300/800ms 那几拍足够),刻意**不进**统一 DOM 守卫 —— 简介只在进页面时渲染一次,没必要常驻轮询拖累播放。 未做:不主动派发 click 去"骗"站内组件切到展开态。CSS 已经把高度放开, 组件内部状态(是否折叠)留在收起态也无副作用;真去点还可能触发 站内「改行内 height → transition → transitionend 清掉」那套状态机 (v132.62 抽屉那次的教训),得不偿失。 ---- 番剧页隐藏「承包榜」(v132.63)---- 探针实测(bili_probe_2026-10-02_16-33-15,番剧播放页 ep193549): #sponsor_module [344x257] top=324 bottom=581,位于简介区之下、 评论区 #comment-module 之上,内部两半: · 左 .sponsor_sponsor_mini_rank [158x224] —— 标题「承包榜」+ 10 条 li.rankList_sponsor_rank_item(用户名 + 留言) · 右 .sponsor_sponsor_info [186x224] —— 「查看承包榜」按钮 + 「已有 1423 人承包」计数 选择器取舍:内部类名全是 CSS Module 哈希(cx4yJ / cyO8d / BZrNc …), 站内每次构建都会换,写不死;但 #sponsor_module 这个 id 长期稳定, 故以 id 为主(特异性 0,1,0,0 已是最高一档,无需再叠 body class)。 另配三条同义兜底:[id*="sponsor_module"] / [class*="sponsor_module"] / [class*="sponsor_sponsor"] / [class*="sponsor_rank"] / [class*="rankView_sponsor"] / [class*="rankList_sponsor"]。 ⚠ 刻意不写 [class*="sponsor"] 这类宽泛选择器 —— 番剧页还有 「为TA充电」等模块,泛化会误伤掉一大块。 JS 兜底 bangumiHideSponsor(只防"将来 id 与类名全换哈希"): 页面已有 #sponsor_module 或已打过标记就直接返回;否则只在 .player-left-components(或 .plp-l)的直接子级里按文本找含 「承包榜」的块,打 .bili-hide-sponsor 交给样式表隐藏 —— 只比对十来个节点,不碰评论区那几千个节点。 间距:隐藏后评论区自然上移(原本 comment 顶 581 紧贴 sponsor 底, 间距由 comment 自身 pt=12 提供),故不额外补 margin。 ---- 抽屉底部「展开 / 收起」按键失效(v132.62)---- 现象(v132.60/61 引入的回归): ① 抽屉底部「展开」点一下能展开,再点「收起」却毫无反应; ② 在这状态下点抽屉里某个视频换页后,本该由"展开"显示的那一段 直接变成一大片空白,且按键又变回「展开」。 两个真凶都是 v132.60 为降载加的、属于"越界优化": 【主因】抽屉内 * 的 animation/transition:none 站内展开/收起是一套"改行内 height → 靠 transition 过渡 → transitionend 里再把行内 height 清掉"的状态机。过渡被 !important 钉死后 过渡根本不发生,transitionend 永远等不到,行内 height 就被永久钉在 展开后的数值上 —— 于是"收起"看着没反应(高度下不来)。 换页后 Vue 重渲染,按键文字回到「展开」,但残留的行内 height 还在, 组件按"当前已展开"计算 → 该显示的那段被算成 0 高 → 空白一大片。 【次因】抽屉关闭态的 content-visibility:hidden + contain-intrinsic-size 抽屉未打开时整块不参与布局,站内组件在挂载阶段量到的高度是 0 或 contain-intrinsic-size 编造的值,"展开"该展开到多少从一开始就算错。 【次因】warmImages 给封面写的 loading="lazy" + fetchPriority="low" 站内自己的懒加载/占位逻辑被这两个属性带偏,可表现为"该显示的地方 一片空白"(图片压根没开始加载)。 修法(该退的退、该留的留): 撤掉:抽屉 content-visibility / contain-intrinsic-size(三套抽屉规则); 抽屉内 * 的 animation/transition:none(视频页与列表页两处); 封面的 loading=lazy 与 fetchPriority=low(只保留 decoding=async)。 保留(用户已确认流畅度提升,这些是真起作用的):v132.61 的 run() 风暴 修复(播放器噪音过滤 / run 限频 / 扫描节流与节点数快照)、 v132.60 的 will-change 仅动画期间挂、transition 0.26s 减速曲线、 收窄阴影、overscroll-behavior:contain、后台任务静默窗口。 新增 syncDrawerState():点抽屉里的视频换页后,抽屉节点可能被换掉, 而脚本挂在 body 上的 bili-drawer-open 与遮罩 .show 不会跟着走 —— 残留会造成"播放器动画被永久 paused"与"页面蒙一层灰"。现在每次 run() 顺手校一次(一次 querySelector);开抽屉的两帧预热窗口内 用 drawerOpeningUntil 挡住,免得自愈把自己刚开的抽屉关掉。 教训:涉及高度过渡的组件(展开/收起、折叠面板)不能禁 transition; 需要隐藏大块内容时优先用 visibility / 移出视口,慎用 content-visibility —— 它会让组件量到假尺寸。 ---- 播放中开 / 滑侧边栏卡顿【真因】(v132.61)---- v132.60 只削掉了抽屉自身与几个常驻轮询的开销,用户反馈"依旧明显卡顿"。 回头对照参考脚本(bilibili 移动端 v5.4.3)才看清差距:它的 MutationObserver 全是"一次性 / 定向"的(盯到目标元素就 disconnect),本脚本却有一条 observer.observe(document.body, {childList:true, subtree:true}), 任何节点变动 → 200ms 后无条件 run() 全量重活。 真因链条: 播放中弹幕每秒插入/移除大量节点 → 这条 observer 被持续唤醒 → 每 200ms 一次 run() → run() 里的 hideWatchLaterButtons() → querySelectorAll('body *') 把整页(含弹幕常几千个节点)全收一遍, 命中者再 getBoundingClientRect() 强制布局 → 播放时这段整页遍历一直在跑,抽屉滑入那几帧正好撞上 → 掉帧。 暂停后弹幕停止、页面不再产生 mutation,run() 自然停摆 → 立刻就顺了。 这同时解释了为什么 v132.60 的"静默窗口"不够用:它只在抽屉动画那 几百毫秒里让路,而风暴本身从进页面起就一直存在,抽屉只是临门一脚。 修复(三层,缺一层都压不住): ① 播放器噪音过滤 —— 一批 mutation 若全部落在播放器 / 弹幕 / 控制栏 / 进度条 / loading 等区域内(PLAYER_NOISE_SEL),直接不唤醒 run(); 超过 60 条视为批量变更(换页、整块重绘),不过滤以免漏掉真变化。 判定结果进 WeakSet 缓存,播放中每条弹幕都问一次也不重复走 closest。 ② run() 限频 —— debounce 200ms → 500ms,且两次 run 至少间隔 800ms; 静默窗口(抽屉动画 / 滑动 / 手指落下)内继续让路,延后再补。 ③ hideWatchLaterButtons 自身降载 —— 3 秒节流 + 页面节点总数快照 (没长新节点就零成本返回),且扫描时把昂贵的 aria / textContent 读取延后到 className 未命中之后,命中即停,不再逐节点读两遍属性。 附带:抽屉加 overscroll-behavior:contain,滑到头不再带动底层页面重绘。 未改:抽屉几何仍为 85vw × 100vh 全屏覆盖(参考脚本是 calc(100% - 56.25vw) 只盖播放器下方)。全屏抽屉是既有的交互习惯,卡顿与几何无关,故不动; 若想进一步降合成开销,可改 height 为 calc(100% - 56.25vw) 与参考对齐。 ---- 播放中开 / 滑侧边栏卡顿(v132.60)---- v132.60 现象:视频播放中打开或滑动右侧抽屉明显掉帧,暂停后再试就顺了。 这说明瓶颈不在抽屉那条 transform 动画本身(它只是一次合成,暂停与否都一样), 而在于"播放中"有别的常驻任务在跟视频解码抢主线程: ① 面板 z-index 轮询:固定 250ms 一次,对 8 个面板逐个 getComputedStyle + getBoundingClientRect,播放中样式一直在脏,每次都是完整的样式重算 + 强制布局;暂停后样式静止、命中缓存路径,代价骤降 → 所以"暂停就不卡"。 ② DOM 守卫:观察整个 body 的 class / style 变化,播放器每帧改行内样式、 每来一条弹幕插一个节点都会把它唤醒,播放中几乎一直在跑。 ③ 抽屉自己:未打开时也在参与布局 / 绘制 / 命中测试(里面几十张封面图), 且带常驻 will-change 一直钉在合成层;首次打开还要在同一帧里完成 "整块首次布局 + 图片解码 + 滑入动画",必然卡在最前面几帧。 处理(三条同时做,缺一条都还能感觉到顿): A 后台任务静默窗口 holdBackground(ms) —— 抽屉开合的动画期(280+150ms)、 抽屉内滑动期间(滚动续 320ms)、手指落下时(220ms)统一挂起: 面板轮询直接跳过,DOM 守卫延后到动画结束再补一轮,查看器的 scroll 兜底同样跳过。用 var / function 声明避免 TDZ(tick 初始化即跑一次)。 B 面板轮询降载 —— 250ms 定频 setInterval 改为自适应 setTimeout: 静默期 1500ms、刚交互过的 2 秒窗口内 200ms;并加 WeakMap 签名缓存 (className + 行内 style),签名未变就沿用上次可见性结果,一次 getComputedStyle / getBoundingClientRect 都不做;点击补拍改为直接调 同步函数,不再叠加多个 schedule。 C 抽屉自身降载 —— 未打开时 content-visibility:hidden(整块跳过布局 / 绘制 / 命中测试,配 contain-intrinsic-size 保尺寸);will-change 只在 .bili-drawer-anim 动画期间挂、结束即摘;开抽屉前先打 .bili-drawer-warm 切回可渲染并强制一次 offsetHeight 布局,等两帧后才起动画,把首帧成本 挪出动画窗口;抽屉内所有子元素的 animation / transition 停掉(滑入瞬间 几十个条目同时淡入位移最要命);封面图提前标 decoding=async + loading=lazy + fetchPriority=low(前 120 张);阴影 -4px 16px 收窄到 -2px 10px、过渡 0.3s → 0.26s 并改减速曲线;抽屉打开时把播放器内的动画 animation-play-state:paused(此时都在遮罩下看不见,用 paused 而非 none 以免重置站内动画状态);视频页 / 番剧页 / 列表页三套抽屉规则同步改。 D DOM 守卫播放器噪音过滤 —— 新增 PLAYER_NOISE_SEL(播放器容器、控制栏、 弹幕区、进度条、时间、loading 等),一批 mutation 全是这些区域里的动静 就直接不唤醒守卫(超过 60 条视为批量变更,不过滤)。 未做:不隐藏播放器画面(左侧仍露出约 15vw,隐藏会看到空白突变), 视频解码开销不在主线程,硬解下对这几帧影响远小于上面三项。 ---- 直播间控制栏 / 遮罩 ---- v132.01 全屏点画面唤出的控制栏被底部 84px 空档顶起 → 按键贴屏幕底 v132.03 上一版顶掉了站内的 height / transform 状态通道,导致控制栏 弹出来不隐藏;改为只做 margin 位移补偿(偏移由 JS 实测), 收起行为完全交回站内 v132.05 站内遮罩层带 bottom:84px 让位,唤出瞬间先渲染在 84px 处, 补偿永远慢一拍 → 弃用该层,改由 #live-player::after 自建铺满式渐变遮罩(opacity 淡入) ---- 首屏零闪 ---- v132.06 顶部导航等改用不依赖 body class 的直播间独有选择器隐藏 v132.07 JS 兜底:脚本一执行即写行内 display:none + MutationObserver 盯住 DOM 节点一插入即藏;injectStyle 未就绪时改为重试 ---- 顶部栏 / 卡片 ---- v132.08 「排序」「切换」按钮改为只显示图标(36x36 正圆), 把横向空间全留给搜索框 v132.09 直播卡 UP主名行与综合页对齐(21px 高 + margin-top 4px, info 75→77、wrap→172) v132.10 修正 v132.09 引入的"UP 图标与名字分家":该行 width:100% 把名字格撑满且文字居中 → 改为 inline-flex 收缩到内容宽、 名字紧贴图标左对齐 v132.11 搜索页加载:骨架屏阶段列表容器不在两列网格的类名名单里, 走原生布局导致"先窄居中、再放大到 8px 边距";改为按结构 认出列表层(含 ≥2 个骨架屏的最深层)打 .bili-skel-grid, 并把骨架屏内部尺寸钉成卡片尺寸(封面 16:9、整卡 172)。 另修:点底部分页后仍停在页面最底部 → 监听分页点击并回顶。v132.12 定位到真因:骨架屏阶段列表容器叫 .i_wrapper.mt_xxl(无 .video / .search-all-list),两条旧规则都选不中,B站 i_wrapper 的 PC 定宽把它压成 248 内容宽 + 左右各 56 居中;改为按任意 .i_wrapper 兜底清零定宽与内边距。v132.13 修正:换页回顶窗口 1600ms 过长,用户滑动后仍被反复拽回顶部 → 收到 700ms 并加"用户滑动即交还控制权";顶部栏左侧按键图标颜色(#61666d)与右侧(继承 #18191c)不一致 → 统一为 #18191c。v132.14 新增:专栏首页(read/home)信息行只留「头像 + UP主名」,隐藏分类标签与阅读/赞/评论;顺带修掉长名字撑爆该行、导致头像名字下移被裁剪的问题(该行 author+label+stats 需 262px,容器仅 207px,溢出换行后垂直居中的 author 随之下移,被写死 88px 高且 overflow:hidden 的左半块切掉)。v132.15 新增:右侧封面图距下方分割线 60px、距上方 20px 不相等(左半块限两行后长到 128,右半块仍是原生 88 且顶部对齐,下方空出 40 死白)→ 两列改 align-items:stretch 等高、封面铺满并用 object-fit:cover 只裁不变形,上下各 20px 完全一致。v132.16 新增:推荐文章页卡片补上与专栏页 .b-article-card 一致的白卡背景(#fff / 8px 圆角 / 0 1px 3px rgba(0,0,0,.08) 阴影);文字与封面间距由 0 补到 12px(专栏页实测 174−162=12);卡片左右加 4px 内边距避免文字贴边;封面四角同步 8px 圆角。v132.17 新增:卡片间距 8px(放在 .feed-item 的 margin-bottom,不用 gap —— .feed 的 display 未知),并隐藏分割线(该线是 feed-item 169 与 article-item 168 的 1px 高度差,可能是 border-bottom 也可能是伪元素,故边框与 ::before/::after 一并清掉,不能直接 display:none 否则连同卡片一起消失)。v132.18 新增:第一张卡与上方搜索框之间同样留 8px(探针:header 底 78 = feed 顶 78 = 首卡顶 78,严丝合缝);给 .feed 加 padding-top:8px,用 padding 而非 margin 以免被 margin 折叠吞掉。v132.19 新增:卡片上下内边距由站内原生 20px 收到 8px(封面距卡片上下边缘各 8px),卡片高 168→152;封面自身尺寸与裁切方式完全不动。v132.20 新增:适配他人「关注/粉丝」页(route_relationFollow)—— PC 的「左 195 侧栏 + 右 267 列表」改为上下堆叠;左侧按键(全部关注 56 / Ta的粉丝 131)移到导航栏下方整行居中;隐藏分组标题「Ta的关注」「Ta的粉丝」;列表改单列,卡片按搜索页→用户页的 344x86 排版(头像 86 + 间距 15 + 内容 243),卡片间距 16px、左右 8px。v132.21 修正:头像框装饰层偏移 (82,82) 被卡片裁掉 → 钉回头像框内;隐藏大标题 .follow-main-title、导航行 .nav-bar.space-navbar 与站内搜索框(保留自建 #my-bili-searchbar);清除按键下 1px 分割线;.space-follow 退回块流、.items 不再强改 display 并关闭 content-visibility。v132.22 修正:闪电图标改到头像右下角、22x22(面积约头像 1/16)压住一角,不再整块盖住头像;粉丝页(route_relationFans)用的是 .space-fans / .fans-main / .fans-main-head 另一套类名,此前完全没被规则命中(.fans-main 宽 487 且 left=360 整块在屏幕外,这才是"6000 多粉丝页面内容不显示"的真因),现两套类名一并覆盖。v132.23 修正:闪电徽标内层相对外层偏 (-2,-2) 导致圆心内图标往左上挪 → 内层 top/left/margin/transform 全部钉零;「Ta的粉丝」用的是图标字体(按 baseline 排且字号大于盒子),比「全部关注」的 svg 更大且不居中 → 统一为 inline-flex 居中、字号行高 16;关注/粉丝页单独隐藏自建搜索栏并同步去掉 #app 的 52px 上留白;卡片间距 16→8px,分页条与直播页完全一致。v132.24 修正:分页条「只剩一个 5」—— 泛化选择器 [class*=pagenation] 连 --btns 一起命中并加了 justify-content:center,按钮总宽超 360 时左侧溢出被裁;现改为 --btns 左起排布 + overflow-x:auto 横滑 + 按钮 flex-shrink:0,并恢复 .vui_pagenation 的 pt/pb 8px、mt 归零(卡片 mb:8 已给出间距);卡片间距实测 16(grid row-gap 8 + .item mb 8 叠加),现把 row-gap 归零回到 8px。v132.25 修复:搜索页综合页翻到第 2 页后容器类名变为 .search-page-video,分页条正好是它的直接子级,被 v132.12 的「容器链满宽」规则 html body.bili-search-page #app .search-page > div { margin:0 } 命中(第 1 页是孙级所以躲过),导致上下 margin 归零、间距由 16px 缩到 8px;现补一条更高特异性规则把所有子页分页条外层统一拉回 mt/mb 8px,与直播页一致。v132.26 修正:综合页 .video-list.row 的 pb=8 与分页条 mt=8 叠加,导致"距最后一行卡片 24px、距屏幕底 16px"差 8px → 列表行 padding-bottom 归零(上边 8 保留),回到 16/16 对称;另修搜索栏菜单偶发"展开但图标不变蓝、点不回去"——createSearchBar 缺单例守卫可能在 body 里建出两个 bar、两套同 id checkbox,label for 只激活第一个同名 id,故改为一次性标志位 + append 前去重 + label 点击由 JS 显式 toggle(preventDefault 防双重切换)并同步 .is-open 标记。v132.27 新增:视频播放页 / 列表播放页(同为 body.bili-video-page)/ 番剧播放页 —— 控制栏按键弹出的面板(倍速 70x166、清晰度 95x166、设置 132x140、弹幕 320x359,原为 absolute 贴着播放器右下角 / fixed 贴屏幕顶边)改为与直播间 .quality-wrap .panel 一致的视口居中:fixed + top/left 50% + translate(-50%,-50%) + max-height calc(100vh-32px) + overflow-y auto + z-index 2147483000。v132.28 修复:面板被控制栏那排按键压住(按键还能点到)—— 每个 .bpx-player-ctrl-btn 都是 position:relative + z-index:2,各自创建堆叠上下文,面板 z-index 再大也出不去,同级按钮同 z=2 按 DOM 顺序后来居上;现把带面板的按钮 z-index 无条件提到 2147483646,并把控制栏常态 z 提到 1000(压过播放器内 z=999 的 dialogue-area)、面板展开时用 :has 抬到 2147483646。v132.29 修正:v132.28 把所有面板按钮抬到同一个极大值,它们之间重回"同层级比 DOM 顺序",排在后面的(设置/字幕)照样盖住前面的(倍速/清晰度)—— 这就是"只修好一部分"的原因;改为只抬当前展开的那个(.bpx-state-show / JS 补的 .bili-panel-host),并在 body.bili-player-panel-open 时把其余按键压到 z-index:0,与 DOM 顺序彻底解耦。v132.44 修复:非 via 浏览器打开视频页「自动满屏 + 左上偏移」—— 探针实测 #playerWrap 344x196、#bilibili-player 338x190 都正常,唯独 .bpx-player-container 是 360x800 / position:fixed / z-index:100000 / rect left=-84 top=-48:这是站内 JS 自己切进了「网页全屏」(非原生 Fullscreen API,故 body 上没有 video-fullscreen、fullscreenchange 也不会触发)。容器一变 fixed,2116 行那组 width/height:100% 的参照系就从 #playerWrap 换成视口 → 直接算出满屏;站内又按它自己以为的容器尺寸(约 528x896)做 left/top 50% + 负 margin 居中,实际渲染 360x800,两套数值打架 → 上下各溢出 48、左右各溢出 84。番剧页没事是因为 3621 行那组写了 position:absolute + top/left 0,天然免疫。现给视频页 / 列表页补上同样的定位钉死(absolute + top/left 0 + right/bottom auto + margin 0 + transform none + z-index auto),并用 :not(.video-fullscreen) 排除真全屏;另加 JS 兜底 bindVideoWebFullscreenGuard:加载后 12 秒内轮询 + MutationObserver,盯住容器 computed position 变 fixed(且无原生 fullscreenElement、无 video-fullscreen、无 player-full-win)时逐项 setProperty important 钉死,防站内把定位写成行内 !important 让 CSS 压不过v132.45 修复:仅在 via 以外的浏览器(Chrome / Edge / 夸克等)出现的「播放器被一整层遮罩糊死」——画面正中一个「电视机 + 播放/暂停」大图案,右上角一个 ×,底部压一条进度条,点不动也退不掉。探针实测为 .bpx-player-video-area > .bpx-player-mini-warp:338x190 与画面同尺寸铺满视频区、position:absolute、z-index:12 高于画面层,故整个画面被盖住;内部三个零件正好对上肉眼所见——.bpx-player-mini-close(右上 26x26 的 ×)、.bpx-player-mini-state(居中 100x100 的电视+暂停图案)、.bpx-player-mini-progress(底部 338x3 进度条)。它是站内「小窗播放」留在原位的占位层,本脚本不需要小窗占位,整层连子元素一并 display:none + visibility:hidden + pointer-events:none;选择器不带 body class,视频页 / 列表页 / 番剧页(同为 bpx 播放器)一次全中。另加 JS 兜底 bindMiniOverlayGuard:扫到仍可见的 mini-warp 时先派发一次 .bpx-player-mini-close 的 click 让站内自己退出小窗状态,再逐项 setProperty important 钉死(防站内把样式写成行内 !important 让 CSS 压不过),前 15 秒 MutationObserver + 定时扫描,只在视频页 / 列表页 / 番剧页运行。v132.46 修复:未登录(游客)态动态页右上角竖着一条约 18px 宽、从上拖到底的蓝条,占掉一竖行。探针实测 #app > div.bili-dyn-home--visitor > aside.right 为 246x1796 / left=342 —— 视口只有 360 宽,它从 342 排到 588,被 body 的 overflow-x:hidden 裁掉后只剩 342~360 这 18px 露在屏幕上,条内颜色来自它内部的 .bili-dyn-banner 广告图(246x143)。真因:隐藏左右栏那组选择器只写了 .bili-dyn-home--member(登录态),未登录时根节点类名是 .bili-dyn-home--visitor,整组规则一条都没命中,所以只有「没登录 + 非 via」才看得到。现用 [class*=bili-dyn-home] 把登录 / 未登录两种状态一次覆盖,left / right 一并清零(display/visibility/width/height/padding/margin 全归零),并补一条不带父级限定的 aside.left / aside.right 兜底防站内改类名再度漏出;另把主列父容器退回块流、gap 归零,main 才真正吃到 360 满宽(此前被 flex 挤到 left=12、只剩 318)。v132.47 新增:登录弹层(.bili-mini-mask)手机屏适配,并隐藏扫码登录区。探针实测:.bili-mini-content-wp 为 PC 定宽 820x460、rect left=-230,在 360 视口里左右各溢出 230,所以只能看到中间一块;其中的 .login-scan-wp(214x271 / left=-173,扫码区)整块在屏幕外,.bili-mini-close-icon(32x32 / left=538)关闭键整个跑出屏幕,.login-agreement-wp(620x38 / left=-130 + translate(-310))协议说明也被推出可视区。处理:① .login-scan-wp + .login-client-qr-code + .bili-mini-line(中间竖分割线)整块隐藏,手机用不到扫码;② 面板宽度收到 calc(100vw - 32px)(上限 340)并居中,内边距改 52/16/20 给关闭键留位;③ 关闭键钉回面板右上(top/right 12);④ 右半区 .bili-mini-login-right-wp / .login-pwd-wp / .login-sms-wp / .tab__form / .form__item 一律 100% 自适应,input 改 flex:1 1 auto 吃掉剩余宽度;⑤ 注册 / 登录两键 .btn_wp 改 flex 等分(gap 8);⑥ 三方登录 .login-sns-wp 整行居中、三项等分;⑦ 协议说明改回 static + 100% + transform none + 居中 + 12px。选择器一律不带 body class(弹层挂在 body 上,任何页面点登录都会出现),另加 JS 兜底 bindLoginPanelFit:动态插入的弹层可能被写成行内 !important,扫到 .bili-mini-content-wp 时逐项 setProperty important 钉死并隐藏扫码区,无弹层时立即返回。v132.48 修正登录弹层 v132.47 留下的三个问题(探针对比 登录面板_10-30-40 视频页 / 另一个页面_10-33-04 动态页,两页面板本体同为 328x898 / top=-49、表单区同为 296x293、三方登录同为 296x75):① 「密码」「忘记密码?」有的页面换行有的正常 —— 动态页 .form_info 28x20 / input 122 / .eye-btn 20 / .clickable 70 全正常,视频页 .form_info 24x40(竖排)/ input 138 / .eye-btn 17 / .clickable 61x40(两行):.form__item 并非 flex,四个零件按 inline 排、谁先挤爆谁换行,而 input 的固有宽度由站内按页面算出(138 vs 122),视频页多吃 16px 就把后两个挤爆,肉眼"还有空"是因为空间全被 input 的固有宽度占住。修法:.form__item 改 display:flex + row + nowrap + align-items:center,input 改 flex:1 1 0 + min-width:0(只吃真正剩下的),.form_info / .eye-btn / .clickable 改 flex:0 0 auto + white-space:nowrap。② 协议文字一字一行竖排 —— 探针实测 .login-agreement-wp 为 0x810 / left=328:.bili-mini-content-wp 站内是 display:flex(row),扫码区隐藏后 .bili-mini-login-right-wp 独占 296(left=32),协议区被挤到它右边且宽度塌成 0,于是每字一行,并把面板高度撑到 898(原本只需 ~400)。修法:.bili-mini-content-wp 改 display:block(+ flex-direction:column 双写防站内在行内改回 flex),两者上下堆叠,协议区 100% 宽 + 居中,正好落在「微信登录 / 微博登录 / QQ登录」按键下方。③ 面板没居中(top=-49 顶部被裁)—— 898 的面板在 800 视口里被 flex 的 align-items:center 居中,上下各溢出 49 顶部直接被裁。修法:面板改 margin:auto(内容小于容器时居中、大于容器时不裁顶且可完整滚动),再加 max-height:calc(100vh - 32px) + overflow-y:auto 双保险。另修部分页面出现的 .bili-mini-customer-title(820x26 / absolute)同样溢出,改 static + 100% + 居中。JS 兜底 loginPanelSweep 同步补这三处(行内 !important 会压过样式表)。v132.49 修复:动态 / 空间页展开图片后,点任意一张缩略图,底部那圈高亮框始终停在第一张上。探针实测(bili_probe_2026-10-02_11-02-04,bili-space-page 已展开那条):.bili-album__watch__track 310x54 / relative / left=25;.bili-album__watch__track__activeitemborder 58x58 / absolute / rect left=23 —— 即相对轨道的 css left 恒为 0(只叠加自身 transform translate(-2,-2)),永远钉在第 0 张;六张缩略图 left = 25 / 83 / 141 / 199 / 257 / 315,第 3 张带 .active(left=141);其余 9 条未展开的动态 .active 都落在第 1 项,所以平时看不出来。结论:.active 类站内在维护且指对了,错的只有高亮框 —— 站内没把「当前那张相对轨道的偏移」写回这个 absolute 元素,窄屏下算出来恒为 0(PC 上轨道够宽、第 0 张恰好居中,不易察觉)。修法:不猜站内的索引算法,直接用几何量重算 left = 当前项 rect.left − 轨道 rect.left,尺寸按当前项 +4(框比缩略图各大 2px)、transform 钉死 translate(-2px,-2px)、max-width:none 防被压变形,全部 setProperty important 写回;这样即便站内为把当前图滚进可视区而横向平移了缩略图列表,也能自动跟上。触发:点击缩略图 / 大图左右切换 / 查看器控件后用 requestAnimationFrame 逐帧跟 700ms(覆盖 active 切换与列表横滑动画),MutationObserver 监听 class / style 变化(页面上没有 .bili-album__watch__track 时立即返回,不做无谓开销),另加滚动兜底。CSS 侧只给 track 补 position:relative、给高亮框补 max-width:none 与 transform,位置交给 JS。v132.50 修复:图片查看器左右翻页箭头不出来(高亮框 v132.49 已修好,箭头仍不显示)。对照两份探针(bili_probe_2026-10-02_11-15-45 空间页 vs 对照.txt opus 详情页):opus 正常 content 296x690 / relative、prev 98x690 / absolute / left=32;space 异常 content 310x723 / relative、prev 102x723 / absolute / left=25。两处 DOM 结构、层级、定位方式、宽高比例(约 1/3)完全一致,prev 的 rect.left 也都正好等于 content 的 left,说明元素本身没跑位;站内没有 .arrow 之类子节点,prev / next 内部是空的(探针里 prev 下面直接就是 track),箭头只能由 CSS 伪元素或 background-image 画出。窄屏 + 触屏下不出来的两个典型原因一并兜住:① 站内把箭头做成 :hover 才 opacity:1 —— 触屏没有 hover,永远不显示;② 站内箭头是伪元素 / 背景图,被窄屏挤压或 overflow 吃掉。处理:CSS 无条件把 prev / next 拉回可见(display/visibility/opacity/z-index/pointer-events/overflow),并把父级 hover、自身 hover 都显式写一遍 opacity:1 解掉 hover 依赖;箭形本身不在 CSS 里硬画(怕和站内 ::before 重出两个箭头),交给 JS 探测:prev / next 内既没有可见子节点、::before 与 ::after 的 content 与 background-image 也都为空时,才注入一个 .bili-album-arrow-fix 的 chevron(CSS 旋转 -45°/135° + 白色描边 + drop-shadow),未展开时(0x0)跳过,不重复注入。另修:空间页查看器顶部控制栏(收起 / 查看大图 / 向左旋转 / 向右旋转)—— 探针实测 option 内 span 被压成 17x50、34x50(文字竖排两行,比 32 高的选项本身还高),是站内 inline 排布 + 宽度不足所致,改 inline-flex + row + nowrap + 居中 + gap 4,span 补 nowrap、svg 补 flex:0 0 auto,图标与文字回到一行。v132.51 修正图片查看器 v132.49/50 留下的两处裁剪,并收敛 JS 兜底的性能开销。【A 缩略图轨道:第 6 张超出被裁剪、右箭头看不到】关键发现:站内的左右翻页箭头是 .bili-album__watch__track__prev / __next(12x54 / absolute / z=10),不是大图两侧的 content__prev / __next —— v132.50 只处理了后者,压根没覆盖到。但箭头"看不到"只是表象,真因在列表没翻页:opus 页(正常)list 是 translateX(-290),已把第 5 张滚进可视区;动态页(异常)list 是 translateX(0),站内压根没平移,于是第 6 张仍停在 left=315(315~369 超出轨道右边界 335),高亮框框住的是裁剪区外的空位,箭头又钉在轨道右缘,跟着一起被裁 —— 所以看起来像"箭头没显示"。修法三步(全部按实测几何量算,不猜站内算法):① albumTrackFitWindow —— 取相邻两张的 left 差得步进 step(实测 58 = 54 + 4 间距), n = floor((trackW + gap) / step),把 slider 宽度收到 n*step - gap,让第 6 张不再露半截;② albumTrackScrollActiveIn —— 用 DOMMatrixReadOnly 读 list 当前 translateX, active 项不在可视窗口内时按差额追加平移(沿用站内已有的 transform),补上动态页缺失的翻页行为;③ albumTrackPinArrows —— 把 prev / next 钉在轨道内两端、z-index 20; 注意只在它本身有尺寸时才校位置,0x0 代表站内主动隐藏(如已到最后一张),不能强行显示。顺带把高亮框也纳入同一条 sync 链(窗口 → 滚入 → 箭头 → 高亮框,顺序不能反)。【B 「向右旋转」被裁剪】这是 v132.50 自己引入的回归:为让文字不竖排给 option 加了 inline-flex + nowrap,四个按键从 62/78/78/78(正好塞满 296)被撑成 80/104/104/104 = 392,超过容器(opus 296 / 动态页 310),第 4 个「向右旋转」被挤到 left=313、右边界 417,既超容器也超 360 视口。修法:内容压紧(字号 12px、图标 12x12、gap 2、左右 padding 4,四个按键约 260 宽)+ 容器兜底(control 改 flex + nowrap + overflow-x:auto 且隐藏滚动条)。【C 性能收敛】此前每个兜底各自起一个常驻 MutationObserver,再加一条 1.5s 的永久 setInterval(loginPanelSweep),在弹幕密集页会叠成一堆回调。现收敛为单例 observer(installDomGuardObserver):只维护一个 120ms debounce 定时器,到点后依次问各 guard 的 test()(一次廉价 querySelector),页面上没有目标元素就直接跳过 —— 视频页既无登录弹层也无查看器时,一轮只花两次 querySelector;同时删掉 login 的永久 setInterval,scroll 监听改 passive。v132.44 / v132.45 那两个 guard 本来就是前 15 秒后自动 disconnect,保持原样。v132.52 修复:点「查看大图」后全屏遮罩出来了(黑底、关闭键都在)但图是空的,任何页面都可能出现。这是 B 站的全屏看图器 PhotoSwipe(.pswp),脚本此前零覆盖。探针实测对比(图片正常显示.txt 动态页 vs 未显示出图片的探针.txt opus 详情页):正常页 .pswp__zoom-wrap transform matrix(1,0,0,1,0,160),正好是 Y 居中偏移 (800-480)/2=160,.pswp__img 有尺寸 360x480;异常页 .pswp__zoom-wrap transform matrix(1,0,0,1,80,0),X 偏移 (360-201)/2≈80 也是对的,但 .pswp__zoom-wrap 与 .pswp__img 双双是 0x0 —— 图片根本没渲染出来。结论:站内把「贴合视口」的尺寸(长图应得约 201x800)和居中偏移都算对了,唯独把 width / height 写成了 0。修法:只补尺寸,绝不碰它已算好的 transform(那是居中偏移,改了图会跑位)。JS pswpImageFit 按 naturalWidth / naturalHeight 现算 scale = min(vw/nw, vh/nh)(vw / vh 取 .pswp 根的 clientWidth / clientHeight),把结果用 setProperty important 写回 .pswp__img 的 width / height,并同步补 .pswp__zoom-wrap 的 width / height;只在「渲染尺寸为 0 且 naturalWidth 已就绪」时介入,尺寸正常的图一个字节都不动;naturalWidth 为 0(未加载完)时跳过,等 load 事件。触发三路:① 捕获阶段监听 load(img 的 load 不冒泡,但捕获能收到)→ 跟 400ms;② 点「查看大图」(.full-screen) 或点灯箱内任意处 → 逐帧跟 900ms;③ 注册到统一 DOM 守卫(test 为 .pswp__img 是否存在),灯箱未打开时零成本。CSS 只放一条无害兜底 .pswp__img { object-fit: contain } —— 尺寸正确时本就无变化,JS 还没跑到时也能避免图片被拉伸变形。v132.53 修正 v132.52 遗留:多图相册点开全屏看图器后整组图仍是 0x0。v132.52 只在 img.naturalWidth 就绪时才补尺寸,而多图相册里 .pswp__img 常常还没加载(naturalWidth 为 0),于是直接跳过、图继续不显示;上一版能修好的是「单张长图」那种早已加载完的情况,所以出现"有的好了有的还是不行"。新增第二条取尺寸通路 —— 从 .pswp__zoom-wrap 的居中偏移反推,不依赖图片是否加载:tx = (vw - w) / 2 → w = vw - 2 * tx;ty = (vh - h) / 2 → h = vh - 2 * ty。探针验证(异常图片位置2.txt,4 图相册「1 / 4」,视口 360x800,三个 .pswp__img 全为 0x0):zoom-wrap transform = matrix(1,0,0,1,0,282) → w = 360 - 0 = 360,h = 800 - 564 = 236;回头验算 v132.52 那张长图:transform = matrix(1,0,0,1,80,0) → 200x800,同样吻合。即这个偏移本身就是站内按「贴合视口后的尺寸」算出来的,与 naturalWidth 推导结果一致。安全约束:只有出现信箱边(tx 或 ty 有留白,> 0.5px)时才采信反推值,避免两个偏移都是 0 时反推出 360x800 这种错误满屏尺寸;naturalWidth 就绪时仍优先走原比例通路,反推仅作兜底;两条路都拿不到就跳过等下一次,不做无根据的猜测。transform 依旧一个字节都不动 —— 那是居中偏移,改了图会跑位。v132.54 修正 v132.53 漏掉的第三种情形(用户提示:显示不出来时触碰一下屏幕就正常显示了,线索在探针文件名上)。关键对比(显示不出来.txt vs 显示不出来的情况下触碰一下屏幕正常显示了.txt,同一张「长图」,全文仅 4 行不同):未触碰 .pswp__zoom-wrap [0x0] / transform matrix(1,0,0,1,0,0);已触碰 .pswp__zoom-wrap [360x800] / transform matrix(1,0,0,1,0,0)。→ 两份的 transform 都是 (0,0),而正解就是 360x800 = vw x vh(视口满屏)。v132.53 的反推通路要求 tx > 0.5 或 ty > 0.5 才采信(防两个偏移都是 0 时反推出错误满屏),恰恰把这种情形挡掉了 —— 这就是"还有地方不行"的原因。两处改动:① 替用户"碰一下" —— PhotoSwipe 自己就监听 window resize 并调用 updateSize 重算, 这等价于触碰触发的那次重算,且零交互副作用 (不能派发 tap:灯箱带 pswp--click-to-zoom,点了会缩放甚至把灯箱关掉)。 每个灯箱实例只派发一次(标记挂在 root 上),避免反复触发。② 尺寸兜底补第三通路 —— naturalWidth 未就绪、且 zoom-wrap 偏移为 (0,0) 时, 采信视口满屏 vw x vh,正是探针实证的正解; 配合 CSS 的 object-fit:contain,即使该图比例不是满屏比例也只会留白、绝不拉伸变形。 至此三条通路覆盖全部已观测情形: 原比例贴合(长图 200x800)/偏移反推(四图相册 360x236)/视口满屏(本次 360x800)。另把统一 DOM 守卫的 test 从 .pswp__img 收紧为 .pswp.pswp--open .pswp__img —— 灯箱 DOM 常驻 body,只看前者会一直命中、导致无谓空跑。transform 依旧一个字节都不动 —— 那是居中偏移,改了图会跑位。v132.55 定位到【真因】:这是本脚本自己造成的回归,不是站内 bug(用户关键提示:「不开脚本没有这个 bug」),v132.52/53/54 全是在给一个本不该存在的伤口贴创可贴。真凶是脚本自己的三条通用 img 规则:body.bili-search-page img / body.bili-opus-page img / body.bili-space-page img,内容为 max-width:100% !important + height:auto !important(opus 版多一条 display:block)。PhotoSwipe 的灯箱 .pswp 挂在 body 直接子级,正好落在这三条的射程内,于是:① height:auto !important 压过站内给 .pswp__img 设的行内 height → 图塌成 0 高;② 父元素 .pswp__zoom-wrap 随之塌成 0;③ max-width:100% 的参考系正是这个已塌成 0 的父元素 → img 宽度也被压成 0;④ 结果 .pswp__zoom-wrap 与 .pswp__img 双双 0x0,黑底和关闭键都在、唯独图是空的。这一条链同时解释了此前全部现象:不开脚本就没 bug;search / opus / space 三个页面都会中招(各有一条同名规则);「触碰一下就正常」是因为 PhotoSwipe 重算时走了另一条路径、绕开了被压成 0 的父元素;不同图片表现不同,是因为触碰时机与加载时机各不相同。修法(根本修复,不再猜尺寸):给这三条规则的选择器统一加 :not([class*="pswp"]),把灯箱子树排除在通用 img 规则之外,站内的行内 width/height 随即正常生效。另外两条同类规则不必改:body.bili-bangumi-page .plp-r-wrap img 有具体容器限定;body.bili-dynamic-page / body.bili-space-page 的 .bili-dyn-item img 也限定在卡片内,灯箱不在其中。再加一道防御:显式声明 .pswp img / video / picture 的 max-width:none,将来若新增宽泛 img 规则也不至于再把它压成 0(只解除限制,绝不写 width / height,尺寸仍归站内 JS)。v132.52~54 的 pswpImageFit 兜底保留但降级为保险 —— 真因已除,它应当永不触发;仅在灯箱已打开时才跑一次 querySelector,开销可忽略。v132.56 新增:隐藏音乐播放器页(bili-music-page)的「展开更多」按键。探针实测(展开更多按键目前没作用隐藏掉.txt):.song-introText [48x17] / left=8 / top=131,文字正是「展开更多」,紧贴在 .song-content.song-fold(简介正文 344x66)下方。它在手机端点了没反应 —— 站内的展开判断依赖 PC 版容器宽度,窄屏下算不出可展开状态,留着只会诱导用户反复去点,故整块隐藏(display/visibility/高度/内外边距/边框全归零,并关掉 pointer-events 防止隐藏后仍占据点击热区)。选择器只认类名、不带 DOM 层级 —— 音乐页是 SPA、节点插入时机不定,body 上有 bili-music-page 类即可命中,无需 JS 兜底。附带处理:按键既已隐藏,被 song-fold 截断的简介就再无途径展开,因此顺带解掉行数截断让全文显示 —— 脚本此前只解了 max-height / overflow,实测正文仍是 344x66(约三行),说明真正生效的是 -webkit-line-clamp;补 display:block(line-clamp 只在 -webkit-box 下起作用)+ line-clamp:unset + text-overflow:clip,作用于 .song-content / .song-content.song-fold / .content-span。若只想隐藏按键、保留三行截断,把这一组附带规则删掉即可。v132.57 新增:隐藏直播间播放器里的「点击取消静音」提示。探针实测(bili_probe_2026-10-02_13-10-34):#live-player > div.ntMzdJGK5xCLVyRrSZKp [150x32] / left=41 / top=187、absolute、z-index:40,内含 img.LvQXQu436nrg7xhvy0gR [18x18](喇叭图标)与 p.Vei429vuPGSkiUk6mvNg [95x32](文字「点击取消静音」)。这是浏览器自动播放策略导致的静音提示,手机端不需要(真要出声点播放器上的喇叭或音量键即可),留着反而挡在画面底部中央。难点:该层类名是 CSS Module 哈希(ntMzdJGK5xCLVyRrSZKp),站内每次构建都会换,CSS 写不死;故沿用 .bili-live-ctx-menu 那套思路 —— JS 按文本识别后打上稳定的 .bili-live-mute-toast,由样式表负责隐藏(display/visibility/尺寸/边距/opacity 全归零 + pointer-events:none)。性能取舍(弹幕页最怕空转):扫描范围只限 #live-player 的直接子级,且先过一道廉价前置过滤 —— 只看 DIV、恰好 2 个子节点、同时含 img 与 p(提示层就是「图标 + 一行文字」),全部通过才读文本并匹配「取消静音」;因此绝不会去读 .web-player-danmaku 那棵装着成百上千条弹幕的子树,一轮只有十几次 O(1) 判断,即便统一守卫每 120ms 批量触发一次也可忽略。命中后打 data-bili-mute-hidden,后续轮次直接跳过、不重复写样式;播放器异步挂载,首屏补 0/150/400/900/1800ms 五拍,之后交给统一守卫。v132.58 新增:合集详情页(空间页 → 合集和系列 → 打开某个合集,main.space-main.route_listDetails)分页条「上一页 1 2 3 4 5 下一页」上下间距统一为 16px。探针实测:.list-footer [344x110] / top=674 bottom=784 / pt=0 pb=60;其内 .vui_pagenation [344x50] / top=674 bottom=724 / pt=8 pb=8;按键本体 .vui_pagenation--btns [344x34] / top=682 bottom=716。按现状算:上间距 = 按键顶 682 − 末行卡片底 674 = 8px;下间距 = 容器底 800 − 按键底 716 = 84px(分页条 pb 8 + footer pb 60 + 容器 pb 16 三处叠加)。根因是间距被三个元素分摊且职责混乱 —— footer 那 60px 是 PC 版留给页面底部的余量,手机上是纯白占;容器(.space-list-details)脚本自己加的 pb=16 又和分页条的 pb 叠在一起。改法:把间距收归分页条一个元素负责 —— .list-footer 的 padding 全清零(干掉 PC 版那 60px)、.space-list-details 的 padding-bottom 清零(避免与分页条 pb 叠加)、.vui_pagenation 的 padding 改 16px 0。改后几何:按键 top=690 / bottom=724,容器底 740 → 上 16、下 16,对称。选择器走完整链路(body.bili-space-page main.space-main.route_listDetails > .list-footer > .vui_pagenation),特异性高于既有的「body.bili-space-page .vui_pagenation { padding: 8px 0 }」,无需 !important 也能压过;另加一条 [class*=pagenation] 兜底,防站内改类名。只作用于合集详情页这一个路由,不影响空间主页 / 关注 / 粉丝页的分页条。v132.59 新增:空间页「投稿 → 图文」(route_uploadOpus)里只有文字没有封面的卡片,改为与带封面的图文卡片完全一致,并补一块蓝色占位封面。探针实测:带封面卡 .opus-card--with-cover[168x172] = a.opus-card__cover[168x95](封面 + 左下角点赞数)+ a.opus-card__title[168x77](pt/pb 8,文字 3 行 60 居中);纯文字卡 .opus-card--only-text 只有 [168x65] —— a > .opus-card__text[148x20] + .opus-card__icons[148x17](mt 8),两者同处 .item[168x172] 中,一高一矮很不整齐。改法(纯 CSS,无需 JS —— 类名 .opus-card--only-text 稳定、非哈希):① 卡片高度拉到 172 并 padding-top:95px 让出封面位 → 余下 172 − 95 = 77, 正好等于带封面卡片标题区的高度,两者几何完全对齐;② ::before 画占位封面 —— absolute 钉在顶部 95 高、满宽,flex 居中放「未发现封面图」, 浅蓝底 #e6f4ff + B站蓝字 #00a1d6,圆角 8px 8px 0 0 与 .opus-card__cover 一致;③ 文字区 a 与 .opus-card__title 同款(77 高 / 上下 8 / 文字 3 行 60px 居中), 文字长度不同也不会破坏卡片高度;④ 点赞数 .opus-card__icons 从文字下方挪到占位封面左下(top 74 / left 8,17 高 → 距封面底 4px), 与带封面卡片里 .bili-cover-card__stats 压在封面左下角的位置对齐,数字不再丢失。细节:::before 与 icons 都加 pointer-events:none,绝不挡文字链接或任何点击;::before 设 z-index 0、icons 设 1,保证数字压在占位块之上;占位块圆角只做上方两角,与封面一致,下方由卡片自身的 8px 圆角 + overflow:hidden 收尾。 ============================================================ */ const isHome = () => { const h = location.hostname, p = location.pathname; return (h === 'www.bilibili.com' || h === 'bilibili.com') && (p === '/' || p === '/index.html' || p === ''); }; const isVideoPage = () => location.hostname === 'www.bilibili.com' && /^\/video\//.test(location.pathname); // 收藏夹/合集播放列表页(/list/mlxxx、/medialist/play/mlxxx):DOM 与视频页同构,完整复用视频页优化 const isListPage = () => location.hostname === 'www.bilibili.com' && (/^\/list\//.test(location.pathname) || /^\/medialist\/play\//.test(location.pathname)); const isBangumiPage = () => location.hostname === 'www.bilibili.com' && /^\/bangumi\/play\//.test(location.pathname); /* v132.64:话题页(点标签 / 话题进来,如 /v/topic/detail/?topic_id=xxx)。 URL 前缀站内可能改,故同时用页面根元素 .topic-detail 做 DOM 兜底。 document-start 时 body 可能还没建,querySelector 一律包在 try 里。 */ const isTopicPage = () => { const h = location.hostname, p = location.pathname; if ((h === 'www.bilibili.com' || h === 'bilibili.com') && /^\/v\/topic\/detail\//.test(p)) return true; try { return !!(document.body && document.body.querySelector('.topic-detail')); } catch (e) { return false; } }; const isTBiliDynamic = () => location.hostname === 't.bilibili.com'; /* v132.37:音乐播放器页(歌曲详情,如 /audio/au1234、/music/au1234)。 这一页脚本此前**完全没有适配**,body 上没有任何 bili-* 页面 class。 底部播放器 #music-container.audioplayer 只在本页出现,用它做 DOM 兜底, 避免以后站内改 URL 前缀(/audio ↔ /music)后判定失效。 */ const isMusicPage = () => { const h = location.hostname, p = location.pathname; if (h === 'www.bilibili.com' || h === 'bilibili.com') { if (/^\/(audio|music)\//.test(p)) return true; } try { return !!document.querySelector('#music-container.audioplayer'); } catch (e) { return false; } }; const isOpusPage = () => { const h = location.hostname, p = location.pathname; return (h === 'www.bilibili.com' || h === 'bilibili.com') && (/^\/opus\//.test(p) || /^\/read\//.test(p)); }; const isSearchPage = () => location.hostname === 'search.bilibili.com'; const isSpacePage = () => location.hostname === 'space.bilibili.com'; // 直播间页(live.bilibili.com/房间号、live.bilibili.com/blanc/房间号) // 探针 bili_probe_2026-09-30_08-03-19 实测结构: // body.supportWebp > div.live-room-app > main.app-content(padding-top 74) // > div.app-body > section.player-and-aside-area // > #player-ctnr(#head-info-vm + .fullscreen-container-paddingbox > #fullscreen-container > .player-section / #gift-control-vm) // > #aside-area-vm(absolute 右侧:#rank-list-vm + .chat-history-panel + #chat-control-panel-vm) // > #sections-vm(动态/公告)> footer.link-footer // 只认房间号路径,直播中心 / 活动页等不处理 const isLiveRoomPage = () => { if (location.hostname !== 'live.bilibili.com') return false; const p = location.pathname; if (/^\/(live-center|studio|activity|blackboard|h5|p)(\/|$)/.test(p)) return false; return /^\/blanc\/\d+/.test(p) || /^\/\d+(\/|$)/.test(p); }; function installShadowRootPatch() { if (installShadowRootPatch._installed) return; installShadowRootPatch._installed = true; const originalAttachShadow = Element.prototype.attachShadow; Element.prototype.attachShadow = function(init) { const shadowRoot = originalAttachShadow.call(this, init); // 动态详情页(t.bilibili.com/xxx)与 opus 详情页(/opus/xxx)评论区 // 都与视频页同源(bili-comments),一并注入才能做到"与视频页完全一致" if (init.mode === 'open' && (isVideoPage() || isBangumiPage() || isListPage() || isTBiliDynamic() || isOpusPage())) { try { const style = document.createElement('style'); style.setAttribute('data-bili-mobile-hidden', 'true'); style.textContent = ` bili-comment-box, #commentbox { display: none !important; visibility: hidden !important; height: 0 !important; } #like, #dislike, #reply, #more { display: none !important; visibility: hidden !important; height: 0 !important; } bili-comments-vote-card, #vote { display: none !important; visibility: hidden !important; height: 0 !important; } bili-comments-header-renderer { margin-bottom: 0 !important; padding-bottom: 0 !important; } #header { margin-bottom: 0 !important; padding-bottom: 0 !important; } #contents { margin-top: 0 !important; padding-top: 0 !important; } `; shadowRoot.appendChild(style); } catch (e) {} } return shadowRoot; }; } function fixViewport() { const apply = () => { const root = document.head || document.documentElement; if (!root) return; let vp = document.querySelector('meta[name="viewport"]'); if (!vp) { vp = document.createElement('meta'); vp.name = 'viewport'; root.appendChild(vp); } const content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover'; if (vp.getAttribute('content') !== content) { vp.setAttribute('content', content); } }; apply(); [0, 50, 200, 500, 1000, 2000, 5000].forEach(t => setTimeout(apply, t)); document.addEventListener('DOMContentLoaded', apply); window.addEventListener('load', apply); try { const obs = new MutationObserver(() => apply()); obs.observe(document.head || document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['content'] }); } catch (e) {} } function injectStyle() { if (document.getElementById('bili-mobile-style')) return; const root = document.head || document.documentElement; // document-start 时 documentElement 可能还没建好 —— 这种情况下 // 必须重试,否则整份样式表永远注入不进来(首屏就会全裸) if (!root) { setTimeout(injectStyle, 0); return; } const style = document.createElement('style'); style.id = 'bili-mobile-style'; style.textContent = ` /* ===== 全局 ===== */ html, body { background-color: #ffffff !important; width: 100% !important; max-width: 100vw !important; min-width: 0 !important; overflow-x: hidden !important; margin: 0 !important; padding: 0 !important; overscroll-behavior-y: none !important; } html { background-color: #ffffff !important; } * { box-sizing: border-box !important; } body:not(.video-fullscreen) > * { max-width: 100vw !important; } .bili-header, .international-header, .mini-header, #biliMainHeader, .bili-header-m, .nav-menu { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; } #bili-header-container, .bili-header--mini, .bili-header--fixed { display: none !important; height: 0 !important; overflow: hidden !important; } /* ===== 自建搜索栏 ===== */ #my-bili-searchbar { position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; width: 100% !important; max-width: 100vw !important; height: 52px !important; background: #ffffff !important; box-shadow: 0 1px 4px rgba(0,0,0,0.08) !important; z-index: 2147483646 !important; display: flex !important; align-items: center !important; padding: 0 10px !important; box-sizing: border-box !important; gap: 8px !important; } #my-bili-dynamic-btn { flex-shrink: 0 !important; display: flex !important; align-items: center !important; justify-content: center !important; height: 36px !important; padding: 0 10px !important; background: #f1f2f3 !important; border-radius: 18px !important; color: #18191c !important; text-decoration: none !important; cursor: pointer !important; white-space: nowrap !important; user-select: none !important; } #my-bili-dynamic-btn:active { background: #e3e5e7 !important; } /* 同上:显式写死同一个色值,避免与右侧再次出现深浅差 */ #my-bili-dynamic-btn svg { width: 18px !important; height: 18px !important; flex-shrink: 0 !important; color: #18191c !important; } #my-bili-searchbar .my-search-wrap { flex: 1 !important; display: flex !important; align-items: center !important; background: #f1f2f3 !important; border-radius: 20px !important; height: 36px !important; padding: 0 6px 0 14px !important; overflow: hidden !important; min-width: 0 !important; } #my-bili-searchbar #my-bili-search-input { flex: 1 !important; border: none !important; outline: none !important; background: transparent !important; font-size: 14px !important; color: #18191c !important; height: 100% !important; padding: 0 !important; margin: 0 !important; min-width: 0 !important; -webkit-appearance: none !important; appearance: none !important; } #my-bili-searchbar #my-bili-search-input::placeholder { color: #9499a0 !important; } #my-bili-searchbar .my-search-btn { width: 32px !important; height: 32px !important; border-radius: 50% !important; border: none !important; background: transparent !important; color: #61666d !important; display: flex !important; align-items: center !important; justify-content: center !important; cursor: pointer !important; padding: 0 !important; flex-shrink: 0 !important; } /* v132.08:只留左侧图标(文字与右侧小箭头都隐藏), 按钮收成正圆 36x36,把宽度全让给中间的搜索框。 aria-label 仍在,读屏与无障碍不受影响。 */ #my-bili-order-btn { flex-shrink: 0 !important; display: flex !important; align-items: center !important; justify-content: center !important; gap: 0 !important; width: 36px !important; min-width: 36px !important; max-width: 36px !important; height: 36px !important; padding: 0 !important; background: #f1f2f3 !important; border-radius: 50% !important; color: #18191c !important; font-size: 13px !important; cursor: pointer !important; white-space: nowrap !important; user-select: none !important; -webkit-tap-highlight-color: transparent !important; } #my-bili-order-btn:active { background: #e3e5e7 !important; } /* v132.13:左侧(排序/动态)与右侧(切换)图标同色。 原先左边显式写了 #61666d(灰),右边没写 color、 继承按钮的 #18191c(近黑),两个键摆在一起明显一深一浅。 这里统一成 #18191c,与右侧完全一致。 注意:展开时变蓝由后面那条 :checked 规则负责,不受影响。 */ #my-bili-order-btn svg { width: 18px !important; height: 18px !important; flex-shrink: 0 !important; color: #18191c !important; } /* ===== 搜索页:「切换」按钮 + 展开行 ===== 展开/收起是纯 CSS:两个隐藏 checkbox 打头, :checked ~ 兄弟选择器控制下方那一行 display。 JS 只负责生成 HTML、互斥、点击外部收起、构造跳转链接。 */ .my-bili-menu-cb { position: absolute !important; width: 0 !important; height: 0 !important; opacity: 0 !important; pointer-events: none !important; margin: 0 !important; padding: 0 !important; border: none !important; appearance: none !important; -webkit-appearance: none !important; } #my-bili-switch-btn { flex-shrink: 0 !important; display: flex !important; align-items: center !important; justify-content: center !important; gap: 0 !important; width: 36px !important; min-width: 36px !important; max-width: 36px !important; height: 36px !important; padding: 0 !important; background: #f1f2f3 !important; border-radius: 50% !important; color: #18191c !important; font-size: 13px !important; cursor: pointer !important; white-space: nowrap !important; user-select: none !important; -webkit-tap-highlight-color: transparent !important; } #my-bili-switch-btn:active { background: #e3e5e7 !important; } #my-bili-switch-btn svg { width: 18px !important; height: 18px !important; flex-shrink: 0 !important; } /* ---- 只留图标:文字标签与右侧小箭头一并隐藏 ---- */ #my-bili-order-btn .order-label, #my-bili-switch-btn .switch-label, #my-bili-order-btn .my-bili-menu-caret, #my-bili-switch-btn .my-bili-menu-caret { display: none !important; width: 0 !important; height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* 小箭头没了,展开状态改由「底色 + 图标变蓝」表示,不占额外宽度 */ #my-bili-order-cb:checked ~ #my-bili-order-btn, #my-bili-switch-cb:checked ~ #my-bili-switch-btn { background: #e3e5e7 !important; } #my-bili-order-cb:checked ~ #my-bili-order-btn svg, #my-bili-switch-cb:checked ~ #my-bili-switch-btn svg { color: #00aeec !important; } /* v132.26:.is-open 兜底 —— 展开状态除了 :checked 兄弟选择器, 再由 JS 显式打一份 class。两条路都通向同一个样式, 即便兄弟选择器因 DOM 结构变化失效,底色 / 蓝色图标 / 菜单行 也仍然是三位一体,不会出现"菜单开了但图标没变蓝"。 */ #my-bili-order-btn.is-open, #my-bili-switch-btn.is-open { background: #e3e5e7 !important; } #my-bili-order-btn.is-open svg, #my-bili-switch-btn.is-open svg { color: #00aeec !important; } #my-bili-order-row.is-open, #my-bili-switch-row.is-open { display: flex !important; } /* 展开行:紧贴搜索框容器(52px)下方,高度与容器一致 52px */ .my-bili-menu-row { display: none !important; position: absolute !important; top: 52px !important; left: 0 !important; right: 0 !important; width: 100% !important; height: 52px !important; padding: 0 10px !important; box-sizing: border-box !important; background: #ffffff !important; box-shadow: 0 2px 6px rgba(0,0,0,0.10) !important; align-items: center !important; gap: 8px !important; z-index: 1 !important; } #my-bili-order-cb:checked ~ #my-bili-order-row, #my-bili-switch-cb:checked ~ #my-bili-switch-row { display: flex !important; } .my-bili-menu-item { flex: 1 1 0 !important; min-width: 0 !important; height: 36px !important; display: flex !important; align-items: center !important; justify-content: center !important; background: #f1f2f3 !important; border-radius: 18px !important; color: #61666d !important; font-size: 13px !important; text-decoration: none !important; cursor: pointer !important; white-space: nowrap !important; overflow: hidden !important; -webkit-tap-highlight-color: transparent !important; } .my-bili-menu-item:active { background: #e3e5e7 !important; } /* 当前所在的那个分类 / 排序:高亮 */ .my-bili-menu-item.is-current { background: #00aeec !important; color: #ffffff !important; font-weight: 600 !important; } /* ============================================================ ========== 首页 ========== ============================================================ */ body.bili-home-page { padding-top: 52px !important; } body.bili-home-page .banner, body.bili-home-page .bili-banner, body.bili-home-page .carousel, body.bili-home-page .carousel-container, body.bili-home-page .carousel-area, body.bili-home-page .carousel-area-img, body.bili-home-page .recommend-banner, body.bili-home-page [class*="carousel"], body.bili-home-page [class*="vui_carousel"], body.bili-home-page .recommended-swipe, body.bili-home-page .recommended-swipe-body, body.bili-home-page .recommended-swipe-body-normal, body.bili-home-page .floor, body.bili-home-page .bili-floor, body.bili-home-page .floor-single-card, body.bili-home-page .fixed-sidebar, body.bili-home-page .go-top, body.bili-home-page .bili-fixed-sidebar, body.bili-home-page .footer, body.bili-home-page .bili-footer, body.bili-home-page .international-footer, body.bili-home-page .mini-footer, body.bili-home-page .ad-report, body.bili-home-page .ad-floor, body.bili-home-page .channel-menu, body.bili-home-page .nav-channel, body.bili-home-page .bili-header__channel, body.bili-home-page .bili-header__channel-sticky, body.bili-home-page .header-channel, body.bili-home-page .bili-header-channel, body.bili-home-page .nav-channel-list, body.bili-home-page [class*="channel"][class*="header"], body.bili-home-page [class*="header"][class*="channel"], body.bili-home-page .header-nav, body.bili-home-page .nav-bar, body.bili-home-page .roll-btn, body.bili-home-page .roll-btn-text, body.bili-home-page .feed-roll-btn, body.bili-home-page [class*="roll-btn"], body.bili-home-page .bili-header__menu, body.bili-home-page .bili-header__bar, body.bili-home-page .bili-header-banner-img, body.bili-home-page .banner-img, body.bili-home-page .animated-banner, body.bili-home-page .taper-line, body.bili-home-page .palette-button-outer, body.bili-home-page .palette-feed4, body.bili-home-page [class*="right-side"], body.bili-home-page [class*="right-bar"], body.bili-home-page [class*="right-tool"], body.bili-home-page [class*="side-tool"], body.bili-home-page [class*="toolbar"], body.bili-home-page [class*="feedback"], body.bili-home-page [class*="customer-service"], body.bili-home-page [class*="service-btn"], body.bili-home-page [class*="back-top"], body.bili-home-page [class*="goto-top"], body.bili-home-page [class*="to-top"], body.bili-home-page .go-top, body.bili-home-page .bili-go-top { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-home-page .container, body.bili-home-page .container.is-version8, body.bili-home-page .recommended-container_floor-aside .container { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; grid-grid-gap: 8px !important; gap: 8px !important; width: 100% !important; max-width: 100% !important; padding: 8px !important; margin: 0 !important; background-color: #f1f2f3 !important; box-sizing: border-box !important; align-items: start !important; align-content: start !important; grid-auto-rows: auto !important; } body.bili-home-page .bili-feed4, body.bili-home-page .bili-feed4-layout, body.bili-home-page .feed2, body.bili-home-page .recommended-container_floor-aside { width: 100% !important; max-width: 100% !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-home-page .bili-feed4 * { max-width: 100% !important; } body.bili-home-page .container > .feed-card, body.bili-home-page .container > .bili-feed-card { display: contents !important; } body.bili-home-page .container > .feed-card:not(:has(.bili-video-card__wrap)):not(:has(.bili-live-card__wrap)), body.bili-home-page .container > .bili-feed-card:not(:has(.bili-video-card__wrap)):not(:has(.bili-live-card__wrap)), body.bili-home-page .container > .feed-card:has(.bili-video-card__info--ad), body.bili-home-page .container > .feed-card:has(.bili-video-card__info--creative-ad), body.bili-home-page .container > .feed-card:has([href^="//cm.bilibili.com/cm/api/"]), body.bili-home-page .container > .bili-feed-card:has(.bili-video-card__info--ad), body.bili-home-page .container > .bili-feed-card:has(.bili-video-card__info--creative-ad), body.bili-home-page .container > .bili-feed-card:has([href^="//cm.bilibili.com/cm/api/"]) { display: none !important; } /* ============================================================ ========== 搜索页 ========== ============================================================ */ html:has(body.bili-search-page), body.bili-search-page { width: 100vw !important; max-width: 100vw !important; min-width: 0 !important; overflow-x: hidden !important; padding: 0 !important; margin: 0 !important; background-color: #f1f2f3 !important; } body.bili-search-page { padding-top: 52px !important; } /* ===== 搜索页容器链统一背景色(v131.74)===== body.bili-search-page 早已是 #f1f2f3,但专栏页 / 用户页的中间容器 (.search-content / .search-page 等)自带白色背景把它盖住了, 结果加上的白卡与页面同色、完全看不出来。 综合页之所以正常,是因为它挂了 B站原生的 .search-content--gray。 这里把整条容器链(含综合页)统一强制成同一个 #f1f2f3, 既让专栏页白卡凸显,也保证与综合页字面一致。 ⚠ 只作用于容器,不碰 .media-list / .video-list / 卡片本体, 否则会把刚加的白卡也刷成灰色。 ---- */ html body.bili-search-page #app, html body.bili-search-page #app > div, html body.bili-search-page #app .search-layout, html body.bili-search-page #app .search-layout.clearfix, html body.bili-search-page #app .search-layout.clearfix > div, html body.bili-search-page #app .search-content, html body.bili-search-page #app .search-content--gray, html body.bili-search-page #app .search-content--gray.search-content, html body.bili-search-page #app .search-page-wrapper, html body.bili-search-page #app .search-page, html body.bili-search-page #app .search-page.search-page-all, html body.bili-search-page #app .search-page.search-page-article, html body.bili-search-page #app .search-page.search-page-upuser, html body.bili-search-page #app .search-page.search-page-live, html body.bili-search-page #app .search-page > div, html body.bili-search-page #app .video.i_wrapper, html body.bili-search-page #app .video.i_wrapper.search-all-list, body.bili-search-page #app, body.bili-search-page #app > div, body.bili-search-page .search-layout, body.bili-search-page .search-layout > div, body.bili-search-page .search-content, body.bili-search-page .search-content--gray, body.bili-search-page .search-page-wrapper, body.bili-search-page .search-page, body.bili-search-page .search-page > div, body.bili-search-page .video.i_wrapper, body.bili-search-page .video.i_wrapper.search-all-list { background: #f1f2f3 !important; background-color: #f1f2f3 !important; background-image: none !important; } /* ===== 用户搜索页(/upuser):隐藏原生排序与筛选按钮区 ===== 探针实测:.search-page-upuser > div:nth-of-type(1) > .search-conditions.mt_lg(360x222,含 mt 20) ├─ .conditions-order(默认排序/粉丝数由高到低/由低到高/ │ Lv等级由高到低/由低到高 + 更多筛选) └─ .more-conditions(全部用户/UP主用户/普通用户/认证用户) 整块吃掉 242px,隐藏后第一个用户卡片直接顶上来。 只作用于 .search-page-upuser,不影响综合/直播/专栏页。 ---- */ body.bili-search-page .search-page-upuser .search-conditions, body.bili-search-page .search-page-upuser .search-conditions.mt_lg, body.bili-search-page .search-page-upuser .conditions-order, body.bili-search-page .search-page-upuser .more-conditions, body.bili-search-page .search-page-upuser .condition-row, body.bili-search-page .search-page-upuser .i_button_more { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* 外层那个包着 conditions 的空壳 div 也一起收掉,避免残留 20px mt */ body.bili-search-page .search-page-upuser > div:nth-of-type(1) { padding: 0 !important; margin: 0 !important; min-height: 0 !important; height: auto !important; } /* ===== 用户搜索页卡片:左右边距统一 8px(v131.71)===== 实测(360 屏,.search-page-upuser): .col_6 left=8 w=344 → 距屏幕左 8、距屏幕右 8 a.mr_md(头像) left=8 w=86 → 头像左边距 8 ✓ .user-content left=109 w=243 → 盒子右边 352,距屏幕右 8 但 .pr_md 自带 padding-right 15px, 里面的 h2 / user-actions 实际只到 337 → 距屏幕右 23px 所以「头像距左 8px」与「文字距右 23px」并不相等。 这里把 .pr_md 的 padding-right 归零,文字右边界回到 352(距右 8px), 与头像左边距 8px 完全一致。 ---- */ html body.bili-search-page #app .search-page-upuser .b-user-info-card, body.bili-search-page .search-page-upuser .b-user-info-card { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; } html body.bili-search-page #app .search-page-upuser .b-user-info-card .user-content, html body.bili-search-page #app .search-page-upuser .b-user-info-card .user-content.pr_md, body.bili-search-page .search-page-upuser .b-user-info-card .user-content, body.bili-search-page .search-page-upuser .b-user-info-card .user-content.pr_md { padding-right: 0 !important; min-width: 0 !important; box-sizing: border-box !important; } /* ===== 用户搜索页分页条:上间距 = 下间距 = 8px(v131.71)===== 实测滚动到底时(#app top=-2954,页高 3754 = 屏高 800 + 滚动量): 最后一个用户卡片 bottom=710 + 卡片 margin-bottom 16 (.mb_x60 被规则3改成 16) + .media-list padding-bottom 8 + 分页条 margin-top 8 ([class*="mt_x50"]) = 分页条 top 742 → 距最后一张卡片 32px 分页条 bottom=792,+ margin-bottom 8 = 内容底 800 → 距屏幕底 8px 上下不对称(32 vs 8),这里把上方压到 8: 最后一个卡片 mb → 0、列表容器 pb → 0、分页条 mt 保持 8 = 710 + 0 + 0 + 8 = 718,间距 8px,与距屏幕底的 8px 相等。 只去掉「最后一个」卡片的下边距,卡片之间的 16px 间距不动。 ---- */ html body.bili-search-page #app .search-page-upuser .media-list > [class*="col_"]:last-child, html body.bili-search-page #app .search-page-upuser .media-list > [class*="col_md_"]:last-child, body.bili-search-page .search-page-upuser .media-list > [class*="col_"]:last-child, body.bili-search-page .search-page-upuser .media-list > [class*="col_md_"]:last-child { margin-bottom: 0 !important; } html body.bili-search-page #app .search-page-upuser .media-list, body.bili-search-page .search-page-upuser .media-list { padding-bottom: 0 !important; } html body.bili-search-page #app .search-page-upuser .mt_x50.mb_lg, html body.bili-search-page #app .search-page-upuser .flex_center.mt_x50, body.bili-search-page .search-page-upuser .mt_x50.mb_lg, body.bili-search-page .search-page-upuser .flex_center.mt_x50 { margin-top: 8px !important; margin-bottom: 8px !important; } /* ============================================================ ===== 专栏搜索页(/article)v131.72 ===== 三件事,与用户页(/upuser)同一套口径: 1) 隐藏原生排序区 2) 卡片「封面距左」「文字距右」都统一 8px 3) 分页条上间距 = 下间距 = 8px ============================================================ */ /* ---- 1. 隐藏原生排序区 ---- 探针实测(.search-page-article,360 屏): > div:nth-of-type(1) [360x126] └─ .search-conditions.mt_lg [360x96] mt=20 └─ .conditions-order.flex_between.mb_sm mb=10 ├─ .condition-row [260x96] 综合排序/最新发布/最多点击/最多喜欢/最多评论 └─ .i_button_more [100x34] 更多筛选 126 = 20(mt) + 96 + 10(mb 穿透),整块隐藏后归零, 内容区 div:nth-of-type(2) 从 top=178 直接顶到 52。 ⚠ 专栏页没有 .more-conditions(那是用户页才有的类型筛选), 所以这里只处理 conditions-order 这一支。 ---- */ html body.bili-search-page #app .search-page-article .search-conditions, html body.bili-search-page #app .search-page-article .search-conditions.mt_lg, html body.bili-search-page #app .search-page-article .conditions-order, html body.bili-search-page #app .search-page-article .condition-row, html body.bili-search-page #app .search-page-article .i_button_more, body.bili-search-page .search-page-article .search-conditions, body.bili-search-page .search-page-article .search-conditions.mt_lg, body.bili-search-page .search-page-article .conditions-order, body.bili-search-page .search-page-article .condition-row, body.bili-search-page .search-page-article .i_button_more { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* 外壳 div 一起收掉,避免 20px mt / 10px mb 穿透留下残高 */ html body.bili-search-page #app .search-page-article > div:nth-of-type(1), body.bili-search-page .search-page-article > div:nth-of-type(1) { padding: 0 !important; margin: 0 !important; min-height: 0 !important; height: auto !important; } /* ---- 2. 卡片左右边距统一 8px ---- 实测:.col_6 left=8 w=344(距左 8、距右 8) .article-banner(封面) left=8 w=154 → 距左 8 ✓ .article-content.pr_md left=174 w=178 → 盒子右边 352(距右 8) 但 .pr_md 自带 padding-right 15px(178-163=15), 里面的 h2 / .atc-desc 实际只到 337 → 距右 23px ✗ 把 padding-right 归零,文字右边界回到 352,与封面左边距 8px 对称。 ---- */ html body.bili-search-page #app .search-page-article .b-article-card, body.bili-search-page .search-page-article .b-article-card { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; } html body.bili-search-page #app .search-page-article .b-article-card .article-content, html body.bili-search-page #app .search-page-article .b-article-card .article-content.pr_md, body.bili-search-page .search-page-article .b-article-card .article-content, body.bili-search-page .search-page-article .b-article-card .article-content.pr_md { /* v131.79:右侧留 4px,别让文字贴着白卡边缘。 卡片右边界 352(屏幕 360 - 8),文字内容右边界因此是 348, 距屏幕 12px。原生 .pr_md 是 15px,之前为了「左右统一 8px」 被归零,现在白卡有了,贴边确实难看,补 4px 收一点。 注意:内容区因此窄了 4px(178 → 174), h2 / atc-desc / atc-author 都会更早触发省略, atc-author 那行靠 2d 的 flex 收缩自动吸收,不会溢出。 */ padding-right: 4px !important; /* 综合页视频卡的文字区带上下 8px padding,这里同样补上: .article-content 是 align-items:stretch 撑满卡片高度的, 配合 border-box 加上下 padding 后盒子高度不变,卡片不会被撑高。 */ padding-top: 8px !important; padding-bottom: 8px !important; min-width: 0 !important; box-sizing: border-box !important; } /* ---- 2a2. 卡片之间的间距 16 → 8(v131.76)---- 上面的通用规则把 .col_6 / .col_md_4 的 margin-bottom 钉成 16px (那条是给番剧垂搜页定的:卡片间距 = 距屏幕左右边距 = 16)。 专栏页实测间距 16(卡片 bottom 191 → 下一张 top 207), 与用户页 8、综合页 8 不一致,这里在专栏页作用域内压回 8px。 特异性 (1,3,3) > 通用规则的 (1,2,3),能覆盖。 ⚠ 最后一个卡片仍是 0(下面第 3 组的 :last-child 规则), 所以「分页条距最后一张卡片 = 8」不受影响。 ---- */ html body.bili-search-page #app .search-page-article .media-list [class*="col_6"], html body.bili-search-page #app .search-page-article .media-list [class*="col_md_4"], html body.bili-search-page #app .search-page-article .media-list .mb_x40, html body.bili-search-page #app .search-page-article .media-list > div, body.bili-search-page .search-page-article .media-list [class*="col_6"], body.bili-search-page .search-page-article .media-list [class*="col_md_4"], body.bili-search-page .search-page-article .media-list .mb_x40, body.bili-search-page .search-page-article .media-list > div { margin-bottom: 8px !important; margin-top: 0 !important; } /* ---- 2b. 专栏卡片白卡皮肤:照抄综合页 .bili-video-card ---- 综合页视频卡(探针 .search-page-all,360 屏): .bili-video-card [168x172] background: #ffffff border: none border-radius: 8px box-shadow: 0 1px 3px rgba(0,0,0,0.08) overflow: hidden 专栏卡(探针 .search-page-article): .b-article-card [344x116] flex 横排 ├─ .article-banner [154x116] 封面(贴左、贴上下) └─ .article-content [178x116] 标题 + 摘要 ⚠ 只加背景,不加内边距:卡片本身已占满 344(屏幕左右各 8px), 再加 padding 会把内容推成 16px,破坏上一轮「左右统一 8px」的约定。 封面贴边被圆角裁切,与综合页封面被裁的效果一致。 ---- */ html body.bili-search-page #app .search-page-article .b-article-card, html body.bili-search-page #app .search-page-article .b-article-card.b-article-card, body.bili-search-page .search-page-article .b-article-card, body.bili-search-page .search-page-article .b-article-card.b-article-card { background: #ffffff !important; border: none !important; border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0,0,0,0.08) !important; overflow: hidden !important; box-sizing: border-box !important; position: relative !important; } /* 封面:四角都给 8px 圆角(v131.76 改为全圆角)。 上一版只给了左两角(8px 0 0 8px),右边缘是直角, 与右侧文字区的留白风格不搭,现在四角统一。 封面内的 a / picture / img 是绝对定位铺满的(见 2c), 靠 .article-banner 的 overflow: hidden 裁出圆角。 ---- */ html body.bili-search-page #app .search-page-article .article-banner, body.bili-search-page .search-page-article .article-banner { border-radius: 8px !important; overflow: hidden !important; box-sizing: border-box !important; } /* ---- 2c. 卡片高度 = 封面图片高度(v131.75 重写)---- ❌ v131.74 为什么没生效: 给 .article-banner 写了 height: 100%!important。 flex 项只有在交叉轴尺寸是 auto 时才会 stretch, 而 100% 不是 auto → stretch 被禁用;百分比又算不出来 (卡片高度 auto,不确定)→ 回落到 B站原生的 116px。 同时文字区被我加了 8+8 padding,内容 115 + 16 = 131, 卡片 = max(封面 116, 文字 131) = 131。 结果 卡片 131 ≠ 封面 116,封面底下露出 15px 白边。 探针实测(20 张卡全部如此):卡片 131 / 封面 116。 ✅ 正确做法:反过来让「封面跟着卡片长」,两边恒等。 a) 封面 align-self: stretch + height: auto → 封面高度 = 卡片交叉尺寸,永远等于卡片高度 b) 封面内 a / picture / img 全部绝对定位铺满(top/right/bottom/left:0) → 图片高度 = 封面高度,且内容脱离文档流后 封面自身不再贡献高度(假设交叉尺寸 = 0), 卡片高度就只由「文字区」和 min-height 116 决定 c) img 用 object-fit: cover,拉高不变形 d) 文字区 align-self: stretch,撑满即可,不额外顶高 结果:卡片 = max(116, 文字) → 封面 stretch 到同值,两者恒等。 文字多 → 卡片 131、封面 131;文字少 → 都是 116。 ---- */ html body.bili-search-page #app .search-page-article .b-article-card, body.bili-search-page .search-page-article .b-article-card { align-items: stretch !important; min-height: 116px !important; } html body.bili-search-page #app .search-page-article .article-banner, body.bili-search-page .search-page-article .article-banner { /* 关键:height 必须是 auto,stretch 才会生效(100% 会禁用 stretch) */ height: auto !important; align-self: stretch !important; min-height: 116px !important; max-height: none !important; flex-shrink: 0 !important; position: relative !important; } /* 图片整条链绝对定位铺满封面:图片高度 = 封面高度 = 卡片高度 */ html body.bili-search-page #app .search-page-article .article-banner > a, html body.bili-search-page #app .search-page-article .article-banner a, html body.bili-search-page #app .search-page-article .article-banner picture, body.bili-search-page .search-page-article .article-banner > a, body.bili-search-page .search-page-article .article-banner a, body.bili-search-page .search-page-article .article-banner picture { position: absolute !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important; display: block !important; width: 100% !important; height: 100% !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; box-sizing: border-box !important; } html body.bili-search-page #app .search-page-article .article-banner img, body.bili-search-page .search-page-article .article-banner img { position: absolute !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important; display: block !important; width: 100% !important; height: 100% !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; object-fit: cover !important; box-sizing: border-box !important; } /* 文字区撑满卡片高度即可,不要额外把卡片顶高 */ html body.bili-search-page #app .search-page-article .b-article-card .article-content, body.bili-search-page .search-page-article .b-article-card .article-content { align-self: stretch !important; height: auto !important; max-height: none !important; overflow: hidden !important; } /* ---- 2d. UP主名 + 日期行(.atc-author)不超出卡片(v131.78)---- ❌ v131.76 / v131.77 改错了元素: 改的是 p.atc-info(「日常 · 0点赞 · 评论」那行), 而 UP主名 · 日期 其实在 div.atc-author,两者是不同的兄弟节点。 所以前两版对这行的溢出毫无作用(.atc-info 本来就不溢出)。 真实结构(含隐藏节点的探针,20 张卡一致): div.atc-author.text_ellipsis.flex_start.d_flex.fs_5 [178x16] │ B站自己就是 flex 容器 + overflow:hidden ├─ a.flex_start.flex_inline.text3 [132x18] UP主名 │ ├─ svg.author-icon.mr_2 [0x18] (宽度 0,无可见图标) │ └─ span.lh_xs [130x13] 名字文本 └─ span.text3.ml_4 [45x16] "· 09-26" 日期 溢出实测(20 张卡中 5 张): .atc-author left=174 w=178 → 右边界 352(= 卡片右边界) 名字 a[132] + ml_4(4) + 日期[45] = 181 > 178 → 日期 right=355,超出卡片 3px,被 overflow:hidden 硬裁掉 ⚠ 为什么 B站自带的 .text_ellipsis 没生效: text-overflow 只对「块容器」有效,而 .atc-author 是 flex 容器, 省略号不会渲染,只能硬裁。所以必须在「名字」这一层做省略号。 ✅ 做法:让名字可收缩、日期固定不收缩 a) .atc-author 锁 nowrap + overflow hidden(兜底,任何情况不溢出) b) 名字 :flex 0 1 auto + min-width 0 → 空间不够时收缩 c) 名字内 span.lh_xs:作为 flex 项会被块级化, 此时给它 ellipsis 就能渲染「…」,名字变「做一个莫得感情的彩…」 d) 日期 span.text3.ml_4:flex 0 0 auto,永不收缩,日期完整保留 名字短时(如卡2/3/10)本来就不溢出,不会有任何变化。 ---- */ html body.bili-search-page #app .search-page-article .atc-author, html body.bili-search-page #app .search-page-article .b-article-card .atc-author, html body.bili-search-page #app .search-page-article .article-content .atc-author, body.bili-search-page .search-page-article .atc-author, body.bili-search-page .search-page-article .b-article-card .atc-author, body.bili-search-page .search-page-article .article-content .atc-author { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: center !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; overflow: hidden !important; box-sizing: border-box !important; margin-left: 0 !important; margin-right: 0 !important; } /* 名字链接:唯一可收缩的项 */ html body.bili-search-page #app .search-page-article .atc-author > a, body.bili-search-page .search-page-article .atc-author > a { display: flex !important; flex: 0 1 auto !important; min-width: 0 !important; max-width: 100% !important; overflow: hidden !important; box-sizing: border-box !important; } /* 名字文本:flex 项 → 块级化 → ellipsis 生效 */ html body.bili-search-page #app .search-page-article .atc-author > a > span, html body.bili-search-page #app .search-page-article .atc-author > a .lh_xs, body.bili-search-page .search-page-article .atc-author > a > span, body.bili-search-page .search-page-article .atc-author > a .lh_xs { display: block !important; flex: 0 1 auto !important; min-width: 0 !important; max-width: 100% !important; overflow: hidden !important; white-space: nowrap !important; text-overflow: ellipsis !important; box-sizing: border-box !important; } /* 名字里的图标:宽度已是 0,防止它占位影响收缩 */ html body.bili-search-page #app .search-page-article .atc-author > a > svg, body.bili-search-page .search-page-article .atc-author > a > svg { flex: 0 0 auto !important; max-width: 18px !important; } /* 日期:永不收缩,完整显示「· 09-26」 */ html body.bili-search-page #app .search-page-article .atc-author > span, html body.bili-search-page #app .search-page-article .atc-author .ml_4, body.bili-search-page .search-page-article .atc-author > span, body.bili-search-page .search-page-article .atc-author .ml_4 { display: block !important; flex: 0 0 auto !important; min-width: 0 !important; max-width: none !important; overflow: visible !important; white-space: nowrap !important; text-overflow: clip !important; box-sizing: border-box !important; } /* ---- 3. 分页条:上间距 = 下间距 = 8px ---- 探针未滚动,用文档坐标推算(#app top=52,文档底 2900,屏高 800): 最后一张专栏卡片 bottom = 2810 + 卡片 mb 16 (.mb_x40 被规则3改成 16) + .media-list pb 8 + 分页条 mt 8 ([class*="mt_x50"]) = 分页条 top 2842 → 距最后一张卡片 32px 分页条 bottom 2892 + mb 8 = 2900(文档底) 滚到底时距屏幕底 = 8px 上下不对称(32 vs 8)。压上方: 最后一个卡片 mb→0、media-list pb→0、分页条 mt 保持 8 = 2810 + 8 = 2818,间距 8px,与距屏幕底 8px 相等。 只改「最后一个」卡片,卡片之间的 16px 不动。 ---- */ html body.bili-search-page #app .search-page-article .media-list > [class*="col_"]:last-child, html body.bili-search-page #app .search-page-article .media-list > [class*="col_md_"]:last-child, body.bili-search-page .search-page-article .media-list > [class*="col_"]:last-child, body.bili-search-page .search-page-article .media-list > [class*="col_md_"]:last-child { margin-bottom: 0 !important; } html body.bili-search-page #app .search-page-article .media-list, body.bili-search-page .search-page-article .media-list { padding-bottom: 0 !important; } html body.bili-search-page #app .search-page-article .mt_x50.mb_lg, html body.bili-search-page #app .search-page-article .flex_center.mt_x50, body.bili-search-page .search-page-article .mt_x50.mb_lg, body.bili-search-page .search-page-article .flex_center.mt_x50 { margin-top: 8px !important; margin-bottom: 8px !important; } /* ============================================================ ===== 直播搜索页(/live)v131.80 ===== 探针实测(360 屏,.search-page-live,文档高 1710): div.mt_xxl.live-condition.mt_lg [360x32] top=52 全部/主播/直播间 div:nth-of-type(2) └─ div.mt_xxl.live-user.mt_xl [360x606] top=114 mt=30 ├─ h1.b_text.flex_between [360x34] top=114 主播 共找到… 查看更多 └─ div.live-user-cards.row.mt_lg left=-8 w=360 top=168 mt=20 └─ div.col_6.col_md_4 left=-8 w=360 h=138 └─ div.b-user-live-card [344x86] left=0 mb=52 └─ div.live-room.mt_sm [360x966] top=730 ├─ h1.room-header [360x60] top=730 直播间 排序… └─ div.live-room-cards.row.mt_lg left=-8 w=360 top=810 └─ div.video-list-item.col_3… [90x118] 4 列 └─ div.bili-live-card [74x86] div.mt_x50.mb_lg.flex_center [360x50] top=1704 分页条 ⚠ 关键:两个 .row 容器实测 left=-8 w=360(右边界 352), 整体比父容器左移了 8px,导致卡片距屏幕左 0、距右 16,不对称。 所以先把 row 的负 margin 归零,再由容器 padding 提供左右 8px。 ============================================================ */ /* ---- 1. 隐藏「全部 / 主播 / 直播间」切换行([360x32])---- */ html body.bili-search-page #app .search-page-live .live-condition, html body.bili-search-page #app .search-page-live .live-condition.mt_xxl, html body.bili-search-page #app .search-page-live .live-condition.mt_lg, body.bili-search-page .search-page-live .live-condition, body.bili-search-page .search-page-live .live-condition.mt_xxl, body.bili-search-page .search-page-live .live-condition.mt_lg { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* ---- 2. 整个主播区 .live-user 隐藏(v131.81 改)---- v131.80 只隐藏了里面的 h1 标题,4 张用户卡还在; 用户确认主播卡也不需要 → 整块(含 h1「主播 · 共找到1000+个结果 · 查看更多」)隐藏。 (下面第 3~5 组给主播卡加的白卡 / 间距规则因此全部失效,留着无副作用。) ---- */ html body.bili-search-page #app .search-page-live .live-user, html body.bili-search-page #app .search-page-live .live-user.mt_xxl, html body.bili-search-page #app .search-page-live .live-user.mt_xl, body.bili-search-page .search-page-live .live-user, body.bili-search-page .search-page-live .live-user.mt_xxl, body.bili-search-page .search-page-live .live-user.mt_xl { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* ---- 3. 主播区:容器归位(row 负 margin 清零)+ 顶部/左右 8px ---- */ html body.bili-search-page #app .search-page-live .live-user, html body.bili-search-page #app .search-page-live .live-user.mt_xxl, body.bili-search-page .search-page-live .live-user, body.bili-search-page .search-page-live .live-user.mt_xxl { margin-top: 0 !important; margin-bottom: 0 !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; } html body.bili-search-page #app .search-page-live .live-user-cards, html body.bili-search-page #app .search-page-live .live-user-cards.row, html body.bili-search-page #app .search-page-live .live-user-cards.row.mt_lg, body.bili-search-page .search-page-live .live-user-cards, body.bili-search-page .search-page-live .live-user-cards.row, body.bili-search-page .search-page-live .live-user-cards.row.mt_lg { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; /* 负 margin 归零后:容器 0~360,padding 8 → 内容 8~352, 卡片距屏幕左右各 8px(改前是距左 0 / 距右 16) */ margin: 8px 0 0 0 !important; padding: 0 8px !important; box-sizing: border-box !important; float: none !important; } /* ---- 4. 主播卡:列间距 52 → 8(最后一个 0)---- */ html body.bili-search-page #app .search-page-live .live-user-cards > [class*="col_"], html body.bili-search-page #app .search-page-live .live-user-cards > [class*="col_md_"], body.bili-search-page .search-page-live .live-user-cards > [class*="col_"], body.bili-search-page .search-page-live .live-user-cards > [class*="col_md_"] { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 !important; margin: 0 0 8px 0 !important; box-sizing: border-box !important; float: none !important; } html body.bili-search-page #app .search-page-live .live-user-cards > [class*="col_"]:last-child, body.bili-search-page .search-page-live .live-user-cards > [class*="col_"]:last-child { margin-bottom: 0 !important; } /* ---- 5. 主播卡白卡皮肤(与综合页视频卡同参数)---- 实测改前 [344x86] 无背景,白卡是后面加的。 ⚠ 只加外框,不动内容区(头像 86 + .live-content)内部排布。 ---- */ html body.bili-search-page #app .search-page-live .b-user-live-card, html body.bili-search-page #app .search-page-live .b-user-live-card.b-user-live-card, body.bili-search-page .search-page-live .b-user-live-card, body.bili-search-page .search-page-live .b-user-live-card.b-user-live-card { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; height: auto !important; display: flex !important; background: #ffffff !important; border: none !important; border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0,0,0,0.08) !important; overflow: hidden !important; box-sizing: border-box !important; float: none !important; } /* ---- 6. 直播间区:4 列 74px → 2 列 168px(照抄综合页 grid 参数)---- 综合页 .video-list:grid repeat(2, minmax(0,1fr)) + gap 8 + padding 8 → 360 - 16(padding) = 344,(344 - 8)/2 = 168 ✓ 与综合页同宽同间距 ---- */ html body.bili-search-page #app .search-page-live .live-room, body.bili-search-page .search-page-live .live-room { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin-top: 8px !important; margin-bottom: 0 !important; } html body.bili-search-page #app .search-page-live .live-room-cards, html body.bili-search-page #app .search-page-live .live-room-cards.row, html body.bili-search-page #app .search-page-live .live-room-cards.row.mt_lg, body.bili-search-page .search-page-live .live-room-cards, body.bili-search-page .search-page-live .live-room-cards.row, body.bili-search-page .search-page-live .live-room-cards.row.mt_lg { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; grid-gap: 8px !important; gap: 8px !important; row-gap: 8px !important; column-gap: 8px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; /* v131.83:上 padding 也归零,否则与 h1 的 mb 8 叠成 16px。 下 padding 同样是 0:让「最后一行卡片 → 分页条」= 分页条的 mt 8px, 与分页条距屏幕底的 8px 相等。 */ padding: 0 8px 0 8px !important; margin: 0 !important; box-sizing: border-box !important; list-style: none !important; align-items: start !important; align-content: start !important; grid-auto-rows: auto !important; float: none !important; } html body.bili-search-page #app .search-page-live .live-room-cards > *, html body.bili-search-page #app .search-page-live .live-room-cards > [class*="col_"], html body.bili-search-page #app .search-page-live .live-room-cards > [class*="video-list-item"], body.bili-search-page .search-page-live .live-room-cards > *, body.bili-search-page .search-page-live .live-room-cards > [class*="col_"], body.bili-search-page .search-page-live .live-room-cards > [class*="video-list-item"] { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; float: none !important; position: static !important; flex: 1 1 auto !important; } /* .bili-live-card 的白卡皮肤在「通用两列视频卡片」段已定义 (body.bili-search-page .bili-live-card),这里只把高度对齐综合页的 172px */ html body.bili-search-page #app .search-page-live .bili-live-card, body.bili-search-page .search-page-live .bili-live-card { min-height: 172px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; } /* ---- 6b. 直播间标题行:只留「共找到 N 个结果」(v131.81)---- 结构(探针 .live-room > h1.room-header [360x60]): h1.room-header.b_text.flex_between └─ span.flex_start [360x60] ├─ 裸文本节点「直播间」 ← CSS 选不中文本节点 ├─ div.room-order [210x28] 综合排序 / 最新开播 └─ span.ml_md.fs_4.text3 [75x35] 共找到214个结果 需求:隐藏「直播间 + 综合排序 + 最新开播」,保留「共找到 N 个结果」。 · div.room-order 直接 display:none · 「直播间」是裸文本节点 → 给父级 span.flex_start 设 font-size:0 (字号为 0 时文本不占宽也不显示),再把 span.ml_md 的字号还原 (B站 fs_4 = 14px),这样无论官方有没有包一层元素都能干掉 那两个排序按钮改由搜索框左侧的「排序」接管,见 JS 部分。 ---- */ html body.bili-search-page #app .search-page-live .live-room > h1, html body.bili-search-page #app .search-page-live .live-room > h1.room-header, body.bili-search-page .search-page-live .live-room > h1, body.bili-search-page .search-page-live .live-room > h1.room-header { display: flex !important; /* v131.82:改为居中。原为 flex-end(右对齐,实测 left=244 right=352)。 内容区 8~352 = 344,结果数 108 宽 → 居中后 left = 8+(344-108)/2 = 126 */ justify-content: center !important; align-items: center !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; padding: 0 8px !important; /* 下间距:由这一条 margin-bottom 提供唯一的 8px。 ⚠ v131.83 修复:实测「共找到9个结果」到第一张卡片是 16px—— h1 的 mb 8 + .live-room-cards 的 padding-top 8 叠加成了 16 (探针:h1 bottom=80、cards 盒子 top=88、卡 top=96)。 所以下面把 cards 的 padding-top 归零,间距只由这里给。 */ margin: 0 0 8px 0 !important; box-sizing: border-box !important; } /* font-size:0 干掉裸文本「直播间」 */ html body.bili-search-page #app .search-page-live .live-room h1 > span.flex_start, body.bili-search-page .search-page-live .live-room h1 > span.flex_start { font-size: 0 !important; line-height: 0 !important; width: auto !important; min-width: 0 !important; margin: 0 !important; } html body.bili-search-page #app .search-page-live .live-room .room-order, body.bili-search-page .search-page-live .live-room .room-order { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-width: 0 !important; max-width: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* 结果数字号还原(fs_4 = 14px),右对齐、距屏幕右 8px */ html body.bili-search-page #app .search-page-live .live-room .ml_md, body.bili-search-page .search-page-live .live-room .ml_md { font-size: 14px !important; line-height: 20px !important; display: inline-block !important; max-width: 100% !important; margin-left: 0 !important; white-space: nowrap !important; } /* ---- 7. 分页条:上间距 = 下间距 = 8px ---- 已有通用规则 [class*="mt_x50"] { margin: 8px 0 }, 这里再把「最后一个卡片 mb」和「容器 pb」压到 0 即可两端相等。 ---- */ html body.bili-search-page #app .search-page-live .live-room-cards > *:last-child, body.bili-search-page .search-page-live .live-room-cards > *:last-child { margin-bottom: 0 !important; } /* ===== 关键词屏蔽提示条(+必含 / -排除)v131.62 ===== 固定在自建搜索栏(52px)正下方,告诉用户当前哪些词被排除了、 一共过滤掉多少条,并提供"清除"回到不带屏蔽词的搜索结果。 它占 30px,所以下面配套一条 body.has-filter-bar 把正文再下推 30px, 免得盖住第一条结果。 ---- */ #my-bili-filterbar { position: fixed !important; top: 52px !important; left: 0 !important; right: 0 !important; z-index: 2147483645 !important; display: flex !important; align-items: center !important; gap: 6px !important; height: 30px !important; padding: 0 10px !important; background: #fff7f7 !important; border-bottom: 1px solid #ffe3e3 !important; color: #61666d !important; font-size: 12px !important; line-height: 30px !important; box-sizing: border-box !important; overflow-x: auto !important; overflow-y: hidden !important; white-space: nowrap !important; -webkit-overflow-scrolling: touch !important; } #my-bili-filterbar .fb-label { flex-shrink: 0 !important; color: #9499a0 !important; } #my-bili-filterbar .fb-word { flex-shrink: 0 !important; height: 20px !important; padding: 0 8px !important; line-height: 20px !important; background: #ffe0e0 !important; color: #e34d59 !important; border-radius: 10px !important; } #my-bili-filterbar .fb-count { flex-shrink: 0 !important; margin-left: auto !important; color: #9499a0 !important; } #my-bili-filterbar .fb-clear { flex-shrink: 0 !important; padding: 0 4px 0 8px !important; color: #00aeec !important; cursor: pointer !important; user-select: none !important; -webkit-tap-highlight-color: transparent !important; } /* 提示条占位:52(搜索栏) + 30(提示条) = 82 */ body.has-filter-bar { padding-top: 82px !important; } body.bili-search-page * { max-width: 100% !important; box-sizing: border-box !important; } /* v132.55:排除灯箱 —— .pswp 挂在 body 下,会被这条通用 img 规则砸中, 导致全屏看图器大图变成 0x0(详见样式表末尾 v132.55 说明) */ body.bili-search-page img:not([class*="pswp"]), body.bili-search-page video:not([class*="pswp"]), body.bili-search-page picture:not([class*="pswp"]) { max-width: 100% !important; height: auto !important; } html body.bili-search-page #app, html body.bili-search-page #app > div, html body.bili-search-page #search-app, html body.bili-search-page #app .search-layout, html body.bili-search-page #app .search-layout.clearfix, html body.bili-search-page #app .search-content, html body.bili-search-page #app .search-content--gray, html body.bili-search-page #app .search-page-wrapper, html body.bili-search-page #app .search-page, html body.bili-search-page #app .search-page.search-page-all, html body.bili-search-page #app .search-page > div, html body.bili-search-page #app .video.i_wrapper, html body.bili-search-page #app .video.i_wrapper.search-all-list, /* v132.12:骨架屏阶段的列表容器叫 .i_wrapper.mt_xxl —— 既没有 .video 也没有 .search-all-list,上面两条都选不中它。 B站 .i_wrapper 是 PC 定宽容器,360 视口下算出 248 内容宽 + 左右各 56 居中留白,正是"骨架屏很窄"的来源。 所以这里按 **任意 .i_wrapper** 兜底,不再依赖具体类名。 */ html body.bili-search-page #app .i_wrapper, html body.bili-search-page #app .i_wrapper.mt_xxl, body.bili-search-page .i_wrapper, body.bili-search-page .i_wrapper.mt_xxl, html body.bili-search-page #app .search-all-list, body.bili-search-page .search-container, body.bili-search-page .search-result, body.bili-search-page .search-result-wrapper, body.bili-search-page .search-page-container, body.bili-search-page .search-page-content, body.bili-search-page .video-list-container { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; padding-top: 0 !important; padding-bottom: 0 !important; margin-left: 0 !important; margin-right: 0 !important; margin-top: 0 !important; margin-bottom: 0 !important; box-sizing: border-box !important; position: static !important; left: 0 !important; right: 0 !important; transform: none !important; overflow-x: hidden !important; float: none !important; } body.bili-search-page .bili-header, body.bili-search-page .international-header, body.bili-search-page .mini-header, body.bili-search-page #biliMainHeader, body.bili-search-page #bili-header-container, body.bili-search-page .bili-header--mini, body.bili-search-page .bili-header--fixed, body.bili-search-page .bili-header__bar, body.bili-search-page .bili-header__menu, body.bili-search-page .nav-menu, body.bili-search-page .channel-menu, body.bili-search-page .nav-channel, body.bili-search-page .header-channel, body.bili-search-page .nav-bar, body.bili-search-page .footer, body.bili-search-page .bili-footer, body.bili-search-page .international-footer, body.bili-search-page .mini-footer, body.bili-search-page .go-top, body.bili-search-page .bili-go-top, body.bili-search-page [class*="back-top"], body.bili-search-page [class*="to-top"], body.bili-search-page [class*="right-side"], body.bili-search-page [class*="right-bar"], body.bili-search-page [class*="right-tool"], body.bili-search-page [class*="side-tool"], body.bili-search-page [class*="toolbar"], body.bili-search-page [class*="feedback"], body.bili-search-page [class*="customer-service"], body.bili-search-page [class*="service-btn"], body.bili-search-page .ad-report, body.bili-search-page .ad-floor, body.bili-search-page .fixed-sidebar, body.bili-search-page .bili-fixed-sidebar { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-search-page .search-condition, body.bili-search-page .search-condition-wrapper, body.bili-search-page .filter-wrap, body.bili-search-page .search-filter, body.bili-search-page .left-search-filter, body.bili-search-page .search-page-left, body.bili-search-page aside.left, body.bili-search-page .search-filters, body.bili-search-page .search-condition-left, body.bili-search-page .search-header, body.bili-search-page .search-navbar, body.bili-search-page .nav-search, body.bili-search-page .search-input, body.bili-search-page .search-input-container, body.bili-search-page .bili-header__search, body.bili-search-page [class*="search-header"], body.bili-search-page [class*="search-nav"], body.bili-search-page .search-hot, body.bili-search-page .hot-search, body.bili-search-page .search-trending, body.bili-search-page .trending, body.bili-search-page [class*="hot-word"], body.bili-search-page [class*="hotsearch"] { display: none !important; visibility: hidden !important; } body.bili-search-page .activity-game-list, body.bili-search-page .user-list, body.bili-search-page .activity-game-list-wrap { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } html body.bili-search-page #app .video-list, html body.bili-search-page #app .video-list.row, html body.bili-search-page #app .search-video-list, html body.bili-search-page #app .card-list, html body.bili-search-page #app .search-result-list, html body.bili-search-page #app ul.video-list, html body.bili-search-page #app .search-page-video-list, html body.bili-search-page #app .search-all-list > .video-list, html body.bili-search-page #app .search-all-list > .video-list.row, html body.bili-search-page #app .video.i_wrapper.search-all-list > .video-list, html body.bili-search-page #app .video.i_wrapper.search-all-list > .video-list.row { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; grid-gap: 8px !important; gap: 8px !important; row-gap: 8px !important; column-gap: 8px !important; width: 100% !important; max-width: 100% !important; min-width: 100% !important; padding: 8px !important; margin: 0 !important; background-color: #f1f2f3 !important; box-sizing: border-box !important; list-style: none !important; align-items: start !important; align-content: start !important; grid-auto-rows: auto !important; float: none !important; position: static !important; } /* ===== 搜索结果番剧区:综合页 / 番剧垂搜页 统一边距 ===== 涉及两处,类名不同但结构同构,这里共用同一组规则: · 综合页(/all) :.bangumi-pgc-list.i_wrapper.search-all-list · 番剧垂搜页(/bangumi):.media-list.i_wrapper(卡内为 .col_6.mb_x40) 实测(360px 屏,综合页):容器 360 -> row 344 -> col/card 328, 即卡片左右各留 16px(8 容器 + 8 row),左右相等即居中; 垂搜页原本 row 仅 248、card 232,明显更窄,需拉到同一条边线。 同时解除 B站对该区域的固定高度(实测容器 685px、row 已占 684px), 并把卡片下边距 mb_x40(40px) 压到 8px,使「查看全部 N 部相关番剧作品」 不再被裁掉一半、无需再下滑。 卡片内部(左封面 148 + 右信息区)保持 B站原生排布,不改动。 */ /* 1) 容器:左右 8px + 上下 8px,解除官方高度限制 */ html body.bili-search-page #app .bangumi-pgc-list, html body.bili-search-page #app .bangumi-pgc-list.i_wrapper, html body.bili-search-page #app .media-list, html body.bili-search-page #app .media-list.i_wrapper, body.bili-search-page .bangumi-pgc-list, body.bili-search-page .media-list, body.bili-search-page .media-list.i_wrapper { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 8px !important; padding-right: 8px !important; padding-top: 8px !important; padding-bottom: 8px !important; margin-left: auto !important; margin-right: auto !important; margin-top: 0 !important; margin-bottom: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; box-sizing: border-box !important; float: none !important; } /* 2) 行容器:左右 8px(与容器叠加 = 16px),高度自适应、不裁剪 */ html body.bili-search-page #app .bangumi-pgc-list .row.media-item, html body.bili-search-page #app .media-list .row, html body.bili-search-page #app .media-list.i_wrapper > .row, body.bili-search-page .bangumi-pgc-list .row.media-item, body.bili-search-page .media-list .row, body.bili-search-page .media-list.i_wrapper > .row { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 8px !important; padding-right: 8px !important; padding-top: 0 !important; padding-bottom: 0 !important; margin-left: auto !important; margin-right: auto !important; margin-top: 0 !important; margin-bottom: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; box-sizing: border-box !important; float: none !important; } /* 3) 卡片栅格列:占满整行(单列)。 上下卡片间距 = 卡片距屏幕左右边缘的距离 = 16px (左右实测:容器 8 + row 8 = 卡片距屏幕 16px,两边相等即居中) */ /* v132.88:末尾的 :not([class*="to_hide"]) 是关键,别删。 用户页 / 番剧页的列表同样带「下一页预渲染占位」—— 官方 24 个 col_6.col_md_4,后面还跟着 12 个 to_hide_md.col_6.col_md_4,由站内响应式规则隐藏为 0x0。 这条规则此前无差别 display:block,等于把那 12 个也放了出来 → 每页 36 张、翻页重复 12 张(与视频页 24/42 同一个病)。 用 :not 从源头排除,比事后隐藏更干净:本页项照旧,占位项 根本不吃这条规则,回落到站内的隐藏。 */ html body.bili-search-page #app .bangumi-pgc-list .media-item-col:not([class*="to_hide"]), html body.bili-search-page #app .bangumi-pgc-list [class*="media-item-col"]:not([class*="to_hide"]), html body.bili-search-page #app .media-list [class*="col_6"]:not([class*="to_hide"]), html body.bili-search-page #app .media-list [class*="col_md_4"]:not([class*="to_hide"]), html body.bili-search-page #app .media-list .mb_x40:not([class*="to_hide"]), body.bili-search-page .bangumi-pgc-list [class*="media-item-col"]:not([class*="to_hide"]), body.bili-search-page .media-list [class*="col_6"]:not([class*="to_hide"]), body.bili-search-page .media-list [class*="col_md_4"]:not([class*="to_hide"]), body.bili-search-page .media-list .mb_x40:not([class*="to_hide"]) { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; padding-top: 0 !important; padding-bottom: 0 !important; margin-left: auto !important; margin-right: auto !important; margin-top: 0 !important; margin-bottom: 16px !important; height: auto !important; min-height: 0 !important; max-height: none !important; box-sizing: border-box !important; float: none !important; } /* 4) 卡片本体:满宽居中(综合页 232/328 -> 328,与综合页同一条边线) 【关键1】.media-card 原生就是 flex 行布局(左封面 148 + 右信息区), 上一版误写成 display:block,把封面和文字压成了上下排列(卡片 198 -> 647 高)。 这里显式写回 flex row:左封面固定 148,右信息区吃掉剩余宽度。 【关键2】改回 flex 后信息区变成 180(148+180=328)把间距吃没了, 原生是 封面148 + 间距16 + 信息区164 = 328, 所以用 column-gap:16px 把这段间距加回来(信息区自动回到 164)。 */ html body.bili-search-page #app .bangumi-pgc-list .media-card, html body.bili-search-page #app .media-list .media-card, body.bili-search-page .bangumi-pgc-list .media-card, body.bili-search-page .media-list .media-card, body.bili-search-page .media-card { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: flex-start !important; column-gap: 16px !important; gap: 0 16px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin-left: auto !important; margin-right: auto !important; margin-top: 0 !important; margin-bottom: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; box-sizing: border-box !important; float: none !important; } /* 4-1) 左封面:固定 148px(与原生一致),不拉伸、不压缩 */ html body.bili-search-page #app .bangumi-pgc-list .media-card > .media-card-image, html body.bili-search-page #app .media-list .media-card > .media-card-image, body.bili-search-page .bangumi-pgc-list .media-card > .media-card-image, body.bili-search-page .media-list .media-card > .media-card-image { flex: 0 0 148px !important; width: 148px !important; min-width: 148px !important; max-width: 148px !important; height: auto !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; float: none !important; } html body.bili-search-page #app .media-card > .media-card-image picture, html body.bili-search-page #app .media-card > .media-card-image img, body.bili-search-page .media-card > .media-card-image picture, body.bili-search-page .media-card > .media-card-image img { width: 100% !important; height: auto !important; max-width: 100% !important; object-fit: cover !important; } /* 4-2) 右信息区:吃掉剩余宽度(垂搜页 68 -> 164,与综合页一致) */ html body.bili-search-page #app .bangumi-pgc-list .media-card > .media-card-content, html body.bili-search-page #app .media-list .media-card > .media-card-content, body.bili-search-page .bangumi-pgc-list .media-card > .media-card-content, body.bili-search-page .media-list .media-card > .media-card-content { flex: 1 1 auto !important; width: auto !important; min-width: 0 !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; float: none !important; } /* 5) 「查看全部 N 部相关番剧作品」:解除裁剪 + 在长条容器内完全居中 【叠加的真因】按钮带 .p_center(原生靠 absolute + transform 居中), 脱出文档流后 .show-more-text 高度塌成 0,按钮就画在别的内容上面了。 这里把按钮和外框都改回 static,让按钮重新撑起高度、参与正常排版; 容器已解除固定高度 + overflow:visible,按钮得以完整显示且不再重叠。 【上下不居中的原因】外框是 block 长条,按钮是 inline-block, 只能靠 text-align 左右居中;垂直方向贴着 padding-top 走,天然偏上。 这里把外框改成 flex,align-items:center + justify-content:center, 按钮在长条容器内左右、上下都居中;外框保留最小高度,保证长条背景完整。 */ html body.bili-search-page #app .bangumi-pgc-list .show-more-text, html body.bili-search-page #app .media-list .show-more-text, body.bili-search-page .bangumi-pgc-list .show-more-text, body.bili-search-page .media-list .show-more-text { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: center !important; justify-content: center !important; position: static !important; visibility: visible !important; opacity: 1 !important; width: 100% !important; max-width: 100% !important; height: auto !important; min-height: 40px !important; max-height: none !important; /* 上下 padding 必须对称,否则即使 flex 居中,按钮视觉上仍会偏上 */ padding: 6px 0 !important; margin: 8px auto 0 !important; overflow: visible !important; text-align: center !important; box-sizing: border-box !important; float: none !important; clear: both !important; } html body.bili-search-page #app .bangumi-pgc-list .show-more-text button, html body.bili-search-page #app .media-list .show-more-text button, body.bili-search-page .bangumi-pgc-list .show-more-text button, body.bili-search-page .media-list .show-more-text button, body.bili-search-page .show-more-text button { display: inline-flex !important; align-items: center !important; justify-content: center !important; position: static !important; top: auto !important; right: auto !important; bottom: auto !important; left: auto !important; transform: none !important; visibility: visible !important; opacity: 1 !important; flex: 0 0 auto !important; width: auto !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; max-height: none !important; padding: 4px 8px !important; margin: 0 !important; line-height: normal !important; white-space: normal !important; overflow: visible !important; text-align: center !important; box-sizing: border-box !important; float: none !important; } body.bili-search-page .video-item + .video-item, body.bili-search-page .video-list > li + li, body.bili-search-page .search-result-list > li + li { border-top: none !important; padding-top: 0 !important; margin-top: 0 !important; } body.bili-search-page .video-item::before, body.bili-search-page .video-item::after, body.bili-search-page .bili-video-card::before, body.bili-search-page .bili-video-card::after, body.bili-search-page .video-list > li::before, body.bili-search-page .video-list > li::after { display: none !important; content: none !important; } html body.bili-search-page #app .video-list > *, html body.bili-search-page #app .search-video-list > *, html body.bili-search-page #app .card-list > *, html body.bili-search-page #app .search-result-list > *, html body.bili-search-page #app ul.video-list > *, html body.bili-search-page #app .search-page-video-list > *, html body.bili-search-page #app .search-all-list .video-list > *, html body.bili-search-page #app .video-list.row > div[class*="col_"], html body.bili-search-page #app .video-list.row > .col_3, html body.bili-search-page #app .video-list.row > .col_xs_1_5, html body.bili-search-page #app .video-list.row > .col_md_2 { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; float: none !important; box-sizing: border-box !important; flex: 1 1 100% !important; position: static !important; } /* v132.87:搜索页每页卡片数回归官方(24)—— 隐藏站内预渲染的「下一页」占位卡。 站内 SSR 一次吐 42 个 .video-list-item:前 24 个是本页 (.video-list-item.col_3.col_xs_1_5.col_md_2.col_xl_1_7.mb_x40), 后 18 个多带一个 to_hide_xs 类,由站内响应式规则隐藏为 0x0。 上面那条「列表项两列」规则此前把 .to_hide_xs / _md / _xl 也一并 display:block,等于替站内把这 18 个占位卡放了出来 —— 于是每页变 42 张、 翻到第 2 页就与前页重复 18 张。现把选择器还原为站内本意:带 to_hide 的 一律隐藏(display:none 的同时把尺寸 / 边距 / 定位也归零,防它靠 position:absolute 或残留宽高继续占位)。 特异性说明:用了 [class*="to_hide"][class*="video-list-item"] 双属性 选择器(比上面 div[class*="col_"] 多一层),不必依赖书写顺序。 v132.88:同一条规则扩展到用户页 / 番剧页。 用户垂搜页(.search-page-upuser)容器是 .media-list.row.mt_x40, 不含 "video-list",上面几条一条都选不中;放它们出来的是 「番剧区统一边距」那组的 .media-list [class*="col_6"] / [class*="col_md_4"]。 那里已用 :not([class*="to_hide"]) 从源头排除,这里再补一层显式隐藏 做双保险 —— 用 [class*="-list"] 泛化容器名(media-list / video-list / card-list 一网打尽),并叠第二个属性选择器把特异性抬到 (1,4,2), 稳压 (1,3,3) 的显示规则,不依赖书写顺序。 */ html body.bili-search-page #app .video-list.row > div[class*="to_hide"][class*="video-list-item"], html body.bili-search-page #app .video-list.row > div[class*="to_hide"], html body.bili-search-page #app [class*="video-list"] > div[class*="to_hide"], html body.bili-search-page #app [class*="-list"] > div[class*="to_hide"], html body.bili-search-page #app [class*="-list"] div[class*="to_hide"][class*="col_"], html body.bili-search-page #app [class*="-list"] > div[class*="to_hide"] > *, html body.bili-search-page #app .video-list.row > div[class*="to_hide"] > .bili-video-card, html body.bili-search-page #app .video-list.row > div[class*="to_hide"] .bili-video-card__wrap { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; max-width: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; border: 0 !important; flex: 0 0 0 !important; float: none !important; opacity: 0 !important; pointer-events: none !important; position: absolute !important; left: -99999px !important; top: 0 !important; content: none !important; } html body.bili-search-page #app .bili-video-card, html body.bili-search-page #app .bili-video-card.bili-video-card, html body.bili-search-page #app .bili-video-card.bili-video-card.bili-video-card, html body.bili-search-page #app .search-all-list .bili-video-card, html body.bili-search-page #app .video-list.row .bili-video-card { height: auto !important; max-height: none !important; min-height: 172px !important; width: 100% !important; max-width: 100% !important; min-width: 100% !important; margin: 0 !important; padding: 0 !important; display: block !important; align-self: start !important; background: #ffffff !important; border: none !important; border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0,0,0,0.08) !important; overflow: hidden !important; box-sizing: border-box !important; position: relative !important; float: none !important; flex: 1 1 100% !important; } html body.bili-search-page #app .bili-video-card__skeleton, html body.bili-search-page #app .bili-video-card__skeleton--cover, html body.bili-search-page #app .bili-video-card__skeleton--info, html body.bili-search-page #app .bili-video-card__skeleton--right, html body.bili-search-page #app .bili-video-card__skeleton--text, html body.bili-search-page #app .bili-video-card__skeleton--light, html body.bili-search-page #app .bili-video-card__skeleton.hide, html body.bili-search-page #app .bili-video-card__skeleton--text.short { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; flex: none !important; display: block !important; } /* ---- v132.11:骨架屏与最终卡片同尺寸,消除"先窄后宽"的跳动 ---- 现象(搜索页 → 综合页): ① 先出现一个**居中、左右边距很宽**的骨架屏 ② 随后它"放大"到左右边距 8px,才换上真正的视频卡片 直播页没这问题 —— 它的骨架屏天生就是卡片尺寸。 根因:两列网格的规则只挂在 html body.bili-search-page #app .video-list(.row) 等 这一串**固定类名**上。骨架屏阶段站内渲染的列表容器不在 这串名单里(类名不同),于是走站内原生布局 (flex + 居中 + PC 定宽),看起来就是"窄而且居中"; 真实卡片到位后容器才对上名单、被 grid 成两列 → 宽度突变。 修法分两层: ① CSS:骨架屏内部尺寸直接钉成最终卡片的尺寸 (封面 16:9 ≈95px,整卡 min-height 172px), 这样即便容器布局不同,骨架屏本身也不会变形; ② JS 兜底:容器类名无从预知,改由 JS 找出"真正装着 多个骨架屏的那一层",给它打上 .bili-skel-grid, 本段再用与综合页完全相同的 grid 两列渲染它 —— 骨架屏从第一帧起就是最终的两列布局,不再跳动。 ---- */ /* ① 骨架屏本体:尺寸与真实卡片一致 */ body.bili-search-page .bili-video-card__skeleton, body.bili-search-page .bili-live-card__skeleton { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 172px !important; max-height: none !important; margin: 0 !important; padding: 0 !important; display: block !important; background: #ffffff !important; border-radius: 8px !important; overflow: hidden !important; box-sizing: border-box !important; } /* 封面 16:9(168 宽 → 95 高);不支持 aspect-ratio 时由 min-height 兜住同一高度,两种情况下尺寸都一致 */ body.bili-search-page .bili-video-card__skeleton--cover, body.bili-search-page .bili-live-card__skeleton--cover { width: 100% !important; height: auto !important; min-height: 95px !important; aspect-ratio: 16 / 9 !important; margin: 0 !important; padding: 0 !important; border-radius: 0 !important; box-sizing: border-box !important; } /* ② .bili-skel-grid:JS 找到的骨架屏列表层, 套用与综合页完全相同的两列网格 */ body.bili-search-page .bili-skel-grid { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; grid-gap: 8px !important; gap: 8px !important; row-gap: 8px !important; column-gap: 8px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 8px !important; margin: 0 !important; box-sizing: border-box !important; align-items: start !important; align-content: start !important; justify-items: stretch !important; background-color: #f1f2f3 !important; } body.bili-search-page .bili-skel-grid > * { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; float: none !important; flex: none !important; box-sizing: border-box !important; } body.bili-search-page .bili-video-card__wrap, body.bili-search-page .bili-video-card__info, body.bili-search-page .bili-video-card__image--wrap { height: auto !important; max-height: none !important; min-height: 0 !important; width: 100% !important; max-width: 100% !important; box-sizing: border-box !important; } body.bili-search-page .flex_center.mt_x50, body.bili-search-page .flex_center.mb_x50, body.bili-search-page [class*="mt_x50"], body.bili-search-page [class*="mb_x50"] { margin-top: 8px !important; margin-bottom: 8px !important; } body.bili-search-page .vui_pagenation, body.bili-search-page .page-wrap, body.bili-search-page .pagination { display: flex !important; justify-content: center !important; width: 100% !important; max-width: 100% !important; padding: 8px 0 !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-search-page .vui_pagenation--btns { display: flex !important; flex-wrap: nowrap !important; justify-content: flex-start !important; align-items: center !important; width: 100% !important; max-width: 100% !important; padding: 0 8px !important; margin: 0 !important; box-sizing: border-box !important; gap: 4px !important; overflow-x: auto !important; -webkit-overflow-scrolling: touch !important; } body.bili-search-page .vui_pagenation--btns::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; } body.bili-search-page .vui_pagenation--btns { scrollbar-width: none !important; -ms-overflow-style: none !important; } body.bili-search-page .vui_pagenation--btn { flex-shrink: 0 !important; flex-grow: 0 !important; margin: 0 !important; min-width: 32px !important; padding: 0 6px !important; height: 34px !important; line-height: 34px !important; box-sizing: border-box !important; white-space: nowrap !important; font-size: 13px !important; text-align: center !important; border-radius: 4px !important; } body.bili-search-page .vui_pagenation--btn-side { min-width: 44px !important; padding: 0 10px !important; } /* ===== 视频页 ===== */ body.bili-video-page { background: #ffffff !important; overflow-x: hidden !important; min-height: 100vh !important; margin-top: 0 !important; padding-top: 0 !important; } body.bili-video-page { --video-equal-spacing: 36px; } body.bili-video-page, body.bili-video-page #app, body.bili-video-page #app > div, body.bili-video-page #mirror-vdcon.video-container-v1, body.bili-video-page .left-container, body.bili-video-page .left-container.scroll-sticky { width: 100vw !important; max-width: 100vw !important; min-width: 100vw !important; overflow-x: hidden !important; box-sizing: border-box !important; margin-left: 0 !important; margin-right: 0 !important; padding-left: 0 !important; padding-right: 0 !important; left: 0 !important; right: 0 !important; position: relative !important; } body.bili-video-page .left-container, body.bili-video-page .left-container.scroll-sticky { padding: 0 8px !important; } body.bili-video-page #app, body.bili-video-page #app > div, body.bili-video-page #mirror-vdcon.video-container-v1, body.bili-video-page #mirror-vdcon > *, body.bili-video-page .left-container, body.bili-video-page .left-container.scroll-sticky, body.bili-video-page .left-container > *, body.bili-video-page #viewbox_report, body.bili-video-page #viewbox_report > *, body.bili-video-page #viewbox_report .video-info-title, body.bili-video-page #viewbox_report .video-info-title-inner, body.bili-video-page #viewbox_report h1.video-title, body.bili-video-page #viewbox_report .video-info-meta, body.bili-video-page #viewbox_report .video-info-detail-list, body.bili-video-page #viewbox_report .pubdate-ip, body.bili-video-page #viewbox_report .pubdate-ip-text { margin-top: 0 !important; padding-top: 0 !important; border-top: 0 !important; top: 0 !important; bottom: auto !important; } body.bili-video-page #viewbox_report { margin-top: var(--video-equal-spacing) !important; margin-bottom: var(--video-equal-spacing) !important; padding-top: 0 !important; } body.bili-video-page #playerWrap { width: 100% !important; max-width: 100% !important; height: calc((100vw - 22px) * 9 / 16 + 6px) !important; max-height: calc((100vw - 22px) * 9 / 16 + 6px) !important; min-width: 0 !important; min-height: 0 !important; overflow: hidden !important; box-sizing: border-box !important; border: 3px solid #00aeec !important; border-radius: 12px !important; box-shadow: 0 4px 16px rgba(0, 174, 236, 0.25) !important; margin-top: 0 !important; margin-bottom: var(--video-equal-spacing) !important; position: relative !important; } body.bili-video-page #playerWrap #bilibili-player-placeholder, body.bili-video-page #playerWrap #bilibili-player, body.bili-video-page #playerWrap .bpx-docker, body.bili-video-page #playerWrap .bpx-player-container, body.bili-video-page #playerWrap .bpx-player-primary-area, body.bili-video-page #playerWrap .bpx-player-video-area, body.bili-video-page #playerWrap .bpx-player-video-wrap { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 100% !important; max-height: 100% !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; transform: none !important; zoom: normal !important; border-radius: 0 !important; } body.bili-video-page #playerWrap video { width: 100% !important; height: 100% !important; object-fit: contain !important; display: block !important; margin: 0 !important; padding: 0 !important; background-color: #000 !important; } body.bili-video-page #v_desc { margin-top: 0 !important; margin-bottom: var(--video-equal-spacing) !important; } body.bili-video-page #commentapp { margin-top: 0 !important; padding-top: 0 !important; } body.bili-video-page #biliMainHeader, body.bili-video-page .bili-header, body.bili-video-page .international-header, body.bili-video-page .mini-header, body.bili-video-page .nav-menu, body.bili-video-page .fixed-sidenav-storage, body.bili-video-page .video-note-sidebar-panel, body.bili-video-page .video-note-sidebar-chapter-layer, body.bili-video-page .video-note-sidebar-chapter-portal, body.bili-video-page .go-top, body.bili-video-page .bili-footer, body.bili-video-page .footer, body.bili-video-page [class*="back-top"], body.bili-video-page [class*="to-top"] { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; } body.bili-video-page #arc_toolbar_report, body.bili-video-page .video-toolbar-container { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; } /* v131.19 视频页 UP 主信息区:移除「关注」前面的「充电」按钮 实测结构:.up-info__btn-panel > .upinfo-btn-panel > div.default-btn.old-charge-btn.not-follow-charge-btn ← 充电 > div.default-btn.follow-btn.b-gz.not-follow ← 关注 这里用 [class*="charge"] 覆盖全部版本(old / new / 已关注变体), 只写 display:none:flex 布局下被移除项不会占用 gap, 关注按钮会自动左移,不留空隙。 */ body.bili-video-page .up-info__btn-panel .new-charge-btn, body.bili-video-page .default-btn.new-charge-btn, body.bili-video-page .default-btn.old-charge-btn, body.bili-video-page .default-btn.not-follow-charge-btn, body.bili-video-page .default-btn.follow-charge-btn, body.bili-video-page .up-info__btn-panel [class*="charge"], body.bili-video-page .upinfo-btn-panel [class*="charge"] { display: none !important; visibility: hidden !important; } body.bili-video-page [class*="ad-"], body.bili-video-page [class*="ads-"], body.bili-video-page [class*="advertisement"], body.bili-video-page .video-card-ad-small, body.bili-video-page .video-card-ad, body.bili-video-page .slide-ad-exp, body.bili-video-page .slide-gg, body.bili-video-page .banner-ad, body.bili-video-page .ad-floor-exp, body.bili-video-page .ad-report, body.bili-video-page .popup-ad, body.bili-video-page .float-ad { display: none !important; visibility: hidden !important; height: 0 !important; width: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-video-page .video-resource-list, body.bili-video-page .activity-m-v1, body.bili-video-page [class*="video-resource-list"] { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-video-page .right-container .video-page-game-card-small, body.bili-video-page .right-container [class*="game-card-small"] { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; padding: 0 !important; margin: 0 !important; } body.bili-video-page .right-container #danmukuBox, body.bili-video-page .right-container .video-pod-above-modules, body.bili-video-page .right-container .danmaku-box, body.bili-video-page .right-container .bpx-player-auxiliary { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; } body.bili-video-page .bpx-player-sending-area, body.bili-video-page .bpx-player-sending-bar, body.bili-video-page .bpx-player-dm-root, body.bili-video-page .bpx-player-video-inputbar { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-video-page .bpx-player-ctrl-pip, body.bili-video-page .bpx-player-ctrl-wide, body.bili-video-page .bpx-player-ctrl-time, body.bili-video-page .bpx-player-ctrl-eplist { display: none !important; } @media screen and (orientation: landscape) { body.bili-video-page .bpx-player-ctrl-time { display: block !important; } } @media screen and (width >= 750px) { body.bili-video-page .bpx-player-container[data-screen=full] .bpx-player-ctrl-quality-result { height: unset !important; font-size: 16px !important; } body.bili-video-page .bpx-player-container[data-screen=full] .bpx-player-control-wrap { height: 43px !important; } body.bili-video-page .bpx-player-container[data-screen=full] .bpx-player-control-top { bottom: 43px !important; } } body.bili-video-page div.bpx-player-control-bottom { height: 29px !important; margin-top: 7px !important; padding: 0 7px !important; } body.bili-video-page div.bpx-player-control-top { transition: none; bottom: 36px; } body.bili-video-page .bpx-player-pbp .bpx-player-pbp-pin-tip { display: none !important; } body.bili-video-page .bpx-player-control-bottom-left, body.bili-video-page .bpx-player-control-bottom-right { flex: unset !important; } body.bili-video-page .bpx-player-container .bpx-player-control-bottom-left, body.bili-video-page .bpx-player-container .bpx-player-control-bottom-right { min-width: 0 !important; } body.bili-video-page .bpx-player-ctrl-quality { min-width: 0; flex: auto !important; margin-right: 0 !important; } body.bili-video-page .bpx-player-ctrl-quality-result, body.bili-video-page .bpx-player-ctrl-playbackrate { font-size: 12px !important; } body.bili-video-page .bpx-player-ctrl-quality-result { height: 22px; overflow: hidden; } body.bili-video-page .bpx-player-ctrl-playbackrate { text-wrap: nowrap; } body.bili-video-page .bpx-player-progress-wrap { height: 7px !important; padding-bottom: 3px !important; } body.bili-video-page .bpx-player-control-mask { background: linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,0.5) 100%) !important; } body.bili-video-page .bpx-player-ctrl-quality-menu-wrap { bottom: 22px !important; } body.bili-video-page .bpx-player-ctrl-quality-menu-item { max-width: 95px; max-height: 36px; height: 7.7vw !important; padding: 0 8px 0 12px !important; } body.bili-video-page .bpx-player-ctrl-quality-badge-bigvip { color: #fff; background-color: #f25d8e; width: 16px; overflow: hidden; right: 8px !important; } body.bili-video-page .bpx-player-ctrl-quality-badge-bigvip:before { color: #fff; content: "V"; background-color: #f25d8e; padding: 0 4px; position: absolute; left: 0; } body.bili-video-page .bpx-player-ctrl-playbackrate-menu { bottom: 22px !important; } body.bili-video-page .bpx-player-ctrl-playbackrate-menu-item { justify-content: center; align-items: center; max-height: 36px; display: flex; height: 7.7vw !important; } body.bili-video-page div.bpx-player-ctrl-subtitle-box { bottom: 0; right: 0; transform: scale(.8); } body.bili-video-page .bpx-player-ctrl-setting-box { bottom: 0 !important; right: 0 !important; } body.bili-video-page .bpx-player-ctrl-setting-menu-right { padding: 5px !important; } body.bili-video-page .bpx-player-ctrl-setting-menu-right > div { justify-content: center; align-items: center; max-height: 40px; display: flex; height: 10vw !important; } body.bili-video-page .bpx-player-ctrl-setting-menu-right .bui-radio { width: 77%; margin: 0 0 8px 7px; } body.bili-video-page .bpx-player-ctrl-setting-others-content { margin-left: 7px; width: 77% !important; } body.bili-video-page .bpx-player-ctrl-setting-highenergy .bui-checkbox-name { white-space: nowrap; width: 48px; overflow: hidden; } body.bili-video-page .bpx-player-dm-setting-wrap { top: 0; left: 50%; transform: translate(-50%); bottom: unset !important; position: fixed !important; } body.bili-video-page .bpx-player-control-bottom-center .bpx-player-sending-bar { height: 24px !important; padding-right: 6px !important; } body.bili-video-page .bpx-player-ctrl-viewpoint { flex-shrink: 1 !important; width: 45px !important; min-width: 0 !important; margin: 0 !important; } body.bili-video-page .bpx-player-ctrl-viewpoint-text { flex: none; font-size: 12px; width: 24px !important; text-overflow: unset !important; } body.bili-video-page .bpx-player-control-wrap:not(.new) { display: none; } body.bili-video-page .bpx-player-control-entity, body.bili-video-page .bpx-player-control-mask { display: block !important; } body.bili-video-page .bpx-player-container[data-ctrl-hidden=true] .bpx-player-control-bottom { display: none; } body.bili-video-page .bpx-player-container[ctrl-shown=false] .bpx-player-control-wrap .bpx-player-control-mask { opacity: 0; transition: opacity .2s ease-in; } body.bili-video-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-wrap .bpx-player-control-mask { opacity: 1; } body.bili-video-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-entity .bpx-player-control-bottom { opacity: 1; display: flex; } body.bili-video-page .bpx-player-container[ctrl-shown=false] .bpx-player-control-entity .bpx-player-control-bottom { display: none; } body.bili-video-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-entity .bpx-player-control-top, body.bili-video-page .bpx-player-container[ctrl-shown=false] .bpx-player-control-entity .bpx-player-shadow-progress-area { opacity: 1; visibility: visible; } body.bili-video-page .bpx-player-container[ctrl-shown=false] .bpx-player-control-entity .bpx-player-control-top, body.bili-video-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-entity .bpx-player-shadow-progress-area { opacity: 0; visibility: hidden; } body.bili-video-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-entity .bpx-player-pbp { opacity: 1; width: 100%; bottom: calc(100% + 6px); left: 0; } body.bili-video-page div.bpx-player-control-entity .bpx-player-pbp { opacity: 0; width: calc(100% + 24px); bottom: 1px; left: -12px; } body.bili-video-page div.bpx-player-control-entity .bpx-player-pbp.pin { opacity: 1; } body.bili-video-page .bpx-player-pbp-pin { display: none; opacity: 1 !important; } body.bili-video-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-entity .bpx-player-pbp-pin { display: block; } body.bili-video-page .bpx-player-video-perch { max-height: 0; } body.bili-video-page .bpx-player-top-wrap, body.bili-video-page .bpx-player-state-wrap { display: none !important; } body.bili-video-page .bpx-player-container, body.bili-video-page #bilibili-player-placeholder { box-shadow: none !important; } /* ★★★ v132.44:视频页 / 列表页 —— 站内「网页全屏」强制按回播放器内 ★★★ 现象:非 via 浏览器打开视频页,画面自动铺满整屏并往左上偏(-84,-48)。 实测 #playerWrap 344x196、#bilibili-player 338x190 全部正常, 唯独 .bpx-player-container 是 360x800 / position:fixed / z-index:100000 —— 站内 JS 把它切进了「网页全屏」态。 两个后果: 1) 元素一变 fixed,上面 2116 行那组 width/height:100% 的百分比 基准就从 #playerWrap(338x190)换成**视口**,直接算出满屏; 2) 站内同时按它自己以为的容器尺寸(约 528x896)写 left/top:50% + 负 margin 居中,实际渲染 360x800, 两套数值打架 → 上下各溢出 48、左右各溢出 84。 番剧页不受影响,是因为 3621 行那组写了 position:absolute+top/left:0, 天生不吃这一套 —— 这里照抄同样的写法补齐视频页 / 列表页。 :not(.video-fullscreen) 必须保留:真·原生全屏要靠下面 body.video-fullscreen #playerWrap 那组铺满视口的规则,不能被拽回来。 z-index:auto 顺带解掉 fixed+z-index:100000 造出的堆叠上下文, 让 v132.27 那批 fixed 视口居中的面板彻底出得来。 */ body.bili-video-page:not(.video-fullscreen) #playerWrap .bpx-player-container, body.bili-list-page:not(.video-fullscreen) #playerWrap .bpx-player-container { position: absolute !important; top: 0 !important; left: 0 !important; right: auto !important; bottom: auto !important; margin: 0 !important; transform: none !important; z-index: auto !important; } /* ★★★ v132.45:移除「迷你播放器(小窗)」占位遮罩层 ★★★ 现象:via 以外的浏览器(Chrome / Edge / 夸克等)打开视频页, 播放器画面被一整层遮罩糊死 —— 正中一个「电视机 + 播放/暂停」 大图案,右上角一个 × 关闭键,底部还压着一条进度条。 探针实测(bili_probe_2026-10-02_09-43-20): .bpx-player-video-area > .bpx-player-mini-warp 338x190 / position:absolute / z-index:12 —— 与画面同尺寸、 铺满整个视频区,z=12 又高于画面,所以整块看不见了; 内部三个零件正好对上肉眼所见: .bpx-player-mini-close 右上 26x26(× 的 svg) .bpx-player-mini-state 居中 100x100(电视 + 暂停图案) .bpx-player-mini-progress 底部 338x3(进度条) 它是站内「小窗播放」留在原位的占位层,本脚本不需要小窗占位, 整层连子元素一并移除;选择器不带 body class,视频页 / 列表页 / 番剧页(同为 bpx 播放器)一次全中。 */ .bpx-player-mini-warp, .bpx-player-mini-warp * { display: none !important; visibility: hidden !important; pointer-events: none !important; } body.video-fullscreen { overflow: hidden !important; } body.video-fullscreen #mirror-vdcon, body.video-fullscreen .left-container { width: 100vw !important; max-width: 100vw !important; min-width: 100vw !important; height: 100vh !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.video-fullscreen #playerWrap { width: 100vw !important; max-width: 100vw !important; height: 100vh !important; max-height: 100vh !important; min-height: 100vh !important; border: none !important; border-radius: 0 !important; box-shadow: none !important; overflow: hidden !important; margin: 0 !important; } body.video-fullscreen #playerWrap #bilibili-player-placeholder, body.video-fullscreen #playerWrap #bilibili-player, body.video-fullscreen #playerWrap .bpx-docker, body.video-fullscreen #playerWrap .bpx-player-container, body.video-fullscreen #playerWrap .bpx-player-primary-area, body.video-fullscreen #playerWrap .bpx-player-video-area, body.video-fullscreen #playerWrap .bpx-player-video-wrap { width: 100% !important; height: 100% !important; max-width: 100% !important; max-height: 100% !important; min-width: 0 !important; min-height: 0 !important; } body.video-fullscreen .bpx-player-video-wrap video { object-fit: contain !important; } /* ★ 侧边栏通用(视频页 / 番剧页):仅保留通用的 fixed + GPU 合成,不加 padding ★ v132.62 保留的两处降载: ① will-change 只在动画期间挂(.bili-drawer-anim),动画一结束就摘, 不让一整块内容常驻在合成层里; ② transition 0.3s → 0.26s 且换成减速曲线,尾段更轻; 阴影范围收窄(-4px 16px → -2px 10px),每帧重绘的像素更少。 ⚠ v132.60 曾加过两条,已在 v132.62 撤掉(会弄坏抽屉内的展开/收起): · content-visibility:hidden(关闭态)—— 抽屉未打开时整块不布局, 站内组件在挂载阶段量到的是 0 / contain-intrinsic-size 的假尺寸, "展开"算出来的高度就是错的; · 抽屉内 * 的 animation/transition:none —— 站内展开/收起是 "设 height 起动画 → transitionend 里清掉行内 height" 的状态机, 过渡被禁掉后 transitionend 永不到来,行内 height 被永久钉在 展开后的数值上。两处细节见变更记录 v132.62。 */ body.bili-video-page .right-container, body.bili-bangumi-page .plp-r-wrap { position: fixed !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: auto !important; width: 85vw !important; max-width: 420px !important; height: 100vh !important; max-height: 100vh !important; overflow-y: auto !important; overflow-x: hidden !important; background: #ffffff !important; z-index: 99999 !important; transform: translate3d(100%, 0, 0) !important; transition: transform 0.26s cubic-bezier(0.22, 0.61, 0.36, 1) !important; backface-visibility: hidden; -webkit-backface-visibility: hidden; box-shadow: -2px 0 10px rgba(0, 0, 0, 0.18) !important; box-sizing: border-box !important; -webkit-overflow-scrolling: touch !important; display: block !important; overscroll-behavior: contain; /* v132.61:滑到头不再带动底层页面 */ } /* ① 只有动画期间才提升为合成层(动画结束即摘) */ body.bili-video-page .right-container.bili-drawer-anim, body.bili-bangumi-page .plp-r-wrap.bili-drawer-anim { will-change: transform; } body.bili-video-page .right-container.show, body.bili-bangumi-page .plp-r-wrap.show { transform: translate3d(0, 0, 0) !important; } /* ★ 番剧侧边栏:pad + 内部 plp-r 归位 ★ */ body.bili-bangumi-page .plp-r-wrap { padding: 12px !important; } body.bili-bangumi-page .plp-r.sticky { position: static !important; top: auto !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; display: block !important; } body.bili-bangumi-page .plp-r-wrap .PaginatedEpList_root__67C7p, body.bili-bangumi-page .plp-r-wrap .recommend_wrap__PccwM { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-bangumi-page .plp-r-wrap img { max-width: 100% !important; height: auto !important; } body.bili-bangumi-page .EpisodeVirtualList_scroll__62SmN { width: 100% !important; max-width: 100% !important; overflow-x: hidden !important; } body.bili-bangumi-page .EpisodeVirtualList_virtualCanvas__0AC8_ { width: 100% !important; max-width: 100% !important; min-width: 0 !important; } body.bili-bangumi-page .EpisodeVirtualList_gridRow__czcgd { display: flex !important; flex-wrap: nowrap !important; justify-content: center !important; align-items: center !important; gap: 8px !important; width: 100% !important; max-width: 100% !important; box-sizing: border-box !important; padding: 0 6px !important; } body.bili-bangumi-page .EpisodeVirtualList_numberItem__GLWgO { width: 46px !important; height: 46px !important; min-width: 46px !important; min-height: 46px !important; flex-shrink: 0 !important; border-radius: 8px !important; font-size: 14px !important; display: flex !important; flex-direction: column !important; align-items: center !important; justify-content: center !important; box-sizing: border-box !important; margin: 0 !important; padding: 0 !important; } body.bili-bangumi-page .EpisodeVirtualList_numberItem__GLWgO * { font-size: 10px !important; transform: scale(0.9) !important; } body.bili-bangumi-page #danmukuBox { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; } body.bili-video-page .right-container .right-container-inner { position: static !important; top: auto !important; height: auto !important; padding: 12px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-video-page .right-container .up-info--right, body.bili-video-page .right-container .up-info__btn-panel, body.bili-video-page .right-container .upinfo-btn-panel, body.bili-video-page .right-container .follow-btn, body.bili-video-page .right-container .interactions { overflow: visible !important; position: relative !important; } body.bili-video-page .right-container .follow-btn .menu-popover, body.bili-video-page .right-container .follow-btn-wrapper { position: absolute !important; top: 100% !important; right: 0 !important; left: auto !important; z-index: 9999999 !important; transform: none !important; margin-top: 4px !important; background: #fff !important; border-radius: 4px !important; box-shadow: 0 2px 8px rgba(0,0,0,0.15) !important; } body.bili-video-page .van-popover, body.bili-video-page .bili-popover, body.bili-video-page [class*="popover"] { z-index: 9999999 !important; } body.bili-video-page .right-container > *, body.bili-video-page .right-container .up-panel-container, body.bili-video-page .right-container .up-info-container, body.bili-video-page .right-container .video-pod-above-modules, body.bili-video-page .right-container .video-pod-above-modules__inner, body.bili-video-page .right-container .rcmd-tab, body.bili-video-page .right-container .recommend-list-v1, body.bili-video-page .right-container .rec-list, body.bili-video-page .right-container .video-page-card-small, body.bili-video-page .right-container .bpx-player-wraplist, body.bili-video-page .right-container .slide-ad-exp, body.bili-video-page .right-container .slide-gg, body.bili-video-page .right-container .ad-report, body.bili-video-page .right-container .ad-floor-cover { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; position: static !important; left: auto !important; right: auto !important; } body.bili-video-page .right-container .ad-report.ad-floor-exp.right-bottom-banner, body.bili-video-page .right-container .ad-floor-cover, body.bili-video-page .right-container .video-card-ad-small, /* v132.75:推荐列表里的「推广卡」——探针实测(bili_probe_2026-10-03_10-15-41): .rec-list 的第一个子级是 .video-page-special-card-small [282x80] / top=282, 文本「直击2026诺贝尔奖>> 专家&UP主独家解读,上B站看懂诺贝尔奖!」, 紧跟其下的才是正常推荐卡 .video-page-card-small(top=374)。 它用的是 special 而不是 ad 类名,此前那几条 ad-* 规则一条都没命中。 整块隐藏(高度 / 内外边距一起归零,避免留下 12px 的 margin-bottom 空档)。 */ .video-page-special-card-small { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; height: 0 !important; min-height: 0 !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; border: none !important; pointer-events: none !important; /* 防隐藏后仍占点击热区 */ } /* JS 兜底打的稳定标记 —— 站内若换掉 special 类名,按文本识别后仍收得掉 */ .bili-video-special-ad { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; height: 0 !important; min-height: 0 !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; border: none !important; pointer-events: none !important; } /* ===== v132.76:个性装扮页 —— 隐藏右侧扫码区 + 手机屏适配 ===== 探针实测(bili_probe_2026-10-03_10-24-57,视口 360x800): body/#app [360x800] └─ main._mainPage_zfm7p_1 [715x800] left=0 ovf=auto ← PC 定宽,横向溢出 ├─ section._leftPanel_ [375x760] left=0 top=20 │ └─ iframe#mall-iframe [375x760] absolute ← 装扮预览(真正要看的内容) └─ section._rightPanel_ [340x760] left=375 ← 扫码区(要隐藏) ├─ img._logo_ / div._title_「个性装扮」 ├─ div._outside_ > canvas._qrcode_ [300x300] ← 二维码 ├─ div._openClient_「打开客户端扫一扫」 └─ div._subTitle_「找寻属于你的个性装扮」 算一下:375 + 340 = 715,正是 main 的宽度 —— PC 上左右并排, 手机 360 视口下 main 横向溢出 355,右侧扫码区整块在屏幕外(left=375), 左边预览 iframe 也只露出 360/375,右侧还被切 15px。 真因:这是「左预览 + 右扫码」的 PC 两栏布局,没有任何窄屏分支。 修法: ① 右栏整块隐藏(display + 尺寸 + 边距全归零 + pointer-events:none); ② main 收回 100% 宽并禁止横向滚动; ③ 左栏吃满 360,高度按视口给(保留现有的上下 20px 呼吸位: 原 bottom=780,故取 calc(100vh - 20px)); ④ iframe 钉回左栏内左上角、铺满(它是 absolute 的, 父栏若不给定高,height:100% 会塌成 0 —— v132.55 那次灯箱 塌成 0x0 就是同一类"父高未定"的坑)。 ⚠ 类名全是 CSS Module 哈希(_mainPage_zfm7p_1,zfm7p 是构建哈希, 每次发版都会变),**不能写死完整类名** —— 用 [class*="_mainPage_"] 这类前缀匹配,只认哈希前的稳定语义名。 另加 body.bili-garb-page 兜底(由 JS 探测到 #mall-iframe 时打上), 万一将来哈希彻底改名也能靠 JS 那条命中。 未做:iframe 里的内容是独立文档(可能同源也可能不同源), 本脚本的样式表进不去,其内部的 PC 排版无法在此一并处理。 */ main[class*="_mainPage_"] { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; overflow-x: hidden !important; overflow-y: auto !important; box-sizing: border-box !important; } main[class*="_mainPage_"] > section[class*="_leftPanel_"] { display: block !important; position: relative !important; top: 0 !important; left: 0 !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: calc(100vh - 20px) !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; box-sizing: border-box !important; } /* iframe 用 absolute 铺满左栏 —— 左栏已给定高,height:100% 才有参照 */ main[class*="_mainPage_"] > section[class*="_leftPanel_"] > iframe, main[class*="_mainPage_"] iframe#mall-iframe, #mall-iframe { position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 100% !important; min-height: 0 !important; border: none !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* 右侧扫码区:整块隐藏 */ main[class*="_mainPage_"] > section[class*="_rightPanel_"], section[class*="_rightPanel_"], body.bili-garb-page section[class*="_rightPanel_"] { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; max-width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; border: none !important; pointer-events: none !important; } body.bili-video-page .right-container img, body.bili-video-page .right-container video, body.bili-video-page .right-container picture { max-width: 100% !important; height: auto !important; } body.bili-video-page .right-container .send-msg, body.bili-video-page .right-container a.send-msg, body.bili-video-page .right-container .up-detail-top > a.send-msg { display: none !important; visibility: hidden !important; } body.bili-video-page .right-container .up-info--right { flex: 1 1 auto !important; min-width: 0 !important; width: auto !important; max-width: 100% !important; display: flex !important; flex-direction: column !important; } body.bili-video-page .right-container .up-info__detail, body.bili-video-page .right-container .up-detail, body.bili-video-page .right-container .upinfo-sign, body.bili-video-page .right-container .upinfo-sign__text { width: 100% !important; max-width: 100% !important; min-width: 0 !important; word-break: break-all !important; white-space: normal !important; overflow-wrap: break-word !important; box-sizing: border-box !important; overflow: hidden !important; } body.bili-video-page .right-container .up-detail-top { display: flex !important; align-items: center !important; justify-content: flex-start !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; gap: 8px !important; } body.bili-video-page .right-container .up-name { white-space: nowrap !important; word-break: keep-all !important; text-overflow: ellipsis !important; overflow: hidden !important; flex-shrink: 1 !important; width: auto !important; max-width: 100% !important; min-width: 0 !important; display: block !important; } body.bili-video-page #viewbox_report, body.bili-video-page .video-info-title, body.bili-video-page .video-info-title-inner, body.bili-video-page .video-title, body.bili-video-page #v_desc, body.bili-video-page .basic-desc-info, body.bili-video-page .desc-info-text { text-align: center !important; justify-content: center !important; align-items: center !important; float: none !important; } body.bili-video-page h1.video-title { width: 100% !important; display: block !important; text-align: center !important; } body.bili-video-page #viewbox_report .show-more, body.bili-video-page #viewbox_report .show-more-btn { display: none !important; visibility: hidden !important; height: 0 !important; width: 0 !important; overflow: hidden !important; } body.bili-video-page #viewbox_report .video-info-detail-list > .item:not(.pubdate-ip) { display: none !important; visibility: hidden !important; } body.bili-video-page #viewbox_report .video-info-title, body.bili-video-page #viewbox_report .video-info-title-inner, body.bili-video-page h1.video-title { height: auto !important; max-height: none !important; overflow: visible !important; -webkit-line-clamp: unset !important; -webkit-box-orient: unset !important; text-overflow: clip !important; white-space: normal !important; word-break: break-all !important; padding: 0 !important; } body.bili-video-page #v_desc, body.bili-video-page #viewbox_report, body.bili-video-page .video-info-container, body.bili-video-page .left-container, body.bili-video-page #mirror-vdcon.video-container-v1, body.bili-video-page #app { height: auto !important; min-height: auto !important; max-height: none !important; overflow: visible !important; } body.bili-video-page #v_desc .basic-desc-info { height: auto !important; max-height: none !important; overflow: visible !important; } body.bili-video-page #v_desc .desc-info-text { display: block !important; height: auto !important; -webkit-line-clamp: unset !important; -webkit-box-orient: unset !important; overflow: visible !important; text-overflow: clip !important; max-height: none !important; white-space: pre-wrap !important; } body.bili-video-page #v_desc .toggle-btn, body.bili-video-page #v_desc .toggle-btn-text { display: none !important; visibility: hidden !important; height: 0 !important; padding: 0 !important; margin: 0 !important; } body.bili-video-page #viewbox_report .video-info-meta, body.bili-video-page #viewbox_report .video-info-detail-list { display: flex !important; justify-content: center !important; align-items: center !important; width: 100% !important; text-align: center !important; margin: 0 auto !important; padding: 0 !important; float: none !important; } body.bili-video-page #viewbox_report .video-info-detail-list .item { float: none !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; } body.bili-video-page .video-tag-container, body.bili-video-page .tag-panel, body.bili-video-page #mirror-vdcon > .left-container > .video-tag-container { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-video-page #commentapp .bili-comment-box, body.bili-video-page #commentapp .bili-comment-input-wrap, body.bili-video-page #commentapp .comment-send-box, body.bili-video-page #commentapp .comment-box, body.bili-video-page #commentapp .reply-box, body.bili-video-page #commentapp .bili-comment-container > .bili-comment-box, body.bili-video-page #commentapp .comment-container .comment-box, body.bili-video-page #commentapp .bili-comment-editor, body.bili-video-page #commentapp .bili-comment-editor-wrap { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-video-page #commentapp, body.bili-video-page #commentapp bili-comments, body.bili-video-page #commentapp .bili-comment-container, body.bili-video-page #commentapp .comment-container, body.bili-video-page #commentapp .comment-box, body.bili-video-page #commentapp .reply-box, body.bili-video-page #commentapp .bili-comment-box, body.bili-video-page #commentapp .bili-comment-input-wrap, body.bili-video-page #commentapp .comment-send-box { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; padding-left: 8px !important; padding-right: 8px !important; overflow: hidden !important; } body.bili-video-page #commentapp textarea, body.bili-video-page #commentapp input[type="text"], body.bili-video-page #commentapp .comment-textarea, body.bili-video-page #commentapp .bili-comment-textarea, body.bili-video-page #commentapp .reply-textarea, body.bili-video-page #commentapp [contenteditable="true"] { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; resize: none !important; word-break: break-all !important; white-space: pre-wrap !important; overflow-wrap: break-word !important; height: auto !important; min-height: 40px !important; padding: 8px !important; margin: 0 !important; overflow: hidden !important; } #my-bili-right-mask { position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; width: 100vw !important; height: 100vh !important; background: rgba(0, 0, 0, 0.35) !important; z-index: 99998 !important; opacity: 0 !important; visibility: hidden !important; pointer-events: none !important; transition: opacity 0.26s ease, visibility 0.26s ease !important; display: block !important; } #my-bili-right-mask.show { opacity: 1 !important; visibility: visible !important; pointer-events: auto !important; will-change: opacity; /* v132.60:只在淡入期间提升为合成层 */ } /* ★ v132.60:抽屉打开时暂停播放器内的动画 ★ 此时它们全在遮罩之下看不见,却仍在每帧重绘(缓冲转圈、 控制栏淡入淡出、弹幕区动画等)。用 animation-play-state 而不是 animation:none,不重置站内动画状态,关闭抽屉即恢复。 */ body.bili-drawer-open .bpx-player-container *, body.bili-drawer-open #bilibili-player * { animation-play-state: paused !important; } /* ============================================================ ========== 播放列表页(/list/):完整复用视频页优化 ========== ============================================================ */ body.bili-list-page { background: #ffffff !important; overflow-x: hidden !important; margin-top: 0 !important; padding-top: 0 !important; } /* 取消左右分栏:左栏独占整宽 */ body.bili-list-page #app.playlist-app, body.bili-list-page #mirror-vdcon.playlist-container { display: block !important; width: 100vw !important; max-width: 100vw !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; overflow-x: hidden !important; box-sizing: border-box !important; } body.bili-list-page #mirror-vdcon .playlist-container--left { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 8px !important; margin: 0 !important; float: none !important; overflow-x: hidden !important; box-sizing: border-box !important; } body.bili-list-page .playlist-container--left > * { max-width: 100% !important; } /* 标题 / 播放信息:与视频页完全一致的居中与等距 */ body.bili-list-page .video-info-container { margin-top: var(--video-equal-spacing) !important; margin-bottom: var(--video-equal-spacing) !important; padding: 0 !important; text-align: center !important; width: 100% !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; box-sizing: border-box !important; } body.bili-list-page .video-info-container .video-info-meta, body.bili-list-page .video-info-container .video-info-detail-list { display: flex !important; justify-content: center !important; align-items: center !important; width: 100% !important; text-align: center !important; margin: 0 auto !important; padding: 0 !important; float: none !important; } body.bili-list-page .video-info-container .video-info-detail-list .item { float: none !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; } body.bili-list-page .video-info-container .video-info-detail-list > .item:not(.pubdate-ip) { display: none !important; visibility: hidden !important; } /* 标题:解除 B站原生行数/高度限制(video-info-title 系列 + special-text-indent), 与视频页 #viewbox_report 下的标题处理保持一致,完整多行显示 */ body.bili-list-page .video-info-container .video-info-title, body.bili-list-page .video-info-container .video-info-title-inner, body.bili-list-page .video-info-container .video-title, body.bili-list-page .video-info-container h1.video-title, body.bili-list-page .video-info-container h1.video-title > a, body.bili-list-page .video-info-container .video-title-href, body.bili-list-page .video-info-container .video-info-title * { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; -webkit-line-clamp: unset !important; line-clamp: unset !important; -webkit-box-orient: horizontal !important; text-overflow: clip !important; white-space: normal !important; word-break: break-word !important; overflow-wrap: break-word !important; text-indent: 0 !important; line-height: normal !important; letter-spacing: normal !important; padding: 0 !important; margin: 0 !important; float: none !important; text-align: center !important; box-sizing: border-box !important; } body.bili-list-page .video-info-container .video-info-title::after, body.bili-list-page .video-info-container .video-info-title-inner::after, body.bili-list-page .video-info-container .video-title::after, body.bili-list-page .video-info-container .video-title::before, body.bili-list-page .video-info-container .video-info-title .show-more, body.bili-list-page .video-info-container .video-info-title .show-more-btn, body.bili-list-page .video-info-container .video-info-title .expand-btn { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; content: none !important; } /* 简介:始终完全展开(等价视频页 #v_desc) */ body.bili-list-page .video-desc-container { margin: 0 0 var(--video-equal-spacing) !important; width: 100% !important; max-width: 100% !important; text-align: center !important; box-sizing: border-box !important; } body.bili-list-page .video-desc-container .basic-desc-info { height: auto !important; max-height: none !important; overflow: visible !important; } body.bili-list-page .video-desc-container .desc-info-text { display: block !important; height: auto !important; max-height: none !important; -webkit-line-clamp: unset !important; -webkit-box-orient: unset !important; overflow: visible !important; text-overflow: clip !important; white-space: pre-wrap !important; word-break: break-all !important; } body.bili-list-page .video-desc-container .toggle-btn, body.bili-list-page .video-desc-container .toggle-btn-text { display: none !important; visibility: hidden !important; height: 0 !important; padding: 0 !important; margin: 0 !important; } /* 右栏(UP主 + 播放列表 + 推荐):与视频页 right-container 相同的浮动抽屉 */ body.bili-video-page #mirror-vdcon .playlist-container--right { position: fixed !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: auto !important; width: 85vw !important; max-width: 420px !important; height: 100vh !important; max-height: 100vh !important; overflow-y: auto !important; overflow-x: hidden !important; background: #ffffff !important; z-index: 99999 !important; transform: translate3d(100%, 0, 0) !important; transition: transform 0.26s cubic-bezier(0.22, 0.61, 0.36, 1) !important; backface-visibility: hidden; -webkit-backface-visibility: hidden; box-shadow: -2px 0 10px rgba(0, 0, 0, 0.18) !important; box-sizing: border-box !important; -webkit-overflow-scrolling: touch !important; display: block !important; overscroll-behavior: contain; /* v132.61:滑到头不再带动底层页面 */ padding: 12px !important; margin: 0 !important; float: none !important; } body.bili-video-page #mirror-vdcon .playlist-container--right.bili-drawer-anim { will-change: transform; } body.bili-video-page #mirror-vdcon .playlist-container--right.show { transform: translate3d(0, 0, 0) !important; } /* 抽屉内容:全部撑满宽度 */ body.bili-video-page .playlist-container--right > *, body.bili-video-page .playlist-container--right .up-panel-container, body.bili-video-page .playlist-container--right .up-info-container, body.bili-video-page .playlist-container--right .action-list-container, body.bili-video-page .playlist-container--right .action-list-header, body.bili-video-page .playlist-container--right .action-list-body, body.bili-video-page .playlist-container--right .action-list-body-top, body.bili-video-page .playlist-container--right .action-list-body-bottom, body.bili-video-page .playlist-container--right .action-list, body.bili-video-page .playlist-container--right .action-list-inner, body.bili-video-page .playlist-container--right .action-list-content, body.bili-video-page .playlist-container--right .action-list-item-wrap, body.bili-video-page .playlist-container--right .action-list-item, body.bili-video-page .playlist-container--right .singlep-list-item-inner, body.bili-video-page .playlist-container--right .multip-list-item-inner, body.bili-video-page .playlist-container--right .multip-list, body.bili-video-page .playlist-container--right .multip-list-item, body.bili-video-page .playlist-container--right .recommend-list-container { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-video-page .playlist-container--right img, body.bili-video-page .playlist-container--right video, body.bili-video-page .playlist-container--right picture { max-width: 100% !important; height: auto !important; } body.bili-video-page .playlist-container--right .action-list-header { display: flex !important; flex-wrap: wrap !important; align-items: center !important; gap: 8px !important; } body.bili-video-page .playlist-container--right .action-list-item .title, body.bili-video-page .playlist-container--right .multip-list-item .title, body.bili-video-page .playlist-container--right .list-display-name { white-space: normal !important; word-break: break-all !important; overflow-wrap: break-word !important; } /* ============================================================ 默认收藏夹 / 播放列表:内容在容器内"居中" —— 左右间距天然相等 思路:不再逐层算 padding(B站原生各层左右留白本就不等,容易一侧偏多), 改为「容器统一给一圈 gutter + 内部所有块级层 margin-inline: auto 居中」。 居中后,无论某一层残留多少单边留白,都会被均分到两侧,左右必然相等。 唯一可调变量:--fav-gutter(默认 8px),改一处、两侧同步。 滚动条按你的要求忽略(不额外预留 scrollbar-gutter)。 ============================================================ */ /* 1) 抽屉本体:左右内边距 12px -> 8px(与推荐卡片、UP 头像同一条起始线) */ body.bili-video-page #mirror-vdcon .playlist-container--right { --fav-gutter: 8px; padding-left: 8px !important; padding-right: 8px !important; } /* 2) 收藏夹容器:左右同为 --fav-gutter,这是容器内部唯一的留白来源 */ body.bili-video-page .playlist-container--right .action-list-container { padding-left: var(--fav-gutter, 8px) !important; padding-right: var(--fav-gutter, 8px) !important; margin-left: auto !important; margin-right: auto !important; } /* 3) 容器内所有块级层:清掉自身单边留白,改为左右 auto 居中 */ body.bili-video-page .playlist-container--right .action-list-header, body.bili-video-page .playlist-container--right .action-list-body, body.bili-video-page .playlist-container--right .action-list-body-top, body.bili-video-page .playlist-container--right .action-list-body-bottom, body.bili-video-page .playlist-container--right .action-list, body.bili-video-page .playlist-container--right .action-list-inner, body.bili-video-page .playlist-container--right .action-list-content, body.bili-video-page .playlist-container--right .action-list-item-wrap, body.bili-video-page .playlist-container--right .action-list-item, body.bili-video-page .playlist-container--right .singlep-list-item-inner, body.bili-video-page .playlist-container--right .multip-list-item-inner { padding-left: 0 !important; padding-right: 0 !important; margin-left: auto !important; margin-right: auto !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } /* 标题行 / 创建者行:内部元素必须按内容取宽(不能 width:100%), 否则会把右侧的 "^" 按钮和"收藏"按钮挤到下一行(实测 header 48px -> 72px)。 这里让它们恢复 auto,并由父行用 space-between 把按钮送回最右侧。 */ body.bili-video-page .playlist-container--right .header-left, body.bili-video-page .playlist-container--right .list-display-name, body.bili-video-page .playlist-container--right .fav-btn, body.bili-video-page .playlist-container--right .list-title, body.bili-video-page .playlist-container--right .list-playorder-btn, body.bili-video-page .playlist-container--right .list-playway-btn { width: auto !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; } /* 这两行强制单行:左文案 + 右按钮,不再换行 */ body.bili-video-page .playlist-container--right .action-list-header, body.bili-video-page .playlist-container--right .action-list-body-top { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: center !important; justify-content: space-between !important; gap: 8px !important; height: auto !important; min-height: 0 !important; } body.bili-video-page .playlist-container--right .header-left { flex: 1 1 auto !important; display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: center !important; gap: 6px !important; } /* 4) 列表滚动区:滚动条改为"零占位"模式 实测:action-list 274 -> action-list-inner 270,被滚动条吃掉 4px, 这 4px 只扣在右侧,正是"右边距比左边距大"的根源。 滚动条本来就不显示,这里直接让它不占宽度,左右两侧才可能真正相等。 (滚动手势与滚轮均不受影响) */ body.bili-video-page .playlist-container--right .action-list-body, body.bili-video-page .playlist-container--right .action-list-body-bottom, body.bili-video-page .playlist-container--right .action-list, body.bili-video-page .playlist-container--right .action-list-inner, body.bili-video-page .playlist-container--right .action-list-content { padding-left: 0 !important; padding-right: 0 !important; margin-left: auto !important; margin-right: auto !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-video-page .playlist-container--right .action-list { scrollbar-width: none !important; -ms-overflow-style: none !important; scrollbar-gutter: auto !important; overflow-y: auto !important; } body.bili-video-page .playlist-container--right .action-list::-webkit-scrollbar { width: 0 !important; height: 0 !important; display: none !important; background: transparent !important; } body.bili-video-page .playlist-container--right .action-list::-webkit-scrollbar-track, body.bili-video-page .playlist-container--right .action-list::-webkit-scrollbar-thumb { width: 0 !important; display: none !important; background: transparent !important; } /* 5) 列表项:三层内边距(wrap / item / inner)各 10px 全部归零并居中 */ body.bili-video-page .playlist-container--right .action-list-item-wrap, body.bili-video-page .playlist-container--right .action-list-item, body.bili-video-page .playlist-container--right .singlep-list-item-inner, body.bili-video-page .playlist-container--right .multip-list-item-inner { padding-left: 0 !important; padding-right: 0 !important; margin-left: auto !important; margin-right: auto !important; } /* 5) 列表项主体:flex 布局,封面固定 106x60,文字区吃掉剩余宽度,间距 10->8 */ body.bili-video-page .playlist-container--right .action-list-item .main, body.bili-video-page .playlist-container--right .multip-list-item .main, body.bili-video-page .playlist-container--right .singlep-list-item-inner .main, body.bili-video-page .playlist-container--right .multip-list-item-inner .main { display: flex !important; flex-direction: row !important; align-items: center !important; gap: 8px !important; width: 100% !important; max-width: 100% !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-video-page .playlist-container--right .action-list-item .main > .cover, body.bili-video-page .playlist-container--right .multip-list-item .main > .cover { flex: 0 0 106px !important; width: 106px !important; min-width: 106px !important; max-width: 106px !important; height: 60px !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-video-page .playlist-container--right .action-list-item .main > .cover .cover-img, body.bili-video-page .playlist-container--right .action-list-item .main > .cover img, body.bili-video-page .playlist-container--right .multip-list-item .main > .cover .cover-img, body.bili-video-page .playlist-container--right .multip-list-item .main > .cover img { width: 100% !important; height: 100% !important; object-fit: cover !important; border-radius: 4px !important; } body.bili-video-page .playlist-container--right .action-list-item .main > .info, body.bili-video-page .playlist-container--right .multip-list-item .main > .info { flex: 1 1 auto !important; width: auto !important; min-width: 0 !important; max-width: 100% !important; height: auto !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; overflow: visible !important; } /* 6) 标题:吃满文字区宽度(保留原生两行截断,只解决"过早换行") */ body.bili-video-page .playlist-container--right .action-list-item .info .title, body.bili-video-page .playlist-container--right .multip-list-item .info .title { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; white-space: normal !important; word-break: break-all !important; overflow-wrap: break-word !important; box-sizing: border-box !important; } /* 7) 多P / 分集列表("第X话 …… 40:33"): 「按键本身」居中(整块在容器内居中),「按键内文字」维持 B站官方排布 —— 即标题靠左、时长靠右(space-between),不再强行居中。 结构:.multip-list > .multip-list-item > (.left-part + .duration.right-part) 【重要】这里绝不能写 display: xxx !important: B 站"展开/收起选集"正是通过切换 .multip-list / .multip-list-item 的 display(none <-> block/flex)实现的。一旦用 !important 钉死 display, 收起时元素就藏不住 —— 表现为"箭头会转、列表却不动"。 所以本组只改「布局内部分配」类属性(justify-content / gap / flex 等), 它们在 display:none 时自动失效,不会干扰展开收起。 另外绝不能用 [class*="multip-list"] > * 这种写法—— "multip-list-item" 也含 "multip-list",会被一起选中, 导致 left-part / right-part 各被撑成 100% 宽并换行,文字顶出按钮。 */ /* 选集整块:按键本体居中(块级居中 + 左右 auto) */ body.bili-video-page .playlist-container--right .multip-list, .multip-list { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; margin-left: auto !important; margin-right: auto !important; box-sizing: border-box !important; } /* 每一行的按键本体:居中于容器;内部文字回到官方的「左标题 / 右时长」 */ body.bili-video-page .playlist-container--right .multip-list > .multip-list-item, .multip-list > .multip-list-item { flex-wrap: nowrap !important; align-items: center !important; justify-content: space-between !important; gap: 8px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; padding-left: 0 !important; padding-right: 0 !important; margin-left: auto !important; margin-right: auto !important; overflow: hidden !important; box-sizing: border-box !important; } /* 左段(第X话 标题):靠左;右段(时长):靠右 —— 官方排布 */ body.bili-video-page .playlist-container--right .multip-list-item > .left-part, body.bili-video-page .playlist-container--right .multip-list-item > .right-part, .multip-list-item > .left-part, .multip-list-item > .right-part { flex: 0 1 auto !important; width: auto !important; min-width: 0 !important; max-width: 100% !important; height: auto !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; white-space: nowrap !important; text-overflow: ellipsis !important; box-sizing: border-box !important; } body.bili-video-page .playlist-container--right .multip-list-item > .left-part, .multip-list-item > .left-part { text-align: left !important; margin-right: auto !important; } /* 时长段不参与收缩,保证 "40:33" 完整显示,并贴右 */ body.bili-video-page .playlist-container--right .multip-list-item > .right-part, .multip-list-item > .right-part { flex: 0 0 auto !important; text-align: right !important; margin-left: auto !important; } body.bili-video-page .playlist-container--right .multip-list-item .title, .multip-list-item .title { width: auto !important; max-width: 100% !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; white-space: nowrap !important; text-overflow: ellipsis !important; text-align: left !important; box-sizing: border-box !important; } /* 与视频页一致:隐藏弹幕框、私信、充电、广告 */ body.bili-video-page .playlist-container--right #danmukuBox, body.bili-video-page .playlist-container--right .danmaku-box, body.bili-video-page .playlist-container--right .send-msg, body.bili-video-page .playlist-container--right a.send-msg, body.bili-video-page .playlist-container--right .new-charge-btn, body.bili-video-page .playlist-container--right [class*="ad-"], body.bili-video-page .playlist-container--right [class*="ads-"], body.bili-video-page .playlist-container--right .video-card-ad-small { display: none !important; visibility: hidden !important; height: 0 !important; width: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* UP 主信息:与视频页 right-container 保持一致 */ body.bili-video-page .playlist-container--right .up-info-container { display: flex !important; align-items: center !important; width: 100% !important; gap: 8px !important; } body.bili-video-page .playlist-container--right .up-info--right { flex: 1 1 auto !important; min-width: 0 !important; width: auto !important; max-width: 100% !important; display: flex !important; flex-direction: column !important; } body.bili-video-page .playlist-container--right .up-info__detail, body.bili-video-page .playlist-container--right .up-detail, body.bili-video-page .playlist-container--right .up-description { width: 100% !important; max-width: 100% !important; min-width: 0 !important; word-break: break-all !important; white-space: normal !important; overflow-wrap: break-word !important; box-sizing: border-box !important; } body.bili-video-page .playlist-container--right .up-detail-top { display: flex !important; align-items: center !important; justify-content: flex-start !important; width: 100% !important; gap: 8px !important; } body.bili-video-page .playlist-container--right .up-name { white-space: nowrap !important; text-overflow: ellipsis !important; overflow: hidden !important; flex-shrink: 1 !important; min-width: 0 !important; max-width: 100% !important; } body.bili-video-page .playlist-container--right .follow-btn .menu-popover, body.bili-video-page .playlist-container--right .follow-btn-wrapper { position: absolute !important; top: 100% !important; right: 0 !important; left: auto !important; z-index: 9999999 !important; transform: none !important; margin-top: 4px !important; background: #fff !important; border-radius: 4px !important; box-shadow: 0 2px 8px rgba(0,0,0,0.15) !important; } /* 全屏时左栏铺满(与视频页 left-container 一致) */ body.video-fullscreen .playlist-container--left { width: 100vw !important; max-width: 100vw !important; min-width: 100vw !important; height: 100vh !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* ============================================================ ========== 番剧播放页 ========== ============================================================ */ html:has(body.bili-bangumi-page), body.bili-bangumi-page { background: #ffffff !important; width: 100% !important; max-width: 100vw !important; min-width: 0 !important; overflow-x: hidden !important; padding-top: 0 !important; margin: 0 !important; position: relative !important; } body.bili-bangumi-page #__next, body.bili-bangumi-page .home-container { width: 100% !important; max-width: 100vw !important; min-width: 0 !important; overflow-x: hidden !important; box-sizing: border-box !important; margin: 0 !important; padding: 0 !important; display: block !important; position: relative !important; } body.bili-bangumi-page .main-container, body.bili-bangumi-page .plp-layout, body.bili-bangumi-page .plp-left-wrap, body.bili-bangumi-page .plp-l, body.bili-bangumi-page .player-left-components { width: 100% !important; max-width: 100% !important; min-width: 0 !important; overflow-x: hidden !important; box-sizing: border-box !important; margin: 0 !important; padding: 0 !important; display: contents !important; position: static !important; left: auto !important; right: auto !important; float: none !important; } /* ★★★ 番剧页播放器顶部间距:36px;宽屏下收窄并居中 ★★★ */ body.bili-bangumi-page .plp-player { width: 100% !important; max-width: 100% !important; padding: 0 8px !important; margin: 36px auto !important; box-sizing: border-box !important; height: auto !important; min-height: 0 !important; } /* 番剧页正文容器(播放器 + 信息区)宽屏下居中显示 */ body.bili-bangumi-page .plp-layout, body.bili-bangumi-page .plp-left-wrap, body.bili-bangumi-page .plp-l { max-width: 820px !important; margin-left: auto !important; margin-right: auto !important; } body.bili-bangumi-page .plp-left-wrap { padding: 0 8px !important; } body.bili-bangumi-page [class*="mediainfo_mediaInfoWrap"] { margin-top: 0 !important; } /* ===== 番剧播放页:禁止点标题 / 简介跳转(v131.61) ===== 探针 09-29_11-25-18 实测(视口 360): .mediainfo_mediaRight__SDOq4[320x385] left=20 > **a.mediainfo_mediaTitle__Zyiqh[320x25]** ← 标题,是 > **a.mediainfo_media_desc_section__Vkt2t[320x276]** ← 简介,也是 > div.mediainfo_display_area__ggRQT > p.mediainfo_content__rexOq[320x270] 两者都是真链接(),手机上手指点标题或简介正文都会整页跳走, 而本站已经在播这一集,跳走纯属误触。 改法(纯 CSS,不动任何 JS): pointer-events: none —— 元素彻底不参与命中测试, 点击/长按/悬停一律落到父容器上,链接的默认跳转行为根本不会被触发。 比 JS 里 addEventListener('click', e => e.preventDefault()) 更干净: · 不依赖节点是否已渲染(CSS 对后插入的节点自动生效) · 不受站内 React 事件委托 / 冒泡顺序影响 · 不需要 MutationObserver 反复重挂 · pointer-events 是**可继承**属性,子节点一并失效, 简介里的

正文、

容器都不用逐个点名。 配套两点小修: · cursor: default —— 不再是"手型",视觉上不像链接; · text-decoration: none —— 去掉链接下划线(若有)。 ⚠ 刻意不改 color:标题/简介的颜色保持站内原样,只改交互不改外观。 ⚠ 文字选中不受影响:pointer-events:none 只拦指针事件, 长按拖选仍会命中父容器并在 DOM 上选中这段文字。 选择器用 [class*=] 前缀匹配 —— tailwind 式哈希后缀(__Zyiqh) 会随版本变,前缀匹配才不会被站内更新打断。---- */ body.bili-bangumi-page [class*="mediainfo_mediaTitle"], body.bili-bangumi-page [class*="mediainfo_media_desc_section"], body.bili-video-page [class*="mediainfo_mediaTitle"], body.bili-video-page [class*="mediainfo_media_desc_section"] { pointer-events: none !important; cursor: default !important; text-decoration: none !important; } /* 子节点显式再压一层:防站内给

/

单独写了 pointer-events:auto */ body.bili-bangumi-page [class*="mediainfo_media_desc_section"] *, body.bili-video-page [class*="mediainfo_media_desc_section"] * { pointer-events: none !important; } body.bili-bangumi-page #bilibili-player-wrap { position: relative !important; width: 100% !important; max-width: 100% !important; height: calc((100vw - 22px) * 9 / 16 + 6px) !important; max-height: calc((100vw - 22px) * 9 / 16 + 6px) !important; padding: 0 !important; border: 3px solid #00aeec !important; border-radius: 12px !important; box-shadow: 0 4px 16px rgba(0, 174, 236, 0.25) !important; overflow: hidden !important; box-sizing: border-box !important; margin: 0 auto !important; } body.bili-bangumi-page #bilibili-player-wrap .video_playerInner__ox52b, body.bili-bangumi-page #bilibili-player-wrap #bilibili-player, body.bili-bangumi-page #bilibili-player-wrap .big-nano-player, body.bili-bangumi-page #bilibili-player-wrap .bpx-docker, body.bili-bangumi-page #bilibili-player-wrap .bpx-player-container, body.bili-bangumi-page #bilibili-player-wrap .bpx-player-primary-area, body.bili-bangumi-page #bilibili-player-wrap .bpx-player-video-area, body.bili-bangumi-page #bilibili-player-wrap .bpx-player-video-wrap { position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; max-width: 100% !important; max-height: 100% !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; transform: none !important; border-radius: 0 !important; } body.bili-bangumi-page #bilibili-player-wrap video { position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; object-fit: contain !important; background-color: #000 !important; } body.bili-bangumi-page .bpx-player-sending-area { display: none !important; } /* ★ 修复简介完全展开后叠加到评论区的问题 ★ 根因:plp-l.sticky 原始为 position: sticky, 简介展开后高度溢出,sticky 定位导致内容叠在评论区上方。 同时 plp-left-wrap / plp-l 需高度自适应, player-left-components 为 display: contents 已不占位。 */ body.bili-bangumi-page .plp-left-wrap { padding: 0 8px !important; display: block !important; height: auto !important; min-height: auto !important; max-height: none !important; overflow: visible !important; position: static !important; } body.bili-bangumi-page .plp-l, body.bili-bangumi-page .plp-l.sticky { padding: 0 !important; display: block !important; position: static !important; top: auto !important; bottom: auto !important; height: auto !important; min-height: auto !important; max-height: none !important; overflow: visible !important; } body.bili-bangumi-page .player-left-components { padding: 0 !important; display: contents !important; } body.bili-bangumi-page .toolbar, body.bili-bangumi-page #like_info, body.bili-bangumi-page #ogv_weslie_tool_coin_info, body.bili-bangumi-page #ogv_weslie_tool_favorite_info, body.bili-bangumi-page #share-container-id, body.bili-bangumi-page #watch_together_tab, body.bili-bangumi-page #tool-bar-more, body.bili-bangumi-page .toolbar-right { display: none !important; visibility: hidden !important; height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-bangumi-page .mediainfo_mediaToolbar__8Q7bn { display: none !important; visibility: hidden !important; height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-bangumi-page .mediainfo_bottomBar__uBfeO { display: none !important; visibility: hidden !important; height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-bangumi-page [class*="navTools_floatNavExp"], body.bili-bangumi-page [class*="navTools_navMenu"] { display: none !important; visibility: hidden !important; height: 0 !important; width: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-bangumi-page [class*="mediainfo_mediaInfoWrap"] { display: flex !important; flex-direction: column !important; align-items: stretch !important; justify-content: flex-start !important; flex-wrap: nowrap !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 10px 12px !important; margin: 0 !important; box-sizing: border-box !important; height: auto !important; min-height: auto !important; max-height: none !important; overflow: visible !important; } /* 确保 mediaInfoWrap 的 flex 子元素正常排列,不被压缩 */ body.bili-bangumi-page [class*="mediainfo_mediaInfoWrap"] > * { flex: 0 0 auto !important; min-height: auto !important; max-height: none !important; } body.bili-bangumi-page [class*="mediainfo_mediaInfoWrap"] [class*="mediainfo_display_area"] { flex: 1 1 auto !important; } /* ★★★ 番剧页:隐藏封面图区域(图中红框部分) ★★★ 仅作用于 mediainfo_mediaCover 内部,避免误伤侧边栏推荐/选集封面 */ body.bili-bangumi-page [class*="mediainfo_mediaCover"], body.bili-bangumi-page a[class*="mediainfo_mediaCover"], body.bili-bangumi-page [class*="mediainfo_mediaCover"] picture, body.bili-bangumi-page [class*="mediainfo_mediaCover"] img, body.bili-bangumi-page [class*="mediainfo_mediaCover"] svg { display: none !important; visibility: hidden !important; width: 0 !important; max-width: 0 !important; min-width: 0 !important; height: 0 !important; max-height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; border: 0 !important; opacity: 0 !important; overflow: hidden !important; position: absolute !important; left: -9999px !important; top: -9999px !important; pointer-events: none !important; flex: 0 0 0 !important; } /* ★★★ v132.63:隐藏番剧页「承包榜」整块 ★★★ 探针实测(bili_probe_2026-10-02_16-33-15,番剧播放页 ep193549): #sponsor_module [344x257] top=324 bottom=581,位于简介区 (mediainfo_mediaInfoWrap)之下、评论区 #comment-module 之上, 内含 .sponsor_mini_rank(左半:标题「承包榜」+ 10 条榜单 li.rankList_sponsor_rank_item)+ .sponsor_info(右半: 「查看承包榜」按钮 + 「已有 1423 人承包」计数)。 选择器策略:内部类名全是 CSS Module 哈希(cx4yJ / cyO8d / BZrNc …)每次构建都会变,写不死;但 **#sponsor_module 这个 id 是稳定的**,故以 id 为主选择器(id 特异性 0,1,0,0 已是 最高一档,无需再加 body class)。 另配三条同义兜底(id 微调 / 类名换哈希都能命中): [id*="sponsor_module"]、[class*="sponsor_module"]、 以及 rank 系列 [class*="sponsor_rank"] / [class*="rankView_sponsor"] / [class*="rankList_sponsor"]。 ⚠ 刻意不写 [class*="sponsor"] 这种宽泛选择器:番剧页还有 「为TA充电」等模块,误伤会让页面缺一大块。 间距:隐藏后评论区会自然上移(原本 comment 顶 581 紧贴 sponsor 底,comment 自身 pt=12 提供间距),故不额外补 margin。 */ #sponsor_module, [id*="sponsor_module"], [class*="sponsor_module"], [class*="sponsor_sponsor"], [class*="sponsor_rank"], [class*="rankView_sponsor"], [class*="rankList_sponsor"] { display: none !important; visibility: hidden !important; width: 0 !important; max-width: 0 !important; min-width: 0 !important; height: 0 !important; max-height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; border: 0 !important; opacity: 0 !important; overflow: hidden !important; flex: 0 0 0 !important; pointer-events: none !important; } /* JS 兜底打上的标记(id / 类名都换了哈希时用文本识别) */ .bili-hide-sponsor { display: none !important; visibility: hidden !important; width: 0 !important; max-width: 0 !important; min-width: 0 !important; height: 0 !important; max-height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; border: 0 !important; opacity: 0 !important; overflow: hidden !important; pointer-events: none !important; } body.bili-bangumi-page [class*="mediainfo_mediaRight"] { width: 100% !important; max-width: 100% !important; min-width: 0 !important; display: flex !important; flex-direction: column !important; align-items: center !important; justify-content: flex-start !important; text-align: center !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; float: none !important; position: static !important; height: auto !important; min-height: auto !important; max-height: none !important; overflow: visible !important; } body.bili-bangumi-page [class*="mediainfo_mediaTitle"] { font-size: 16px !important; font-weight: bold !important; margin: 0 0 6px 0 !important; padding: 0 !important; text-align: center !important; width: 100% !important; display: block !important; height: auto !important; max-height: none !important; overflow: visible !important; -webkit-line-clamp: unset !important; word-break: break-word !important; } /* ★ 番剧简介:始终完全展开,隐藏"展开/收起"按钮 ★ B站原生机制:收起态 display_area 用 -webkit-line-clamp 限高, ellipsis 是末行渐变遮罩+"展开"按钮;展开态切换为纯 p 标签。 实测无论多少字,展开后也只多显示约一行,按钮意义不明。 方案:强制解除所有高度/行数限制,隐藏 ellipsis/placeholder/按钮, 让简介始终完整显示(纯 CSS,无 JS)。 两套 DOM 结构: 收起态 a[class*="mediainfo_media_desc_section"] -> div.display_area > div.placeholder(hidden) + p.content + div.ellipsis("展开") -> p.content_placeholder(hidden) 展开态 a[class*="mediainfo_media_desc"] -> p > span("简介:") + i("收起") */ body.bili-bangumi-page [class*="mediainfo_media_desc"] p, body.bili-bangumi-page [class*="mediainfo_media_desc"] p *, body.bili-bangumi-page [class*="mediainfo_display_area"], body.bili-bangumi-page [class*="mediainfo_display_area"] *, body.bili-bangumi-page [class*="mediainfo_content"], body.bili-bangumi-page [class*="mediainfo_content"] * { color: #61666d !important; font-size: 13px !important; font-weight: 400 !important; line-height: 1.6 !important; letter-spacing: normal !important; font-style: normal !important; text-transform: none !important; text-align: center !important; } body.bili-bangumi-page [class*="mediainfo_media_desc"] { width: 100% !important; max-width: 100% !important; display: block !important; padding: 6px 0 0 0 !important; margin: 0 !important; box-sizing: border-box !important; height: auto !important; min-height: auto !important; max-height: none !important; overflow: visible !important; -webkit-line-clamp: unset !important; -webkit-box-orient: unset !important; text-overflow: clip !important; white-space: normal !important; word-break: break-word !important; } body.bili-bangumi-page [class*="mediainfo_media_desc"] p { width: 100% !important; max-width: 100% !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; height: auto !important; min-height: auto !important; max-height: none !important; overflow: visible !important; -webkit-line-clamp: unset !important; -webkit-box-orient: unset !important; text-overflow: clip !important; white-space: pre-wrap !important; word-break: break-word !important; } /* ★ 关键:解除 display_area 的高度与行数限制,让收起态也能完整展开 ★ 同时强制 position: static,防止 B站原始 absolute 定位导致 简介脱离文档流、叠加到评论区上方 */ body.bili-bangumi-page [class*="mediainfo_display_area"] { position: static !important; top: auto !important; left: auto !important; right: auto !important; bottom: auto !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; display: block !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; height: auto !important; min-height: auto !important; max-height: none !important; overflow: visible !important; -webkit-line-clamp: unset !important; -webkit-box-orient: unset !important; text-overflow: clip !important; white-space: normal !important; word-break: break-word !important; float: none !important; z-index: auto !important; } body.bili-bangumi-page [class*="mediainfo_content"] { width: 100% !important; max-width: 100% !important; min-width: 0 !important; display: block !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; height: auto !important; min-height: auto !important; max-height: none !important; overflow: visible !important; -webkit-line-clamp: unset !important; -webkit-box-orient: unset !important; text-overflow: clip !important; white-space: pre-wrap !important; word-break: break-word !important; } /* ★ 隐藏"展开/收起"按钮、渐变遮罩、占位测量元素 ★ */ body.bili-bangumi-page [class*="mediainfo_ellipsis"], body.bili-bangumi-page [class*="mediainfo_placeholder"], body.bili-bangumi-page [class*="mediainfo_expand"], body.bili-bangumi-page [class*="mediainfo_expandBtn"], body.bili-bangumi-page [class*="mediainfo_moreBtn"], body.bili-bangumi-page [class*="mediainfo_toggleBtn"], body.bili-bangumi-page [class*="mediainfo_content_placeholder"] { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* ★ 隐藏展开态 p 里的 收起 按钮文字 ★ */ body.bili-bangumi-page [class*="mediainfo_media_desc"] p > i, body.bili-bangumi-page [class*="mediainfo_content"] i { display: none !important; visibility: hidden !important; } body.bili-bangumi-page [class*="mediainfo_mediaRating"] { display: inline-flex !important; justify-content: center !important; align-items: center !important; margin: 0 auto !important; } body.bili-bangumi-page [class*="upinfo_upInfoCard"] { display: inline-flex !important; justify-content: center !important; align-items: center !important; margin: 0 auto !important; } body.bili-bangumi-page #comment-module { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 12px 0 0 0 !important; margin: 0 !important; box-sizing: border-box !important; overflow: hidden !important; display: block !important; position: static !important; clear: both !important; } body.bili-bangumi-page #comment-module * { max-width: 100% !important; box-sizing: border-box !important; } body.bili-bangumi-page #comment-body { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 8px !important; margin: 0 !important; box-sizing: border-box !important; overflow: hidden !important; display: block !important; } body.bili-bangumi-page #comment-body bili-comments { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; overflow: hidden !important; position: static !important; } /* ============================================================ ========== 番剧页播放器控制栏 ========== ============================================================ */ body.bili-bangumi-page .bpx-player-ctrl-pip, body.bili-bangumi-page .bpx-player-ctrl-wide, body.bili-bangumi-page .bpx-player-ctrl-time, body.bili-bangumi-page .bpx-player-ctrl-eplist { display: none !important; } @media screen and (orientation: landscape) { body.bili-bangumi-page .bpx-player-ctrl-time { display: block !important; } } @media screen and (width >= 750px) { body.bili-bangumi-page .bpx-player-container[data-screen=full] .bpx-player-ctrl-quality-result { height: unset !important; font-size: 16px !important; } body.bili-bangumi-page .bpx-player-container[data-screen=full] .bpx-player-control-wrap { height: 43px !important; } body.bili-bangumi-page .bpx-player-container[data-screen=full] .bpx-player-control-top { bottom: 43px !important; } } body.bili-bangumi-page div.bpx-player-control-bottom { height: 29px !important; margin-top: 7px !important; padding: 0 7px !important; } body.bili-bangumi-page div.bpx-player-control-top { transition: none; bottom: 36px; } body.bili-bangumi-page .bpx-player-pbp .bpx-player-pbp-pin-tip { display: none !important; } body.bili-bangumi-page .bpx-player-control-bottom-left, body.bili-bangumi-page .bpx-player-control-bottom-right { flex: unset !important; } body.bili-bangumi-page .bpx-player-container .bpx-player-control-bottom-left, body.bili-bangumi-page .bpx-player-container .bpx-player-control-bottom-right { min-width: 0 !important; } body.bili-bangumi-page .bpx-player-ctrl-quality { min-width: 0; flex: auto !important; margin-right: 0 !important; } body.bili-bangumi-page .bpx-player-ctrl-quality-result, body.bili-bangumi-page .bpx-player-ctrl-playbackrate { font-size: 12px !important; } body.bili-bangumi-page .bpx-player-ctrl-quality-result { height: 22px; overflow: hidden; } body.bili-bangumi-page .bpx-player-ctrl-playbackrate { text-wrap: nowrap; } body.bili-bangumi-page .bpx-player-progress-wrap { height: 7px !important; padding-bottom: 3px !important; } body.bili-bangumi-page .bpx-player-control-mask { background: linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,0.5) 100%) !important; } body.bili-bangumi-page .bpx-player-ctrl-quality-menu-wrap { bottom: 22px !important; } body.bili-bangumi-page .bpx-player-ctrl-quality-menu-item { max-width: 95px; max-height: 36px; height: 7.7vw !important; padding: 0 8px 0 12px !important; } body.bili-bangumi-page .bpx-player-ctrl-quality-badge-bigvip { color: #fff; background-color: #f25d8e; width: 16px; overflow: hidden; right: 8px !important; } body.bili-bangumi-page .bpx-player-ctrl-quality-badge-bigvip:before { color: #fff; content: "V"; background-color: #f25d8e; padding: 0 4px; position: absolute; left: 0; } body.bili-bangumi-page .bpx-player-ctrl-playbackrate-menu { bottom: 22px !important; } body.bili-bangumi-page .bpx-player-ctrl-playbackrate-menu-item { justify-content: center; align-items: center; max-height: 36px; display: flex; height: 7.7vw !important; } body.bili-bangumi-page div.bpx-player-ctrl-subtitle-box { bottom: 0; right: 0; transform: scale(.8); } body.bili-bangumi-page .bpx-player-ctrl-setting-box { bottom: 0 !important; right: 0 !important; } body.bili-bangumi-page .bpx-player-ctrl-setting-menu-right { padding: 5px !important; } body.bili-bangumi-page .bpx-player-ctrl-setting-menu-right > div { justify-content: center; align-items: center; max-height: 40px; display: flex; height: 10vw !important; } body.bili-bangumi-page .bpx-player-ctrl-setting-menu-right .bui-radio { width: 77%; margin: 0 0 8px 7px; } body.bili-bangumi-page .bpx-player-ctrl-setting-others-content { margin-left: 7px; width: 77% !important; } body.bili-bangumi-page .bpx-player-ctrl-setting-highenergy .bui-checkbox-name { white-space: nowrap; width: 48px; overflow: hidden; } body.bili-bangumi-page .bpx-player-dm-setting-wrap { top: 0; left: 50%; transform: translate(-50%); bottom: unset !important; position: fixed !important; } body.bili-bangumi-page .bpx-player-control-bottom-center .bpx-player-sending-bar { height: 24px !important; padding-right: 6px !important; } body.bili-bangumi-page .bpx-player-ctrl-viewpoint { flex-shrink: 1 !important; width: 45px !important; min-width: 0 !important; margin: 0 !important; } body.bili-bangumi-page .bpx-player-ctrl-viewpoint-text { flex: none; font-size: 12px; width: 24px !important; text-overflow: unset !important; } body.bili-bangumi-page .bpx-player-control-wrap:not(.new) { display: none; } body.bili-bangumi-page .bpx-player-control-entity, body.bili-bangumi-page .bpx-player-control-mask { display: block !important; } body.bili-bangumi-page .bpx-player-container[data-ctrl-hidden=true] .bpx-player-control-bottom { display: none; } body.bili-bangumi-page .bpx-player-container[ctrl-shown=false] .bpx-player-control-wrap .bpx-player-control-mask { opacity: 0; transition: opacity .2s ease-in; } body.bili-bangumi-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-wrap .bpx-player-control-mask { opacity: 1; } body.bili-bangumi-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-entity .bpx-player-control-bottom { opacity: 1; display: flex; } body.bili-bangumi-page .bpx-player-container[ctrl-shown=false] .bpx-player-control-entity .bpx-player-control-bottom { display: none; } body.bili-bangumi-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-entity .bpx-player-control-top, body.bili-bangumi-page .bpx-player-container[ctrl-shown=false] .bpx-player-control-entity .bpx-player-shadow-progress-area { opacity: 1; visibility: visible; } body.bili-bangumi-page .bpx-player-container[ctrl-shown=false] .bpx-player-control-entity .bpx-player-control-top, body.bili-bangumi-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-entity .bpx-player-shadow-progress-area { opacity: 0; visibility: hidden; } body.bili-bangumi-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-entity .bpx-player-pbp { opacity: 1; width: 100%; bottom: calc(100% + 6px); left: 0; } body.bili-bangumi-page div.bpx-player-control-entity .bpx-player-pbp { opacity: 0; width: calc(100% + 24px); bottom: 1px; left: -12px; } body.bili-bangumi-page div.bpx-player-control-entity .bpx-player-pbp.pin { opacity: 1; } body.bili-bangumi-page .bpx-player-pbp-pin { display: none; opacity: 1 !important; } body.bili-bangumi-page .bpx-player-container[ctrl-shown=true] .bpx-player-control-entity .bpx-player-pbp-pin { display: block; } body.bili-bangumi-page .bpx-player-video-perch { max-height: 0; } body.bili-bangumi-page .bpx-player-top-wrap, body.bili-bangumi-page .bpx-player-state-wrap { display: none !important; } body.bili-bangumi-page .bpx-player-container, body.bili-bangumi-page #bilibili-player-placeholder { box-shadow: none !important; } /* ===== 动态页 ===== */ body.bili-dynamic-page { background: #f4f5f7 !important; } body.bili-dynamic-page .bili-header, body.bili-dynamic-page .international-header, body.bili-dynamic-page .mini-header, body.bili-dynamic-page .bili-header__bar, body.bili-dynamic-page .bili-header__menu, body.bili-dynamic-page #bili-header-container { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; } body.bili-dynamic-page .bgc, body.bili-dynamic-page .bg { display: none !important; } body.bili-dynamic-page .bili-dyn-home--member > .left, body.bili-dynamic-page .bili-dyn-home--member > aside.left, body.bili-dynamic-page .bili-dyn-home--member > .right, body.bili-dynamic-page .bili-dyn-home--member > aside.right, body.bili-dynamic-page .bili-dyn-sidebar, body.bili-dynamic-page .bili-dyn-sidebar__btn { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; overflow: hidden !important; } /* ★★★ v132.46:未登录(游客)态动态页右侧漏出的「蓝色竖条」 ★★★ 现象:via 以外的浏览器打开动态页,屏幕右上角竖着一条约 18px 宽的 蓝条,从上一直拖到底,占掉一竖行。 探针实测(bili_probe_2026-10-02_09-53-50): #app > div.bili-dyn-home--visitor > aside.right 246x1796 / left=342 —— 视口只有 360 宽,它从 342 一直排到 588, body 的 overflow-x:hidden 把 360 以右裁掉,于是只剩下 342~360 这 18px 露在屏幕上,正好是肉眼看到的"一竖行蓝条"; 条里的颜色来自它内部的 .bili-dyn-banner 广告图(246x143)。 真因:上面那组隐藏左右栏的选择器只写了 .bili-dyn-home--member (登录态),未登录时根节点类名是 .bili-dyn-home--visitor, 整组规则一条都没命中 —— 所以只有"没登录 + 非 via"才会看到。 现在用 [class*=bili-dyn-home] 把两种状态一次覆盖,left / right 一并清零;再补一条不带父级限定的兜底,防站内改类名再度漏出。 */ body.bili-dynamic-page [class*="bili-dyn-home"] > .left, body.bili-dynamic-page [class*="bili-dyn-home"] > aside.left, body.bili-dynamic-page [class*="bili-dyn-home"] > .right, body.bili-dynamic-page [class*="bili-dyn-home"] > aside.right, body.bili-dynamic-page .bili-dyn-home--visitor > .left, body.bili-dynamic-page .bili-dyn-home--visitor > aside.left, body.bili-dynamic-page .bili-dyn-home--visitor > .right, body.bili-dynamic-page .bili-dyn-home--visitor > aside.right, body.bili-dynamic-page aside.left, body.bili-dynamic-page aside.right { display: none !important; visibility: hidden !important; width: 0 !important; max-width: 0 !important; min-width: 0 !important; height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-dynamic-page #app, body.bili-dynamic-page #app > div, body.bili-dynamic-page .bili-dyn-home--member, body.bili-dynamic-page .bili-dyn-home--visitor, body.bili-dynamic-page [class*="bili-dyn-home"], body.bili-dynamic-page [class*="bili-dyn-home"] > main, body.bili-dynamic-page .bili-dyn-home--member > main, body.bili-dynamic-page .bili-dyn-home--visitor > main, body.bili-dynamic-page .bili-dyn-list, body.bili-dynamic-page .bili-dyn-list__item, body.bili-dynamic-page .bili-dyn-item { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; float: none !important; } /* v132.46:侧栏清零后,主列仍被父级的 flex/grid 挤到 left=12、只剩 318 宽 —— 父容器退回块流、gap 归零,main 才能真正吃到 360 满宽。 */ body.bili-dynamic-page [class*="bili-dyn-home"] { display: block !important; gap: 0 !important; column-gap: 0 !important; row-gap: 0 !important; } body.bili-dynamic-page [class*="bili-dyn-home"] > main { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-list__items { padding: 8px 0 8px 0 !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-list__item { padding: 0 8px 8px 8px !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-item { position: relative !important; background: #ffffff !important; border: 1px solid #e3e5e7 !important; border-radius: 8px !important; padding: 12px 8px 0 8px !important; margin: 0 !important; box-sizing: border-box !important; overflow: hidden !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; display: block !important; float: none !important; } body.bili-dynamic-page .bili-dyn-item * { max-width: 100% !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-item img, body.bili-dynamic-page .bili-dyn-item video, body.bili-dynamic-page .bili-dyn-item picture { max-width: 100% !important; height: auto !important; } body.bili-dynamic-page .bili-dyn-item__main { position: relative !important; padding-left: 0 !important; padding-right: 0 !important; box-sizing: border-box !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; float: none !important; } body.bili-dynamic-page .bili-dyn-item__avatar { position: absolute !important; left: 0 !important; top: 0 !important; width: 56px !important; max-width: 56px !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-item__avatar .bili-dyn-avatar, body.bili-dynamic-page .bili-dyn-item__avatar .b-avatar, body.bili-dynamic-page .bili-dyn-item__avatar img { width: 48px !important; height: 48px !important; max-width: 100% !important; border-radius: 50% !important; } body.bili-dynamic-page .bili-dyn-item__header { margin-left: 0 !important; margin-right: 0 !important; padding-left: 64px !important; padding-right: 8px !important; width: auto !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-item__more, body.bili-dynamic-page .bili-dyn-more, body.bili-dynamic-page .bili-dyn-more__btn, body.bili-dynamic-page .bili-dyn-more__cascader { display: none !important; visibility: hidden !important; } body.bili-dynamic-page .bili-dyn-content, body.bili-dynamic-page .bili-dyn-content__orig, body.bili-dynamic-page .bili-dyn-content__orig__major { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-card-video { display: flex !important; flex-direction: column !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-card-video__header { width: 100% !important; max-width: 100% !important; height: auto !important; flex: 0 0 auto !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-card-video__cover { width: 100% !important; max-width: 100% !important; height: auto !important; aspect-ratio: 16 / 9 !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-card-video__cover .b-img, body.bili-dynamic-page .bili-dyn-card-video__cover .b-img__inner, body.bili-dynamic-page .bili-dyn-card-video__cover img, body.bili-dynamic-page .bili-dyn-card-video__cover picture { width: 100% !important; max-width: 100% !important; height: auto !important; aspect-ratio: 16 / 9 !important; object-fit: cover !important; display: block !important; } body.bili-dynamic-page .bili-dyn-card-video__body { width: 100% !important; max-width: 100% !important; height: auto !important; flex: 1 1 auto !important; padding: 8px 0 0 0 !important; box-sizing: border-box !important; } /* 投稿了视频:隐藏视频封面下方「框住标题/简介文字」的那条左侧半开口边框 */ body.bili-dynamic-page .bili-dyn-card-video__body, body.bili-dynamic-page .bili-dyn-card-video__title, body.bili-dynamic-page .bili-dyn-card-video__desc { border: none !important; border-top: none !important; border-right: none !important; border-bottom: none !important; border-left: none !important; border-width: 0 !important; border-style: none !important; box-shadow: none !important; outline: none !important; } /* 该边框也可能是 ::before / ::after 画的竖条,一并清掉 */ body.bili-dynamic-page .bili-dyn-card-video__body::before, body.bili-dynamic-page .bili-dyn-card-video__body::after, body.bili-dynamic-page .bili-dyn-card-video__desc::before, body.bili-dynamic-page .bili-dyn-card-video__desc::after { content: none !important; display: none !important; border: none !important; background: none !important; box-shadow: none !important; } body.bili-dynamic-page .bili-dyn-card-video__title, body.bili-dynamic-page .bili-dyn-card-video__desc, body.bili-dynamic-page .bili-dyn-card-video__title .bili-ellipsis, body.bili-dynamic-page .bili-dyn-card-video__desc .bili-ellipsis { text-align: center !important; } body.bili-dynamic-page .bili-dyn-card-video__stat, body.bili-dynamic-page .bili-dyn-card-video__stat__item, body.bili-dynamic-page .bili-dyn-card-video__stat__item.danmaku { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; } /* ===== v132.78:动态页「直播了」卡片 —— 与空间页同一套改法, 规则体与上面空间页版本逐项一致,只是 body class 换成 bili-dynamic-page。 说明与几何依据见空间页那一组。 */ body.bili-dynamic-page .bili-dyn-card-live, body.bili-dynamic-page a.bili-dyn-card-live { display: flex !important; flex-direction: column !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-card-live__header { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; flex: 0 0 auto !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-card-live__cover { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; aspect-ratio: 16 / 9 !important; box-sizing: border-box !important; } body.bili-dynamic-page .bili-dyn-card-live__cover .b-img, body.bili-dynamic-page .bili-dyn-card-live__cover .b-img__inner, body.bili-dynamic-page .bili-dyn-card-live__cover img, body.bili-dynamic-page .bili-dyn-card-live__cover picture { width: 100% !important; max-width: 100% !important; height: auto !important; aspect-ratio: 16 / 9 !important; object-fit: cover !important; display: block !important; } body.bili-dynamic-page .bili-dyn-card-live__body { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; flex: 1 1 auto !important; padding: 8px 0 0 0 !important; box-sizing: border-box !important; } /* 与投稿卡一致:简介与标题之间留 6px(投稿卡 __desc 的 mt 就是 6) */ body.bili-dynamic-page .bili-dyn-card-live__desc { margin-top: 6px !important; } /* 与投稿卡一致:清掉可能框住文字的边框(含可能画成伪元素的竖条) */ body.bili-dynamic-page .bili-dyn-card-live__body, body.bili-dynamic-page .bili-dyn-card-live__title, body.bili-dynamic-page .bili-dyn-card-live__desc { border: none !important; border-top: none !important; border-right: none !important; border-bottom: none !important; border-left: none !important; border-width: 0 !important; border-style: none !important; box-shadow: none !important; outline: none !important; text-align: center !important; } body.bili-dynamic-page .bili-dyn-card-live__body::before, body.bili-dynamic-page .bili-dyn-card-live__body::after, body.bili-dynamic-page .bili-dyn-card-live__desc::before, body.bili-dynamic-page .bili-dyn-card-live__desc::after { content: none !important; display: none !important; border: none !important; background: none !important; box-shadow: none !important; } /* ===== v132.79:动态页动态卡片四角圆角 —— 与空间页同一套改法, 规则体逐项一致,只换 body class。说明与几何依据见空间页那一组。 */ body.bili-dynamic-page .bili-dyn-card-live, body.bili-dynamic-page .bili-dyn-card-video { border-radius: 8px !important; /* ← 圆角大小(1/3) */ overflow: hidden !important; position: relative !important; } body.bili-dynamic-page .bili-dyn-card-live__header, body.bili-dynamic-page .bili-dyn-card-live__cover, body.bili-dynamic-page .bili-dyn-card-live__cover .b-img, body.bili-dynamic-page .bili-dyn-card-live__cover .b-img__inner, body.bili-dynamic-page .bili-dyn-card-live__cover img, body.bili-dynamic-page .bili-dyn-card-live__cover picture, body.bili-dynamic-page .bili-dyn-card-video__header, body.bili-dynamic-page .bili-dyn-card-video__cover, body.bili-dynamic-page .bili-dyn-card-video__cover .b-img, body.bili-dynamic-page .bili-dyn-card-video__cover .b-img__inner, body.bili-dynamic-page .bili-dyn-card-video__cover img, body.bili-dynamic-page .bili-dyn-card-video__cover picture { border-radius: 8px 8px 0 0 !important; /* ← 圆角大小(2/3) */ } body.bili-dynamic-page .bili-dyn-card-live__body, body.bili-dynamic-page .bili-dyn-card-video__body { border-radius: 0 0 8px 8px !important; /* ← 圆角大小(3/3) */ } body.bili-dynamic-page .bili-dyn-item, body.bili-dynamic-page .bili-dyn-item * { text-align: center !important; } /* 正文容器:只做内边距约束,绝不能隐藏(正文 .bili-dyn-content 就在它里面) */ body.bili-dynamic-page .bili-dyn-item__body { margin-left: 0 !important; margin-right: 0 !important; padding-left: 8px !important; padding-right: 8px !important; width: 100% !important; max-width: 100% !important; box-sizing: border-box !important; } /* 去掉卡片内的「转发 / 评论 / 点赞」数量行(整行隐藏) */ body.bili-dynamic-page .bili-dyn-item__footer, body.bili-dynamic-page .bili-dyn-item__action, body.bili-dynamic-page .bili-dyn-action, body.bili-dynamic-page .bili-dyn-action.forward, body.bili-dynamic-page .bili-dyn-action.comment, body.bili-dynamic-page .bili-dyn-action.like { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* 隐藏该行后,卡片底部补回一点留白,避免内容紧贴卡片下边缘 */ body.bili-dynamic-page .bili-dyn-item { padding-bottom: 12px !important; } body.bili-dynamic-page .bili-dyn-content__orig__major img, body.bili-dynamic-page .bili-dyn-content__orig__major picture, body.bili-dynamic-page .bili-dyn-content__orig__major .b-img { margin-left: auto !important; margin-right: auto !important; display: block !important; } body.bili-dynamic-page .bili-dyn-content__orig__major [class*="grid"], body.bili-dynamic-page .bili-dyn-content__orig__major [class*="pics"], body.bili-dynamic-page .bili-dyn-content__orig__major [class*="gallery"], body.bili-dynamic-page .bili-dyn-content__orig__major [class*="goods"], body.bili-dynamic-page .bili-dyn-content__orig__major [class*="commodity"], body.bili-dynamic-page .bili-dyn-content__orig__major [class*="card"] { margin-left: auto !important; margin-right: auto !important; justify-content: center !important; } body.bili-dynamic-page .bili-dyn-item__action { flex: 1 1 0 !important; width: auto !important; max-width: none !important; min-width: 0 !important; box-sizing: border-box !important; display: flex !important; align-items: center !important; justify-content: center !important; } body.bili-dynamic-page .bili-dyn-action { display: flex !important; align-items: center !important; justify-content: center !important; width: 100% !important; gap: 4px !important; } body.bili-dynamic-page .bili-dyn-list-tabs, body.bili-dynamic-page .bili-dyn-list-tabs__list, body.bili-dynamic-page .bili-dyn-list-tabs__item { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; } body.bili-dynamic-page .bili-dyn-home--member > main > section:nth-of-type(1), body.bili-dynamic-page .bili-dyn-home--member > main > section:nth-of-type(2) { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; } body.bili-dynamic-page .bili-dyn-search-trendings, body.bili-dynamic-page .trending-list, body.bili-dynamic-page .sticky { display: none !important; } /* ============================================================ 动态详情页(t.bilibili.com/xxx 单条动态展开):评论区与视频页一致 结构:#app > .content > .card > .bili-tabs.dyn-tabs > .bili-tabs__content > .bili-tab-pane > .comment-wrap > .bili-comment-container > bili-comments(shadow DOM) 实测:bili-comments 仅 312 宽(360 屏),左右各 24px;视频页为 8px。 这里按视频页 #commentapp 的同一套外框规则拉齐, shadow 内部(输入框/点赞/回复按钮等)由 installShadowRootPatch 统一处理。 ============================================================ */ body.bili-dynamic-page .bili-tabs.dyn-tabs, body.bili-dynamic-page .bili-tabs__content, body.bili-dynamic-page .bili-tab-pane, body.bili-dynamic-page .comment-wrap, body.bili-dynamic-page .bili-comment-container { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; padding-left: 8px !important; padding-right: 8px !important; margin-left: 0 !important; margin-right: 0 !important; overflow: hidden !important; float: none !important; position: static !important; } body.bili-dynamic-page bili-comments, body.bili-dynamic-page .bili-comment-container bili-comments { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; display: block !important; } /* 与视频页一致:外部的输入框/编辑器容器一并隐藏(shadow 内由 patch 处理) */ body.bili-dynamic-page .bili-comment-box, body.bili-dynamic-page .bili-comment-input-wrap, body.bili-dynamic-page .comment-send-box, body.bili-dynamic-page .bili-comment-editor, body.bili-dynamic-page .bili-comment-editor-wrap { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* 「评论 N」下面的那条分割线:恢复显示(v131.10) 不再清空 tabs 头部/导航的下边框与伪元素, 并显式补一条 1px 灰线,原生无边框时也能保证有线。 */ body.bili-dynamic-page .bili-tabs.dyn-tabs > .bili-tabs__header, body.bili-dynamic-page .bili-tabs.dyn-tabs .bili-tabs__header { border-top: 0 !important; border-left: 0 !important; border-right: 0 !important; border-bottom: 1px solid #e3e5e7 !important; box-shadow: none !important; background-image: none !important; } /* 若出现两条线(原生伪元素 + 上面补的边框), 把下面这段的 border-bottom 改回 none 即可 */ body.bili-dynamic-page .bili-tabs__nav, body.bili-dynamic-page .bili-tabs__nav__items, body.bili-dynamic-page .bili-tabs__nav__item { box-shadow: none !important; background-image: none !important; } /* 只隐藏「赞与转发」这一个 tab 项,保留「评论 N」—— 视频页评论区本来就有「评论 N」(shadow 内 #title),两边才一致。 探针:nav__items 中 nth-of-type(2) 为「评论 N」(.is-active), nth-of-type(3) 为「赞与转发」。 */ body.bili-dynamic-page .bili-tabs__nav__item:nth-of-type(3), body.bili-dynamic-page .bili-tabs__nav__items > .bili-tabs__nav__item:nth-of-type(3) { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* 右侧悬浮工具栏(417 / 0 / 7 / 顶部):视频页没有,隐藏 */ body.bili-dynamic-page .sidebar-wrap, body.bili-dynamic-page .side-toolbar, body.bili-dynamic-page .side-toolbar__box, body.bili-dynamic-page .side-toolbar__bottom { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* ============================================================ ===== 话题页(点标签/话题进来:/v/topic/detail/?topic_id=)===== 卡片 DOM 与动态页同源:.list__topic-card > .bili-dyn-item, 内部同样是 __avatar / __header / __body / a.bili-dyn-card-video, 所以版式照抄动态页: 白底圆角卡 + 头像左浮 48px + 封面拉全宽 16:9 + 正文居中 + 隐藏「关注 / 更多 / 热评 / 转发评论点赞 / 播放弹幕数」。 作用域全走 body:has(.topic-detail):纯 CSS,不新增 JS、不加页面 class。 ⚠ 踩过的坑一(v131.64 修复):本页列表是 B站 list-view 虚拟滚动, 首屏数据由底部哨兵 .list-view__shim 进视口触发加载。 上一版把 shim 给 display:none 了 → 永远不触发 → 一条都不显示 (实测 .topic-list 只剩 switch-tab 的 40px,flow-list 不在 DOM)。 所以:虚拟列表内部节点(shim / state / flow-list > div)一律不碰, 并显式保 shim 可见,防止被别处规则误伤。 ⚠ 踩过的坑二(v131.66 修复):「热门/最新」行的间距不是它自己给的。 上间距来自 .topic-detail__header 的 margin-bottom, 而中间夹了一张 .active-card(margin-bottom 16px)把它顶成 16px, 所以必须把活动卡隐藏,上间距才会回到 8px。 ⚠ 踩过的坑三(v131.65 修复):absolute 定位的 .bili-dyn-item__avatar 包含块是 .bili-dyn-item__main,不是 .bili-dyn-item。__main 已经是 item 的内容盒(padding 已算进去),所以只能 left/top = 0, 照抄动态页参数即可,不要再加 8/12 的内边距补偿。 ============================================================ */ body:has(.topic-detail) { padding: 8px 0 0 0 !important; background: #f4f5f7 !important; } /* 只收横向溢出,放在 .topic-detail 上;不动 html/body 的 overflow, 避免改变滚动容器让虚拟列表的滚动监听失效 */ body:has(.topic-detail) .topic-detail { overflow-x: hidden !important; background: #f4f5f7 !important; } /* ---- 1. 把 PC 定宽 632 / left:-140 的容器链拉回满宽 ---- 注意:不包含 .topic-list__flow-list 及其子级,避免误伤虚拟列表 */ body:has(.topic-detail) #app, body:has(.topic-detail) .topic-detail, body:has(.topic-detail) .topic-nav__main, body:has(.topic-detail) .topic-detail__header, body:has(.topic-detail) .topic-detail__content, body:has(.topic-detail) .topic-detail__header > .main-part, body:has(.topic-detail) .topic-detail__content > .main-part, body:has(.topic-detail) .topic-list, body:has(.topic-detail) .topic-list__flow-list, body:has(.topic-detail) .list__topic-card, body:has(.topic-detail) .bili-dyn-item, body:has(.topic-detail) .bili-dyn-item__main { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin-left: 0 !important; margin-right: 0 !important; padding-left: 0 !important; padding-right: 0 !important; box-sizing: border-box !important; float: none !important; } /* 整页左右各留 8px:与动态页 .bili-dyn-list__item 的 8px 边距一致 */ body:has(.topic-detail) .topic-detail { padding: 0 8px 8px 8px !important; box-sizing: border-box !important; } /* ---- 1b. flow-list 原生带 80px margin-top(PC 留白)。 不归零的话会与 switch-tab 的下间距 margin-collapse 成 80px ---- */ body:has(.topic-detail) .topic-list__flow-list { margin-top: 0 !important; margin-bottom: 0 !important; } /* ---- 1d. v132.32:「热门 / 最新」—— 文字上下距卡片各 8px ---- 探针「热门_最新按键上下间距改为8px…」实测: .topic-detail__header [344x137] top=8 bottom=145 mb=8 .switch-tab [344x48] top=153 bottom=201 mb=8 ★ 48 高 ├─ .switch-tab__tab [56x48] top=153 bottom=201 │ └─ a.switch-tab__tab-text [56x48] ← 撑满 48 .topic-list__flow-list top=209 外边距其实**已经是 8px**: 上方 145 → 153 = 8 (header 的 mb) 下方 201 → 209 = 8 (switch-tab 的 mb) 真正的问题是这行**自己高 48px**,而文字只有约 22px, 被上下各约 13px 的空白顶在中间 —— 于是视觉上 文字顶 166 → 距 header(145) = 21px 文字底 188 → 距 flow-list(209) = 21px 这就是"按键上下间距不是 8px"的来源(按键本身也存在上下间距)。 修法:让这一行**收缩到文字高度**(去掉 height / 上下 padding / line-height 撑高),外边距仍各 8px。改完: 文字顶 153 → 距 header 145 = 8 ✓ 文字底 ≈175 → 下方卡片 183 = 8 ✓ 两个 tab 原本间距 24px(112+56=168 → 192),用 gap 原样保留, 并把各自 margin 归零,避免与 gap 叠加。 ---- */ body:has(.topic-detail) .switch-tab { display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: center !important; gap: 24px !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 0 8px 0 !important; padding: 0 !important; box-sizing: border-box !important; } body:has(.topic-detail) .switch-tab__tab, body:has(.topic-detail) .switch-tab__tab-text { display: inline-flex !important; align-items: center !important; justify-content: center !important; height: auto !important; min-height: 0 !important; max-height: none !important; line-height: normal !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* ---- 1c. 保命:虚拟滚动的底部哨兵必须可见可布局 ---- */ body:has(.topic-detail) .list-view__shim, body:has(.topic-detail) .topic-list__flow-list > .list-view__shim { display: block !important; visibility: visible !important; width: 100% !important; min-height: 1px !important; height: auto !important; opacity: 0 !important; } /* ---- 2. 只隐藏纯装饰:顶部 PC 导航条(实测 0 高,含重复「参与话题」) 与分享弹层。发布面板 / list-view 内部节点一律不碰。 ---- */ body:has(.topic-detail) .topic-nav, body:has(.topic-detail) .share-popover, /* 活动运营卡(「崩坏星穹铁道4.6创作者激励计划 … 立即参与」): 实测 344x59、margin-bottom 16px,正是它把「热门/最新」行顶下去的元凶 */ body:has(.topic-detail) .active-card, body:has(.topic-detail) .active-content, body:has(.topic-detail) .active-button, /* 「参与话题 / 点赞 / 收藏 / 分享」整行:动态页没有,隐藏 */ body:has(.topic-detail) .interaction-container { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* ---- 3. 话题头部(发起者 / 标题 / 简介 / 参与话题):白卡 + 居中 ---- */ body:has(.topic-detail) .topic-detail__header { height: auto !important; background: #ffffff !important; border: 1px solid #e3e5e7 !important; border-radius: 8px !important; margin: 0 0 8px 0 !important; padding: 12px !important; box-sizing: border-box !important; text-align: center !important; } /* 原生 main-part 带 28px margin-top(给被隐藏的 topic-nav 留位),归零 */ body:has(.topic-detail) .topic-detail__header > .main-part { margin-top: 0 !important; padding-top: 0 !important; } body:has(.topic-detail) .topic-detail__header * { text-align: center !important; } /* 头里那个 88x93 的大电视图标:PC 装饰,隐藏 */ body:has(.topic-detail) .topic-tv-icon { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } body:has(.topic-detail) .launch-user, body:has(.topic-detail) .topic-title, body:has(.topic-detail) .topic-desc { width: 100% !important; max-width: 100% !important; margin-left: auto !important; margin-right: auto !important; justify-content: center !important; box-sizing: border-box !important; } body:has(.topic-detail) .topic-title h1 { font-size: 17px !important; line-height: 24px !important; } body:has(.topic-detail) .topic-desc__text, body:has(.topic-detail) .topic-desc { font-size: 13px !important; line-height: 20px !important; } /* ---- v132.64:话题简介默认展开,并隐藏「展开 / 收起」按键 ---- 高度限制解绑:容器原本被钉成 53(见下方变更记录 v132.64), 第三行只露出一半;height:auto 让它随文字自然长高。 display 必须改回 block —— -webkit-line-clamp 只在 display:-webkit-box 下才生效,光清 clamp 不够。 */ body:has(.topic-detail) .topic-desc, body:has(.topic-detail) .topic-desc__text { display: block !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; -webkit-line-clamp: unset !important; line-clamp: unset !important; -webkit-box-orient: horizontal !important; text-overflow: clip !important; white-space: normal !important; } /* 文字本体同样解 clamp(展开态容器 80 而 span 只有 59, 说明行数限制也可能写在 span 上) */ body:has(.topic-detail) .topic-desc__text > span { display: block !important; height: auto !important; max-height: none !important; overflow: visible !important; -webkit-line-clamp: unset !important; line-clamp: unset !important; text-overflow: clip !important; white-space: normal !important; } /* 展开 / 收起 按键:默认展开后就没用了,整块隐藏 (连同尺寸与热区一起归零,防止隐藏后仍挡住文字) */ body:has(.topic-detail) .topic-desc__text-btn, body:has(.topic-detail) [class*="topic-desc"][class*="btn"] { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; border: none !important; overflow: hidden !important; pointer-events: none !important; } /* ---- 4. 热门 / 最新 切换条:保留并居中(话题页排序靠它) ---- */ body:has(.topic-detail) .switch-tab { display: flex !important; align-items: center !important; justify-content: center !important; width: 100% !important; max-width: 100% !important; height: auto !important; /* v132.33:这里原是 min-height:48px —— 正是它让「按键大小变了但整体上下间距没变成 8px」。 探针「依旧不是8px.txt」实测: .topic-detail__header [344x137] top=8 bottom=145 mb=8 .switch-tab [344x48] top=153 bottom=201 mb=8 ← 仍是 48 └─ .switch-tab__tab [56x22] top=166 bottom=188 ← 已缩到 22 .topic-list__flow-list top=209 tab 文字确实缩到 22 了,但**外壳仍被 min-height 撑成 48**, 22 高的文字被上下各 13px 空白顶在正中: 文字顶 166 → 距 header 底 145 = 21px 文字底 188 → 距 flow-list 209 = 21px 即"按键大小变了、间距没变"。 ⚠ 另一条同名规则(v132.32 加的)已写 min-height:0, 但两条特异性相同、**后写的赢** —— 这条在后面,所以 48px 生效。 改为 0 后:外壳 = 内容高 22 → 文字顶 153 → 距 header 145 = 8 ✓ 文字底 175 → 距 flow-list(175+8=183) = 8 ✓ */ min-height: 0 !important; max-height: none !important; gap: 24px !important; /* 实测:上方 header 自带 8px 下边距,这里再补 8px 下边距, 上下就都等于卡片之间的 8px 间距(.list__topic-card 的下留白)。 */ margin: 0 0 8px 0 !important; padding: 0 !important; box-sizing: border-box !important; background: transparent !important; } /* 上间距 = header 的 margin-bottom 8px(下面这条), 下间距 = switch-tab 自己的 margin-bottom 8px, 两边都与卡片之间的 8px 一致。 ⚠ content 原生自带 margin-top 8px,会与 header 的 8px margin-collapse 成 8px(取大值),实测正常,显式写死防回归。 ⚠ topic-list 自身 margin 必须归零,否则会再叠一层。 */ body:has(.topic-detail) .topic-detail__header { margin-bottom: 8px !important; } body:has(.topic-detail) .topic-detail__content { margin-top: 8px !important; } body:has(.topic-detail) .topic-list { margin-top: 0 !important; margin-bottom: 0 !important; } body:has(.topic-detail) .switch-tab__tab { width: auto !important; height: auto !important; min-height: 0 !important; max-height: none !important; line-height: normal !important; margin: 0 !important; padding: 0 !important; } /* 原生那条 56x2 的下划线指示器是绝对定位的,隐藏后改用选中色区分 */ body:has(.topic-detail) .switch-tab__line { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; } body:has(.topic-detail) .switch-tab__tab-text { font-size: 15px !important; color: #61666d !important; } body:has(.topic-detail) .switch-tab__tab-text.selected { color: #00aeec !important; font-weight: 600 !important; } /* ---- 5. 卡片:与动态页 .bili-dyn-item 同一张皮 ---- */ body:has(.topic-detail) .list__topic-card { padding: 0 0 8px 0 !important; margin: 0 !important; box-sizing: border-box !important; } body:has(.topic-detail) .bili-dyn-item { position: relative !important; display: block !important; background: #ffffff !important; border: 1px solid #e3e5e7 !important; border-radius: 8px !important; padding: 12px 8px 12px 8px !important; margin: 0 !important; overflow: hidden !important; box-sizing: border-box !important; float: none !important; } body:has(.topic-detail) .bili-dyn-item * { max-width: 100% !important; box-sizing: border-box !important; } body:has(.topic-detail) .bili-dyn-item img, body:has(.topic-detail) .bili-dyn-item video, body:has(.topic-detail) .bili-dyn-item picture { max-width: 100% !important; height: auto !important; } body:has(.topic-detail) .bili-dyn-item, body:has(.topic-detail) .bili-dyn-item * { text-align: center !important; } /* ---- 6. 头像左浮 + 头部让位 64px(与动态页同参数) ---- */ /* ⚠ 与动态页同参数。absolute 的包含块是 .bili-dyn-item__main, 而 __main 本身就是 item 的内容盒(左边 +9、上边 +13 的 padding 已经 算进去了),所以这里必须 left/top = 0。 上一版写成 8/12 等于把内边距又叠了一遍 → 头像整体下移 12px 压住正文。 实测(修复前):头像 top=293,用户名 top=279,差 14px; 动态页是头像 56 / 用户名 54,只差 2px。 */ body:has(.topic-detail) .bili-dyn-item__avatar { position: absolute !important; left: 0 !important; top: 0 !important; width: 56px !important; max-width: 56px !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; } body:has(.topic-detail) .bili-dyn-item__avatar .bili-dyn-avatar, body:has(.topic-detail) .bili-dyn-item__avatar .b-avatar, body:has(.topic-detail) .bili-dyn-item__avatar img { width: 48px !important; height: 48px !important; max-width: 100% !important; border-radius: 50% !important; } body:has(.topic-detail) .bili-dyn-item__header { width: auto !important; max-width: 100% !important; min-width: 0 !important; /* 8px:与动态页 header→body 的间隔一致。话题页原生这里是 0, 而头像在容器内偏下 19px、高 48px,底部会压住正文第一行。 */ margin: 0 0 8px 0 !important; padding-left: 64px !important; padding-right: 8px !important; box-sizing: border-box !important; } body:has(.topic-detail) .bili-dyn-item__body { width: 100% !important; max-width: 100% !important; margin: 0 !important; padding: 0 8px 0 8px !important; box-sizing: border-box !important; } /* ---- 7. 动态页没有的行:关注按钮 / 三点更多 / 热评 / 底部互动栏 / 播放弹幕数 ---- */ body:has(.topic-detail) .bili-dyn-item__following, body:has(.topic-detail) .bili-dyn-follow-btn, body:has(.topic-detail) .bili-dyn-item__more, body:has(.topic-detail) .bili-dyn-more, body:has(.topic-detail) .bili-dyn-more__btn, body:has(.topic-detail) .bili-dyn-item__interaction, body:has(.topic-detail) .bili-dyn-interaction, body:has(.topic-detail) .bili-dyn-item__footer, body:has(.topic-detail) .bili-dyn-item__action, body:has(.topic-detail) .bili-dyn-action, body:has(.topic-detail) .bili-dyn-card-video__stat { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* ---- 8. 视频卡:PC 是「左封面 236 + 右文字 288」横排, 改成动态页那种「上封面(全宽 16:9)+ 下标题」竖排 ---- */ body:has(.topic-detail) .bili-dyn-content, body:has(.topic-detail) .bili-dyn-content__orig, body:has(.topic-detail) .bili-dyn-content__orig__major { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body:has(.topic-detail) .bili-dyn-card-video { display: flex !important; flex-direction: column !important; width: 100% !important; max-width: 100% !important; height: auto !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; } body:has(.topic-detail) .bili-dyn-card-video__header { width: 100% !important; max-width: 100% !important; height: auto !important; flex: 0 0 auto !important; } body:has(.topic-detail) .bili-dyn-card-video__cover { width: 100% !important; max-width: 100% !important; height: auto !important; aspect-ratio: 16 / 9 !important; box-sizing: border-box !important; } body:has(.topic-detail) .bili-dyn-card-video__cover .b-img, body:has(.topic-detail) .bili-dyn-card-video__cover .b-img__inner, body:has(.topic-detail) .bili-dyn-card-video__cover img, body:has(.topic-detail) .bili-dyn-card-video__cover picture { width: 100% !important; max-width: 100% !important; height: auto !important; aspect-ratio: 16 / 9 !important; object-fit: cover !important; display: block !important; } body:has(.topic-detail) .bili-dyn-card-video__body { width: 100% !important; max-width: 100% !important; height: auto !important; flex: 1 1 auto !important; padding: 8px 0 0 0 !important; border: none !important; border-width: 0 !important; border-style: none !important; box-shadow: none !important; outline: none !important; } /* 封面右侧那条半开口边框(原生可能是 ::before/::after 画的竖条)一并清掉 */ body:has(.topic-detail) .bili-dyn-card-video__body::before, body:has(.topic-detail) .bili-dyn-card-video__body::after, body:has(.topic-detail) .bili-dyn-card-video__desc::before, body:has(.topic-detail) .bili-dyn-card-video__desc::after { content: none !important; display: none !important; border: none !important; background: none !important; box-shadow: none !important; } body:has(.topic-detail) .bili-dyn-card-video__title { font-size: 14px !important; line-height: 20px !important; } body:has(.topic-detail) .bili-dyn-card-video__desc { font-size: 12px !important; line-height: 18px !important; color: #9499a0 !important; } /* 封面/图册居中 */ body:has(.topic-detail) .bili-dyn-content__orig__major .b-img, body:has(.topic-detail) .bili-dyn-content__orig__major picture { margin-left: auto !important; margin-right: auto !important; } /* ============================================================ opus 详情页(www.bilibili.com/opus/xxx)评论区:同样对齐视频页 实测链路:opus-detail 344 -> .bili-tabs.opus-tabs 328 -> .bili-tabs__content 312 -> .comment-wrap 296 -> .bili-comment-container 280 -> bili-comments 264 每层各吃 16px,层层嵌套后只剩 264,比视频页的 328 窄了 64。 目标:344 - 8(容器) - 8(容器) = 328,与视频页 bili-comments 完全一致。 做法:中间各层水平内边距归零,只由 .bili-comment-container 提供 8px, bili-comments 满宽且自身不留白。 (下面选择器比既有 opus 规则更具体,确保覆盖生效) ============================================================ */ body.bili-opus-page .bili-tabs.opus-tabs, body.bili-opus-page .bili-tabs.opus-tabs > .bili-tabs__content, body.bili-opus-page .bili-tabs.opus-tabs > .bili-tabs__content > .bili-tab-pane, body.bili-opus-page .bili-tabs.opus-tabs .comment-wrap { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; float: none !important; position: static !important; } body.bili-opus-page .bili-tabs.opus-tabs .comment-wrap > .bili-comment-container { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 8px !important; padding-right: 8px !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; } body.bili-opus-page .bili-comment-container bili-comments, body.bili-opus-page .comment-wrap bili-comments { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; box-sizing: border-box !important; } /* 与视频页一致:外部输入框/编辑器容器隐藏(shadow 内由 patch 处理) */ body.bili-opus-page .bili-comment-box, body.bili-opus-page .bili-comment-input-wrap, body.bili-opus-page .comment-send-box, body.bili-opus-page .bili-comment-editor, body.bili-opus-page .bili-comment-editor-wrap { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* 只隐藏「赞与转发」tab,保留「评论 177」 */ body.bili-opus-page .bili-tabs__nav__item:nth-of-type(3), body.bili-opus-page .bili-tabs__nav__items > .bili-tabs__nav__item:nth-of-type(3) { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* 右侧悬浮工具栏(417 / 2 / 0 / 8 / 177 / 顶部) */ body.bili-opus-page .sidebar-wrap, body.bili-opus-page .side-toolbar, body.bili-opus-page .side-toolbar__box, body.bili-opus-page .side-toolbar__bottom { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* ===== v132.65:动态里的相册图片 —— 居中 + 放不下就换行 ===== 探针实测(动态页 多张图片.txt / 动态页图片位置.txt、文章页 文章页面.txt): 九宫格 .bili-album__preview.grid3 [310x404] / left=25, 三张图 132x132 落在 left = -22 / 114 / 250 → 3×132 + 2×4 = 404,比容器 310 多出 94,站内网格把溢出 均摊到两侧 → 第一张左裁 22、第三张 right=382 右裁 22, 正是肉眼看到的"两边各切一刀"。 文章页 grid3 [296x540] / left=32,第三张 left=304 → 右边界 436, 超出 360 视口 76px,被 body 的 overflow-x:hidden 整块切掉。 单图 .bili-album__preview.single:图片 210x280 在 270(引用转发卡) / 310(普通)的容器里左对齐,右侧白留 60 ~ 100px —— 也是没居中。 修法:把预览容器从站内的「定宽 3 列网格」改成可换行的 flex 并居中: · flex-wrap:wrap —— 一行塞不下就整张掉到下一行,绝不裁; · justify-content:center —— 每行都居中,最后一行缺几张也居中; · gap:4px —— 与站内实测间距一致(第二行 top 差 136 = 132 + 4); · grid-template-columns:none —— 那套固定列宽随 display:flex 失效, 显式写死防站内将来改回 grid。 图片自身尺寸一个字节都不动(132×132 / 210×280 都保留), 于是 310 宽的容器自然排 2 张(132×2 + 4 = 268 ≤ 310)居中, 270 的引用转发卡同样 2 张,文章页 296 也是 2 张 —— 全部不裁。 选择器不带 body class:相册在动态页 / 空间页 / 话题页 / opus 详情页 是同一套 DOM;图片查看器叫 .bili-album__watch(不是 __preview), 不受影响。纯 CSS 即可 —— 站内这套是样式表写的,探针里没有行内样式。 */ .bili-album__preview { display: flex !important; flex-direction: row !important; flex-wrap: wrap !important; justify-content: center !important; align-items: flex-start !important; align-content: flex-start !important; gap: 4px !important; width: 100% !important; max-width: 100% !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; grid-template-columns: none !important; grid-auto-flow: row !important; overflow: visible !important; } .bili-album__preview > .bili-album__preview__picture { flex: 0 0 auto !important; /* 不压缩、不拉伸,放不下就整张换行 */ max-width: 100% !important; /* 极窄容器里只缩宽,绝不溢出被裁 */ min-width: 0 !important; margin: 0 !important; box-sizing: border-box !important; } /* ===== v132.66:九宫格回到 3 列(在 v132.65 的基础上缩图)===== v132.65 只做「居中 + 换行」,图仍是站内给的 132×132,于是 310 宽的容器一行只排得下 2 张(132×2 + 4 = 268),九宫格变成 2×5。 用户要的是回到 3×3 —— 那就必须把格子缩到容器内三分之一: 格宽 = (容器宽 − 2×gap) / 3 = (310 − 8) / 3 ≈ 100.7 文章页 296 → 96,引用转发卡 270 → 87.3,全部 3 列且零裁剪。 两条关键点: ① flex 用 0 **1** calc(...) —— 允许极少量收缩。 若写 0 0(不收缩),三个 100.66… 加 8px 间隙在高 DPI 下 舍入后可能算出 310.01 > 310,第 3 张就被挤到下一行变成 2 列; 允许收缩可吸收这点误差,视觉上差不到 0.4px。 ② 图片**不写死高度**,只 width:100% + height:auto。 站内的格子是正方形(132×132),等比缩放后仍是正方形; 不写 height 就不会有「父高未定 → height:100% 塌成 0」的死循环 (v132.55 那次灯箱塌成 0×0 就是这么来的), 也不依赖 aspect-ratio 的浏览器支持。 只作用于 .grid3(九宫格):单图 single 的 210×280 与两图 grid2 保持 v132.65 的原尺寸居中,不参与缩放。 */ .bili-album__preview.grid3 > .bili-album__preview__picture { flex: 0 1 calc((100% - 8px) / 3) !important; width: calc((100% - 8px) / 3) !important; max-width: calc((100% - 8px) / 3) !important; height: auto !important; min-width: 0 !important; box-sizing: border-box !important; } /* 格子里的三层(.b-img 外框 → picture → img)一起跟着缩, 否则外框小了、img 仍是 132 就会被 overflow:hidden 切掉一半 */ .bili-album__preview.grid3 > .bili-album__preview__picture > .bili-album__preview__picture__img, .bili-album__preview.grid3 > .bili-album__preview__picture > .bili-album__preview__picture__img > picture.b-img__inner, .bili-album__preview.grid3 > .bili-album__preview__picture > .bili-album__preview__picture__img > picture.b-img__inner > img { width: 100% !important; height: auto !important; max-width: 100% !important; min-width: 0 !important; object-fit: cover !important; box-sizing: border-box !important; } /* ===== v132.71:相册图片下方多出的一段空档 ===== 现象:动态里有些单图(尤其宽图 / 视频封面)图片下方凭空多出 20~50px 空白,图片与下方文字之间被撑开。 探针实测(bili_probe_2026-10-02_18-58-17 动态页): .bili-album__preview.single [310x203] ├─ __picture [310x203] ← 盒子 203 高 │ └─ __picture__img (.b-img) [310x203] │ └─ picture.b-img__inner [310x175] ← 图只占 175 │ └─ img [310x175] → 底部 203 − 175 = 28px 空档。 另一条(引用转发卡,容器 270):盒 203 / 图 152 → 空 51px。 真因:站内给单图预留的尺寸是「按 360 宽」算的 —— 站内常量:预览最大宽 360、最大高 280。 16:9 的图(ratio 0.565)受"宽度上限"约束 → 360 × 0.565 = 203。 而脚本把内容区收窄到 310(引用卡 270), 本脚本那条通用的 img 规则(max-width:100% + height:auto)又把图 等比压到 310 → 310 × 0.565 = 175。 盒子高度是站内写死的 203,不会跟着一起缩,于是差出 28px。 (竖图 / 窄图不受影响:站内那种情况是受"高度上限 280"约束, 盒宽 = 280 / ratio 本就等于容器内的实际渲染宽,盒图一致。) 修法:让盒子高度跟随图片实际渲染高度 —— height:auto + max-height:none + min-height:0 写在这一整条链上。 只动高度,不动宽度 —— 绝不会把图放大或改变裁切比例; 图本来就是完整显示的,只是盒子多留了一截,收掉即可。 选择器不带 body class:相册在动态页 / 空间页 / 话题页 / opus 详情页是同一套 DOM,一次全中。 */ .bili-album__preview, .bili-album__preview > .bili-album__preview__picture, .bili-album__preview > .bili-album__preview__picture > .bili-album__preview__picture__img, .bili-album__preview picture.b-img__inner { height: auto !important; max-height: none !important; min-height: 0 !important; box-sizing: border-box !important; } /* ⚠ 本组只清高度,display 一律不碰 —— 两处坑: ① .bili-album__preview 上不能写 display:v132.65 那条 display:flex !important 与它特异性相同(都是 0,1,0), 同权重下后写的胜出,本条在它之后会把 flex 覆盖成 block, 居中与换行立刻全废; ② picture.b-img__inner 也不能改成 block: 默认是 inline, 靠 text-align:center 居中(opus / 空间页都是这么排的), 改成 block 后会撑满整行、窄图从居中变成靠左。 探针里那 28px 已由盒高 203 − 图高 175 完整解释, 没有额外基线间隙,不需要动 display。 */ /* ===== v132.72:动态页轮播图改为屏内完整平铺 ===== 需求:动态页里那种「横向滑动看多张图」的轮播模块,改为在手机屏内 完整显示 —— 不裁剪、不拉伸,超出屏宽就等比缩放,放不下就换行。 探针实测(动态页的探针10030829.txt,置顶那条 opus 卡): .dyn-card-opus__pics [310x268] left=25 └─ .bili-dyn-gallery [310x268] └─ .bili-dyn-gallery__window [310x268] pos=relative ovf=hidden ← 裁剪元凶 ├─ .bili-dyn-gallery__nav.prev [0x0] ├─ .bili-dyn-gallery__nav.next [36x36] left=273 └─ .bili-dyn-gallery__track [222x268] left=113 └─ .bili-dyn-pic.bili-dyn-gallery__image ×18 前 6 张 [201x268] 后 2 张 [222x268] img [201x0] 真因两层:① __window 是 overflow:hidden 的定宽窗口,track 比它宽 (18 张横向排开),超出部分被切 —— 这就是"一次只露一张"的实现; ② 每张图的宽(201 / 222)是站内按"填满窗口高 268"反算的, 不是按容器宽算的,容器一收窄就溢出。 修法:把这条链从"定宽窗口 + 横向轨道"改成"可换行的 flex 网格"。 */ /* ① 三层容器:不再裁剪,高度交给内容。 ⚠ 不覆盖 position —— __window 的 relative 是 __nav 的定位上下文, 真正要治的是 overflow,不是定位。 */ .dyn-card-opus__pics, .bili-dyn-gallery, .bili-dyn-gallery__window { display: block !important; /* v132.74:见下方说明 */ width: 100% !important; min-width: 0 !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; /* v132.74:这三处是「左侧 88px 空白」的真凶候选,一并清零 —— ① padding-left 88 → 内容盒只剩 222,子级 width:100% 就变 222; ② 父级是 grid 且列宽 auto → 列按内容宽(222)定型,百分比也变 222; ③ 父级是 flex/grid 且右对齐 → 222 宽的块被推到右边。 */ padding: 0 !important; padding-left: 0 !important; padding-right: 0 !important; padding-inline: 0 !important; padding-inline-start: 0 !important; padding-inline-end: 0 !important; margin: 0 !important; margin-left: 0 !important; margin-right: 0 !important; border-width: 0 !important; border-left-width: 0 !important; grid-template-columns: none !important; grid-auto-flow: row !important; justify-content: flex-start !important; justify-items: stretch !important; align-items: stretch !important; box-sizing: border-box !important; } /* ② 轨道:可换行 flex 居中。transform:none 与 position:static / margin:0 一并写 —— 探针里图片 rect left 是 -1713 这种大负值 (relative 元素却整体左移近 2000px),位移可能来自行内 transform / 负 margin / left 三者之一,单写 transform 有可能漏。 */ /* v132.73:宽度必须是父级链写法 —— 单类选择器(0,1,0)压不住站内那条 同带 !important 的宽度声明,探针实测 track 仍是 222 宽(= 3×71 + 2×4 的内容宽)且贴在窗口右侧,左侧空出 310 − 222 = 88px 的整片空白。 */ .bili-dyn-gallery__window > .bili-dyn-gallery__track, .bili-dyn-gallery > .bili-dyn-gallery__window > .bili-dyn-gallery__track { display: flex !important; flex-direction: row !important; flex-wrap: wrap !important; justify-content: center !important; align-items: flex-start !important; align-content: flex-start !important; gap: 4px !important; width: 100% !important; min-width: 100% !important; /* 防 flex/grid item 被收缩到内容宽 */ max-width: 100% !important; flex: 1 1 100% !important; /* 若父级是 flex/grid,直接吃满一行 */ align-self: stretch !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; position: static !important; left: auto !important; right: auto !important; top: auto !important; transform: none !important; box-sizing: border-box !important; } /* ③ 每张图:3 列等分(与 v132.66 九宫格一致)。 flex:0 **1** 允许极少量收缩 —— 三个 100.66… 加 8px 间隙在高 DPI 下 舍入可能算出 310.01 > 310,写 0 0 会把第 3 张挤到下一行变回 2 列。 height:auto 且不写死高度 —— 站内格子是 201x268 / 222x268 的竖图, 等比缩放后比例不变;写死高度就会"拉伸"。 想改 2 列:把下面三处分母 3 换成 2 即可,别动别的。 */ .bili-dyn-gallery__track > .bili-dyn-pic.bili-dyn-gallery__image { position: relative !important; left: auto !important; right: auto !important; top: auto !important; flex: 0 1 calc((100% - 8px) / 3) !important; width: calc((100% - 8px) / 3) !important; max-width: calc((100% - 8px) / 3) !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; box-sizing: border-box !important; } /* ④ 内层三层跟着缩,否则外框小了、img 仍是 201 会被 overflow:hidden 切一半。 这里可以放心把 .b-img / picture.b-img__inner 改成 block —— v132.71 那次是因为宽度由图片自身决定、窄图靠 text-align:center 居中才不敢动, 本组宽度已被 calc 钉死,居中交给 flex 的 justify-content,不存在那种场景。 object-fit:contain 是保底:万一某张图原始比例与盒子不符,只留白、不裁切。 */ .bili-dyn-gallery__track > .bili-dyn-pic.bili-dyn-gallery__image > .bili-dyn-pic__img, .bili-dyn-gallery__track > .bili-dyn-pic.bili-dyn-gallery__image > .bili-dyn-pic__img > .b-img, .bili-dyn-gallery__track > .bili-dyn-pic.bili-dyn-gallery__image > .bili-dyn-pic__img > .b-img > picture.b-img__inner, .bili-dyn-gallery__track > .bili-dyn-pic.bili-dyn-gallery__image > .bili-dyn-pic__img > .b-img > picture.b-img__inner > img { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; transform: none !important; box-sizing: border-box !important; } .bili-dyn-gallery__track > .bili-dyn-pic.bili-dyn-gallery__image > .bili-dyn-pic__img > .b-img > picture.b-img__inner > img { object-fit: contain !important; } /* ⑤ 左右箭头整块隐藏 —— 轮播已废,箭头留着会盖住图片并吃掉点击热区 */ .bili-dyn-gallery__nav { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; pointer-events: none !important; } /* ===== opus 详情页 ===== */ body.bili-opus-page { background: #f4f5f7 !important; overflow-x: hidden !important; padding-top: 8px !important; } body.bili-opus-page .opus-module-content, body.bili-opus-page .opus-module-content p, body.bili-opus-page .opus-module-content span, body.bili-opus-page .opus-module-title { text-align: center !important; } body.bili-opus-page .opus-para-pic, body.bili-opus-page .bili-album, body.bili-opus-page .bili-album__preview.grid2, body.bili-opus-page .opus-para-link-card, body.bili-opus-page .bili-dyn-card-goods, body.bili-opus-page .bili-dyn-card-goods__wrap, body.bili-opus-page .bili-dyn-card-goods__one { margin-left: auto !important; margin-right: auto !important; text-align: center !important; } body.bili-opus-page #bili-header-container, body.bili-opus-page .bili-header, body.bili-opus-page .bili-header--mini, body.bili-opus-page .bili-header--fixed, body.bili-opus-page .bili-header__bar, body.bili-opus-page .bili-header__menu, body.bili-opus-page .international-header, body.bili-opus-page .mini-header { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; } body.bili-opus-page .bgc, body.bili-opus-page .bg { display: none !important; } body.bili-opus-page .right-sidebar-wrap, body.bili-opus-page .side-toolbar, body.bili-opus-page .go-top, body.bili-opus-page [class*="back-top"], body.bili-opus-page [class*="to-top"] { display: none !important; visibility: hidden !important; } body.bili-opus-page #app, body.bili-opus-page #opus-detail-app, body.bili-opus-page .opus-detail, body.bili-opus-page .bili-opus-view, body.bili-opus-page .opus-module-top, body.bili-opus-page .opus-module-title, body.bili-opus-page .opus-module-author, body.bili-opus-page .opus-module-content, body.bili-opus-page .opus-module-copyright, body.bili-opus-page .opus-module-bottom, body.bili-opus-page .bili-tabs.opus-tabs, body.bili-opus-page .bili-tabs__header, body.bili-opus-page .bili-tabs__content, body.bili-opus-page .comment-wrap, body.bili-opus-page .bili-comment-container { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 8px !important; padding-right: 8px !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; float: none !important; position: static !important; left: auto !important; right: auto !important; } /* v132.55:同上 —— 排除灯箱,否则 opus 页点「查看大图」是空的 */ body.bili-opus-page img:not([class*="pswp"]), body.bili-opus-page video:not([class*="pswp"]), body.bili-opus-page picture:not([class*="pswp"]), body.bili-opus-page .b-img:not([class*="pswp"]), body.bili-opus-page .b-img__inner:not([class*="pswp"]) { max-width: 100% !important; height: auto !important; display: block !important; } body.bili-opus-page .opus-module-top__album__cover, body.bili-opus-page .opus-module-top__display, body.bili-opus-page .opus-module-top__album { width: 100% !important; max-width: 100% !important; height: auto !important; box-sizing: border-box !important; } /* ============================================================ ---- v132.14:专栏首页(read/home)信息行只留「头像 + UP主名」---- 结构(探针 bili_probe_2026-10-01_08-26-52): a.article-item [344x128] (pt/pb 各 20) └─ .article-item__left [207x88] ★ overflow:hidden,高度写死 88 ├─ .article-item__details [207x52](标题 26 + 摘要 22) └─ .article-item__meta [207x35] ├─ .article-item__author [77x24] 头像 24 + 名字 ├─ .article-item__label [22x35] 「日常」 └─ .article-item__stats [163x35] 阅读 / 赞 / 评论 【为什么只有部分 UP主会「头像名字往下偏移、被裁剪」】 这一行是**横向塞不下**的:author + label + stats 实际需要 77 + 22 + 163 = 262px,而 .article-item__left 只有 207px。 名字短的(如「源初之结」4 字)刚好挤得下,探针里就是正常的; 名字一长(如「橘子橘子酸不酸」7 字),author 撑到 110px 以上, 整行溢出 → 站内 flex 让 stats 内部换行 → meta 从 35px 长到 两行高。 而 author 在 meta 里是**垂直居中**的(探针:author 中心 -8155、meta 中心 -8154.5,基本重合)—— meta 一变高, author 的中心就跟着往下跑;左边那块高度写死 88 且 overflow:hidden,于是头像和名字被下半截裁掉。 不开脚本时页面按宽屏渲染、这一行塞得下,所以看不出问题。 修法(正好也是需求要的): ① 该行只留头像 + 名字:分类标签、阅读/赞/评论全部隐藏 → 横向不再溢出,也就不会再换行,author 不再下移; ② meta 钉成单行 24px、nowrap,双保险; ③ 名字超长省略号,不再把行撑爆; ④ .article-item__left 放开高度与裁剪,万一标题两行 也不会再把下面切掉。 ---- */ /* ① 只留头像 + 名字:分类标签 / 阅读·赞·评论 全部隐藏 */ body.bili-opus-page .article-item__label, body.bili-opus-page .article-item__stats, body.bili-opus-page .article-item__stat, body.bili-opus-page .article-item__meta > *:not(.article-item__author) { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* ② 信息行:钉成单行 24px,永不换行 */ body.bili-opus-page .article-item__meta { display: flex !important; flex-wrap: nowrap !important; align-items: center !important; justify-content: flex-start !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 24px !important; min-height: 24px !important; max-height: 24px !important; margin: 4px 0 0 0 !important; padding: 0 !important; overflow: hidden !important; box-sizing: border-box !important; } /* ③ 头像 + 名字:一行内排布,名字超长省略 */ body.bili-opus-page .article-item__author { display: flex !important; flex-wrap: nowrap !important; align-items: center !important; gap: 5px !important; flex: 0 1 auto !important; width: auto !important; max-width: 100% !important; min-width: 0 !important; height: 24px !important; max-height: 24px !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* 头像固定 24x24(上面那条「img 一律 block + height:auto」 会把头像也卷进去,这里按 24 写死,尺寸才稳) */ body.bili-opus-page .article-item__avatar, body.bili-opus-page .article-item__author .b-img, body.bili-opus-page .article-item__author .b-img__inner, body.bili-opus-page .article-item__author img, body.bili-opus-page .article-item__author picture { flex: 0 0 24px !important; width: 24px !important; min-width: 24px !important; max-width: 24px !important; height: 24px !important; max-height: 24px !important; border-radius: 50% !important; object-fit: cover !important; flex-shrink: 0 !important; } body.bili-opus-page .article-item__uname { flex: 1 1 auto !important; width: auto !important; max-width: 100% !important; min-width: 0 !important; height: 17px !important; line-height: 17px !important; margin: 0 !important; padding: 0 !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; } /* ④ 左半块放开高度与裁剪:标题即使两行也不会把下面切掉 */ body.bili-opus-page .article-item, body.bili-opus-page .feed-item, body.bili-opus-page .article-item__left, body.bili-opus-page .article-item__details { height: auto !important; min-height: 0 !important; max-height: none !important; } body.bili-opus-page .article-item__left { overflow: visible !important; } /* 标题 / 摘要各限两行,卡片高度不失控 */ body.bili-opus-page .article-item__title, body.bili-opus-page .article-item__desc { display: -webkit-box !important; -webkit-line-clamp: 2 !important; -webkit-box-orient: vertical !important; overflow: hidden !important; height: auto !important; min-height: 0 !important; max-height: none !important; white-space: normal !important; } /* ============================================================ ---- v132.17:卡片间距 8px + 隐藏分割线 ---- 背景(探针推荐文章页面.txt): .feed [344x3130] └─ .feed-item [344x169] top=78 bottom=247 └─ a.article-item [344x168] top=78 bottom=246 两者差的那 1px(246~247)就是**下方分割线**;上一张卡的 这条线占 77~78。加上 v132.16 的白卡背景后,卡片之间只隔 这 1px 细线,两张白卡几乎贴在一起,阴影也互相压住。 ⚠ 分割线不能用 display:none 干掉 —— 探针里它是 feed-item 与 article-item 的**高度差**(169 vs 168), 极可能就是 .feed-item 自身的 border-bottom, 也可能由某条伪元素绘制;直接隐藏元素会把卡片一起弄没。 所以这里两条一起上: ① border-bottom / border-top 一律 none(覆盖真边框) ② ::before / ::after 一律不显示(覆盖伪元素画的线) ⚠ 间距用 margin-bottom:8px 放在 .feed-item 上: 每张卡「自身高度 + 8」= 下一张的开始,卡片间恒定 8px; 不用 gap —— .feed 的 display 未知,gap 未必生效。 ⚠ 8px 是从 feed-item 外面加的,不改卡片内部几何, v132.16 那套 4px 内边距 / 12px 图文间距完全不受影响。 ---- */ /* ① 卡片间距 8px(最后一张多出的 8px 正好当作列表底部留白) */ body.bili-opus-page .feed-item { margin: 0 0 8px 0 !important; } /* ①-b v132.18:第一张卡与上方搜索框之间同样 8px。 探针(bili_probe_2026-10-01_09-15-24): .header [344x40] top=38 bottom=78 ← 标题 + 搜索框 .feed [344x30346] top=78 ← 紧贴着 header .feed-item [344x168] top=78 ← 第一张卡 header 底 78 = feed 顶 78 = 第一张卡顶 78,三者严丝合缝, 第一张卡与搜索框之间没有间距。 用 padding-top 而不是 margin-top:padding 撑开的是容器内部, 不会被相邻 margin 折叠吞掉,间距恒为 8px。 加完:feed 顶仍 78,第一张卡顶 86 → 距搜索框底 8px ✓ 卡片之间的 8px 由上面那条 margin-bottom 负责,两者互不干扰。 */ body.bili-opus-page .feed { padding-top: 8px !important; box-sizing: border-box !important; } /* ② 分割线:真边框 + 伪元素绘制的,一并清掉 */ body.bili-opus-page .feed-item, body.bili-opus-page .article-item { border: none !important; border-top: none !important; border-bottom: none !important; border-left: none !important; border-right: none !important; background-image: none !important; } body.bili-opus-page .feed-item::before, body.bili-opus-page .feed-item::after, body.bili-opus-page .article-item::before, body.bili-opus-page .article-item::after { display: none !important; content: none !important; height: 0 !important; border: none !important; } /* ============================================================ ---- v132.15:右侧封面图上下留白相等 ---- 以中间那张卡为例(探针 bili_probe_2026-10-01_08-44-10, 上下都有分割线,最准): .feed-item [344x169] top=-10469 bottom=-10300 a.article-item [344x168] top=-10469 bottom=-10301 pt=20 pb=20 → 内容区 -10449 ~ -10321 封面 .article-item__cover [117x88] top=-10449 bottom=-10361 feed-item 比 article-item 多出的那 1px 就是**下方分割线** (占 -10301 ~ -10300);上一张卡的这条线占 -10470 ~ -10469。 封面 → 上方分割线 = -10449 − (-10469) = 20 ← 卡的 padding-top 封面 → 下方分割线 = -10301 − (-10361) = 60 ← 多了 40 多出来的 40 从哪来:左半块标题/摘要限两行后长到 128, 右半块仍是原生 88,且 flex 默认顶部对齐 —— 于是下方空出 128−88 = 40 的死白,加 padding-bottom 20 共 60。 修法:让右半块**拉伸到与左半块等高**(align-items:stretch), 封面铺满它(object-fit:cover 只裁不变形)。 封面底边落到内容区底 -10321 → 距下方分割线 = 20, 与距上方分割线的 20 完全一致。 【v132.16:卡片背景 + 文字封面间距】 专栏页(探针 bili_probe_2026-10-01 专栏页对比): .article-banner [154x131] left=8 → 右边缘 162 .article-content [178x131] left=174 → 文字与封面间距 = 174 − 162 = **12px** 推荐文章页原本是**紧贴的**: .article-item__left [227x128] left=8 → 右边缘 235 .article-item__right [117x128] left=235 → 间距 = 0 所以这里把间距补成 12px(用 flex gap,不再靠各自宽度算)。 卡片皮肤逐项对齐专栏页 .b-article-card: background #ffffff / border none / radius 8px box-shadow 0 1px 3px rgba(0,0,0,.08) / overflow hidden 再加左右 4px 内边距,避免文字贴到卡片左边缘。 ---- */ /* ① 卡片:两列都拉伸到等高(左半块高多少,右半块就跟多少) v132.16:补上白卡背景(与专栏页 .b-article-card 完全一致) + 左右 4px 内边距 + 文字与封面 12px 间距。 */ body.bili-opus-page .article-item { display: flex !important; flex-wrap: nowrap !important; align-items: stretch !important; /* 与专栏页一致的卡片皮肤 */ background: #ffffff !important; border: none !important; border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08) !important; overflow: hidden !important; box-sizing: border-box !important; position: relative !important; /* v132.19:上下由站内原生的 20 收到 8 —— 探针 bili_probe_2026-10-01_09-24-07: a.article-item [344x168] top=83 pt=20 pb=20 封面 .article-item__cover [117x128] top=103 bottom=231 → 封面距卡片上边缘 = 103−83 = 20 封面距卡片下边缘 = 251−231 = 20 现改为上下各 8: 卡片高 = 内容 128 + 8 + 8 = 152(原 168) 封面顶 = 83+8 = 91,底 = 91+128 = 219 卡片底 = 83+152 = 235 → 219 到 235 正好 8 ✓ ⚠ 只改 padding,封面自身(117 宽、拉伸到内容高 128、 object-fit:cover 居中裁切)一行都不动。 左右仍是 4:加了白底后文字紧贴卡片左边缘, 留这 4px 才不贴边。内容区 = 344 − 8 = 336。 */ padding: 8px 4px !important; /* 文字与封面的间距,取专栏页实测值 12px(见下方说明) */ gap: 12px !important; } /* 文字区:吃掉除封面与间距外的全部宽度 336 − 117 − 12 = 207。 实测校验:文字 12→219,间距 12 → 封面 231→348, 卡片右边 352 − padding 4 = 348 ✓ 右侧同样不贴边。 */ body.bili-opus-page .article-item__left { flex: 1 1 0 !important; width: auto !important; min-width: 0 !important; max-width: none !important; height: auto !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* ② 右半块:跟着拉伸,宽度保持 117 */ body.bili-opus-page .article-item__right { display: flex !important; align-self: stretch !important; flex: 0 0 117px !important; width: 117px !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* ③ 封面:铺满右半块(高度 100%),不变形只裁切 */ body.bili-opus-page .article-item__cover { flex: 1 1 auto !important; width: 100% !important; height: 100% !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; /* 与专栏页 .article-banner 一致:四角 8px 圆角 */ border-radius: 8px !important; overflow: hidden !important; box-sizing: border-box !important; } /* ④ 封面内的图:撑满容器。 ⚠ 必须覆盖上面那条「opus 页 img 一律 height:auto」的 全局规则 —— 否则图会按 4:3 缩回 88 高,白改一场。 object-fit:cover 保证只裁剪、不拉伸变形。 */ body.bili-opus-page .article-item__cover .b-img, body.bili-opus-page .article-item__cover .b-img__inner, body.bili-opus-page .article-item__cover img, body.bili-opus-page .article-item__cover picture { position: relative !important; display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 100% !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; border-radius: 0 !important; object-fit: cover !important; object-position: center !important; box-sizing: border-box !important; } /* ===== opus 详情页:隐藏三处模块 ===== 实测:.opus-module-top [312x187](标题上方的封面图) .opus-module-copyright [312x54](cv53172768) .opus-module-bottom [312x96](分享至 / 投诉或建议) 注意:上面已有的宽度规则里包含了这三个选择器,但只设了宽高/内边距, 这里单独用 display:none 隐藏;选择器更具体,优先级覆盖无冲突。 */ body.bili-opus-page .bili-opus-view > .opus-module-top, body.bili-opus-page .opus-detail .opus-module-top, body.bili-opus-page .opus-module-top { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } body.bili-opus-page .bili-opus-view > .opus-module-copyright, body.bili-opus-page .opus-detail .opus-module-copyright, body.bili-opus-page .opus-module-copyright { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } body.bili-opus-page .bili-opus-view > .opus-module-bottom, body.bili-opus-page .opus-detail .opus-module-bottom, body.bili-opus-page .opus-module-bottom, body.bili-opus-page .opus-module-bottom__share, body.bili-opus-page .opus-module-bottom__share__list { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* ===== opus 详情页:UP 主行右侧竖排「⋯」更多按钮 ===== 探针 09-29_11-17-15 实测(视口 360): .opus-module-author[312x90] left=24 pt=12 pb=12 > .__left[66x66] left=32 (头像 66 + 挂件) > .__center[88x62] left=98 (昵称 / 发布时间) > .__right[142x48] left=186 > .opus-module-author__more[24x24] **left=304** ← 目标 > .opus-more > .opus-more__container.opus-more__btn > svg[24x24] > .opus-module-author__decorate[110x33] left=186(装扮卡片,保留) 竖排三个点就是 .opus-module-author__more —— 整块 24x24, 点开是举报/关注之类的浮层,手机端这一列本来就挤(昵称只有 72 宽), 收掉它还能让 .__right 少占 24px。 只隐藏 __more,**不动 __decorate**(装扮卡片是 UP 主的展示信息)。 ------------------------------------------------------------ */ body.bili-opus-page .opus-module-author__more, body.bili-opus-page .opus-module-author__more > .opus-more, body.bili-opus-page .opus-module-author__more .opus-more__container, body.bili-opus-page .opus-module-author__more .opus-more__btn, body.bili-opus-page .opus-module-author__right > .opus-more { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* 高特异性兜底(v131.36 套路):完整链路压过去 */ body.bili-opus-page .bili-opus-view > .opus-module-author > .opus-module-author__right > .opus-module-author__more { display: none !important; width: 0 !important; height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-opus-page .opus-para-pic, body.bili-opus-page .opus-pic-view, body.bili-opus-page .bili-dyn-pic, body.bili-opus-page .bili-dyn-pic__img { width: 100% !important; max-width: 100% !important; height: auto !important; margin-left: auto !important; margin-right: auto !important; box-sizing: border-box !important; } body.bili-opus-page * { max-width: 100% !important; box-sizing: border-box !important; } /* ============================================================ v131.28 长按头像弹出的「UP 主信息卡片」固定在屏幕内显示 探针实测(三份都已抓到,挂在 body 直下): 空间搜索页 body > div.bili-user-profile:nth-of-type(5) [360x261] 空间动态页 body > div.bili-user-profile:nth-of-type(5) [360x261] 动态详情页 body > div.bili-user-profile:nth-of-type(10) [360x266] 卡片本身已经是满宽 360,问题出在定位:站内 JS 按 PC 布局算出的 left/top(或 transform)把它推到了视口之外。探针只记录尺寸不记录坐标, 无法确定是被横向推出还是纵向推出,所以这里采用"强制钉在视口内"的 做法,两种情形一并覆盖: · position: fixed —— 相对视口定位,不再受文档坐标影响 · left/right 双写 0 + width 100% —— 横向必定铺满视口 · top: auto + bottom: 0 —— 纵向必定贴底(手机上的 bottom-sheet) · transform: none —— 压掉站内用 translate 做的偏移 !important 的作者样式优先级高于站内写在 style 属性里的普通声明。 注意:不写 display / visibility,显示与隐藏仍交给站内 JS 控制。 ============================================================ */ body .bili-user-profile { position: fixed !important; left: 0 !important; right: 0 !important; /* v131.29:改为距屏幕顶部 36px(原来是贴底 bottom:0) */ top: 36px !important; bottom: auto !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; max-height: 90vh !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; transform: none !important; translate: none !important; scale: none !important; overflow-x: hidden !important; overflow-y: auto !important; -webkit-overflow-scrolling: touch !important; border-radius: 12px 12px 0 0 !important; box-shadow: 0 -2px 12px rgba(0,0,0,0.12) !important; z-index: 2147483647 !important; } /* 内部两层:满宽 + 裁掉溢出(背景图原生 366 宽,比视口宽 6px) */ body .bili-user-profile__content, body .bili-user-profile-view, body .bili-user-profile-view__background, body .bili-user-profile-view__info { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; box-sizing: border-box !important; overflow: hidden !important; transform: none !important; } /* v131.29 隐藏「发消息」按键(保留「关注/已关注」)。 只写 display:none:footer 是 flex,移除后关注按钮会自动铺满,不留空隙。 */ body .bili-user-profile-view__info__button.chat { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* ============================================================ ========== UP 主空间页 ========== ============================================================ */ html:has(body.bili-space-page), body.bili-space-page { width: 100% !important; max-width: 100% !important; min-width: 0 !important; /* v131.33:hidden 会让 body 变成滚动容器,导致内部 sticky 全部失效 (胶囊栏吸不住、一滚就钻到搜索框底下 -> 间距看起来是 0)。 clip 同样能裁掉横向溢出,但不会创建滚动容器,sticky 正常工作。 两条都写:老浏览器吃 hidden,新版吃 clip。 */ overflow-x: hidden !important; overflow-x: clip !important; margin: 0 !important; padding: 0 !important; background: #f1f2f3 !important; } /* v131.37 顶部 52px 占位改为「唯一来源」。 原写法只给 body 加 padding-top: 52px,一旦它被站内样式或其它规则 覆盖,整个空间页内容就会顶到 y=0: 胶囊栏(pill) 8..42 完全落进固定搜索栏 0..52 里 -> 胶囊栏看不见 第一行卡片顶 50,被搜索栏压掉 2px -> 顶部裁剪显示不全 正好对应你反馈的两个现象。 改法:body 的 padding 归零,改由 #app 提供这 52px。 两者效果等价(都是把 #app 内容整体下移 52),但来源唯一、 不再依赖 body 那条容易被盖掉的规则。 放在 #app 上也同时保证 nav-bar(投稿页)初始位置就是 52, 与它自己的 sticky top:52 一致,不会先被搜索栏盖住再跳下来。 */ body.bili-space-page { padding-top: 0 !important; } body.bili-space-page > #app, body.bili-space-page > div#app { padding-top: 52px !important; box-sizing: border-box !important; /* v131.39 真凶(探针 14-15-15 实证):#app 带站内原生的 margin-top: -64px,而且它与 body 的 margin 发生了外边距合并, 把 body 本身也顶到了 top=-64: html top=0 / body top=-64 / #app top=-64 (pt=52 mt=-64) → main top=-12 → 胶囊栏 top=-4 → 卡片顶 38 于是胶囊栏 8..50 整体落进固定搜索栏 0..52 里被完全盖住, 第一行卡片顶 38 也被切掉 —— 正好是你看到的两个现象。 这个 -64 原本是站内给 PC 头部留的负补偿,脚本已隐藏 PC 头部, 它就必须归零。 */ margin-top: 0 !important; } /* 顺带断掉 body 与 #app 的外边距合并,避免再被负边距带偏 */ body.bili-space-page > #app::before { content: '' !important; display: block !important; height: 0 !important; overflow: hidden !important; } /* v131.38 加固:.space-theme-bg 是站内绝对定位的背景层(360x800), 脚本从未处理过它。若它层级高于普通流内容,就会把 main 顶部 (含胶囊栏)整块盖住 —— 表现就是"胶囊栏凭空消失"。 这里把它压到最底层并禁用命中,彻底排除遮挡可能。 */ body.bili-space-page .space-theme-bg, body.bili-space-page #app > .space-theme-bg { position: absolute !important; top: 0 !important; left: 0 !important; z-index: 0 !important; pointer-events: none !important; } /* main 与胶囊栏显式抬到背景层之上 */ body.bili-space-page #app > main.space-main { position: relative !important; z-index: 2 !important; } body.bili-space-page .search-sidebar, body.bili-space-page .upload-sidenav { z-index: 3 !important; } body.bili-space-page .bili-header, body.bili-space-page .international-header, body.bili-space-page .mini-header, body.bili-space-page #biliMainHeader, body.bili-space-page #bili-header-container, body.bili-space-page .bili-header--mini, body.bili-space-page .bili-header--fixed, body.bili-space-page .bili-header__bar, body.bili-space-page .bili-header__menu, body.bili-space-page .footer, body.bili-space-page .bili-footer, body.bili-space-page .international-footer, body.bili-space-page .mini-footer, body.bili-space-page .space-footer, body.bili-space-page .go-top, body.bili-space-page .bili-go-top, body.bili-space-page [class*="back-top"], body.bili-space-page [class*="goto-top"], body.bili-space-page [class*="to-top"], body.bili-space-page .fixed-sidebar, body.bili-space-page .bili-fixed-sidebar, body.bili-space-page .side-toolbar, body.bili-space-page .right-sidebar-wrap, body.bili-space-page .space-float { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; } body.bili-space-page .toutu.header-toutu { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-space-page .header.space-header { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; overflow: hidden !important; } body.bili-space-page .upinfo-detail__top .level, body.bili-space-page .upinfo-detail__top a.level, body.bili-space-page .upinfo-detail__top .vip, body.bili-space-page .upinfo-detail__top .img-label, body.bili-space-page .upinfo-detail__bottom, body.bili-space-page .upinfo-sign, body.bili-space-page .upinfo-sign__text, body.bili-space-page .interactions > a.message-btn, body.bili-space-page .interactions > .more-actions, body.bili-space-page .interactions > .menu-popover.message, body.bili-space-page .interactions > .v-popover-wrap.message-entry { display: none !important; visibility: hidden !important; } body.bili-space-page .nav-search.nav-bar-search, body.bili-space-page label.nav-search, body.bili-space-page input.nav-search-input, body.bili-space-page .nav-bar__main-right, body.bili-space-page .nav-statistics { display: none !important; visibility: hidden !important; } /* v131.26 导航条高度 64 → 52,与顶部自建搜索栏 #my-bili-searchbar 等高。 实测:#my-bili-searchbar 360x52;.nav-bar.space-navbar 360x64 (内部 .nav-bar__main / .nav-tab / a.nav-tab__item 都是 64,被 flex 拉伸)。 只压外层 + 内层容器的 height,icon(20x21) 与文字本身没变, 所以视觉上只是上下留白收紧 12px(各 6px),图标文字不受影响。 */ body.bili-space-page .nav-bar.space-navbar { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 52px !important; max-height: 52px !important; overflow: hidden !important; box-sizing: border-box !important; margin-left: 0 !important; margin-right: 0 !important; margin-top: 0 !important; margin-bottom: 0 !important; padding: 0 8px !important; /* v131.33:吸顶位置定死在自建搜索栏正下方(搜索栏高 52), 否则站内 PC 的 top 值会让它在手机上错位、把胶囊栏顶到搜索框底下。 */ position: sticky !important; top: 52px !important; z-index: 1000 !important; background: #ffffff !important; } /* ------------------------------------------------------------ v131.25 UP主空间页→搜索页:隐藏「主页 动态 投稿 合集和系列」这一行 结构:#app > div.nav-bar.space-navbar[360x64](main 的前置兄弟节点) #app > main.space-main.route_search CSS 没有"选前面的兄弟"的选择器,所以用 :has() 从父级反查: #app:has(> main.route_search) > .nav-bar.space-navbar nav-bar 原生是 sticky,这里只写 display:none(不动 position), 收掉后它不再占位,下方内容自然顶上来,不留 64px 空隙。 ------------------------------------------------------------ */ body.bili-space-page #app:has(> main.space-main.route_search) > .nav-bar.space-navbar, body.bili-space-page #app:has(> main.space-main.route_search) > .header.space-header ~ .nav-bar.space-navbar, body.bili-space-page:has(main.space-main.route_search) .nav-bar.space-navbar { display: none !important; visibility: hidden !important; width: 0 !important; max-width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* v131.36 UP主空间页→搜索页:隐藏 .header.space-header。 探针里它是 360x56(原为头像+昵称+关注),但脚本早已把内部内容 (头像/昵称/等级/签名/按钮等)逐个隐藏,只剩一个 56px 的空壳 —— 用户 截图确认"这一部分就是不存在"。 它的存在把胶囊栏整体推高了 56px: 搜索框底 52 → 空壳 52..108 → 胶囊栏 108..158 于是无论把间距放在胶囊栏的 padding 还是 margin 上, 白色胶囊 pill 的位置都是 116,肉眼看起来始终"紧贴搜索框"。 收掉空壳后:搜索框底 52 → 胶囊栏 60..110 → pill 顶 60,间距正好 8。 v131.40 这行改为「全部 UP主空间页都隐藏」(原先只对搜索页生效)。 原因是 v131.39 修掉了 #app 的 margin-top:-64px 之后,这行才第一次 真正露出来 —— 之前它被那 -64 顶到 top=-12..44,几乎整块压在固定 搜索栏(0..52)底下,所以你一直"看不到它"(v131.36 时你说得没错)。 现在归位到 52..108 就完整显示了,而里面的头像 .space-avatar 是 68x68、top=35,会往上戳进搜索栏区域被切掉一截 —— 正是你看到的 "被搜索栏遮挡了一部分"。 用户要求:这一行整体隐藏。于是直接收掉,不做显示。 */ body.bili-space-page .header.space-header, body.bili-space-page #app > .header.space-header, body.bili-space-page #app:has(> main.space-main.route_search) > .header.space-header, body.bili-space-page:has(main.space-main.route_search) .header.space-header { display: none !important; visibility: hidden !important; width: 0 !important; max-width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } body.bili-space-page .nav-bar__main { display: flex !important; align-items: center !important; justify-content: center !important; flex-wrap: nowrap !important; gap: 0 !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 52px !important; max-height: 52px !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* .nav-tab / .nav-bar__main-left / 各 tab 项同压到 52(v131.26) */ body.bili-space-page .nav-bar__main-left, body.bili-space-page .nav-tab { height: 52px !important; max-height: 52px !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* v131.27 按键组占满一整行,且「按键间距 = 左右距屏幕边缘」 实测(09-04-40 探针):.nav-bar__main[344] > .nav-bar__main-left[274] > .nav-tab[274] ← 只到 274,右侧空 70 原因是 main-left 为 flex:0 1 auto / width:auto、nav-tab 也是 width:auto, 两者都只按内容宽度收缩,于是整组按键缩在中间。 改法:main-left 与 nav-tab 全部撑满 100%,tab 项改 flex:1 1 auto 均分余量。 算下来:内容区 344(nav-bar 左右各 8)− gap 8×3 = 24 − 按键自然宽 (52+52+52+94=250) = 70,4 个按键各增 17.5 → 69.5/69.5/69.5/111.5, 合计 320 + 24 = 344 正好满行;左右各留 8、按键间距 8,三者相等。 */ body.bili-space-page .nav-tab > a.nav-tab__item { height: 52px !important; max-height: 52px !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; display: flex !important; align-items: center !important; justify-content: center !important; flex: 1 1 auto !important; width: auto !important; min-width: 0 !important; max-width: none !important; } body.bili-space-page .nav-bar__main-left { display: flex !important; align-items: center !important; justify-content: center !important; flex: 1 1 auto !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; overflow: visible !important; } body.bili-space-page .nav-tab { display: flex !important; align-items: center !important; justify-content: flex-start !important; gap: 8px !important; white-space: nowrap !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-space-page .radio-filter { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; overflow: hidden !important; padding: 0 !important; margin: 0 !important; } body.bili-space-page button.playall-btn, body.bili-space-page .vui_button.playall-btn, body.bili-space-page .action-btn.playall-btn, body.bili-space-page [class*="playall"] { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; overflow: hidden !important; padding: 0 !important; margin: 0 !important; } body.bili-space-page #app, body.bili-space-page main.space-main, body.bili-space-page .space-home { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; display: block !important; } body.bili-space-page .space-home > .aside, body.bili-space-page .space-home > aside.aside, body.bili-space-page main.space-main .aside { display: none !important; visibility: hidden !important; width: 0 !important; max-width: 0 !important; min-width: 0 !important; height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; flex: 0 0 0 !important; position: absolute !important; left: -9999px !important; } body.bili-space-page .space-home > .content { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; flex: 1 1 100% !important; /* v131.41:底部 padding 16 -> 0(原为给页脚留的,页脚已隐藏) */ padding: 0 8px 0 8px !important; margin: 0 !important; box-sizing: border-box !important; } /* ------------------------------------------------------------ v132.42 主页「置顶视频」文字右侧冲出屏幕被硬裁 → 改为省略号 探针(视口 360)实测: .top-video left=8 w=344 .top-video__cover left=8 w=110 .top-video__info left=118 w=355 ← 右边界 473,冲出屏幕 113 .top-video__title left=130 w=343 ← 右边界 473,overflow:hidden 硬裁 .top-video__desc left=130 w=343 ← 同上 .top-video__stats left=130 w=343 ← 同上 即:info 沿用了 PC 定宽(355),而父容器只有 344 且左边已被 封面吃掉 110(+12 内边距),剩余可用不足 226px, 整块文字溢到屏幕外,被 .top-video 的 overflow 与屏幕边缘 直接切掉,看不到任何省略号。 改法:.top-video 改 flex 并锁死满宽,封面钉 110, info 用 flex:1 + min-width:0 吃剩余空间(不再有 355 定宽), 标题沿用站内两行截断并补 ellipsis,描述单行 ellipsis。 ------------------------------------------------------------ */ body.bili-space-page .top-video { display: flex !important; flex-wrap: nowrap !important; /* v132.43:封面与文字整体垂直居中(见下方说明) */ align-items: center !important; gap: 0 !important; column-gap: 0 !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; overflow: hidden !important; } body.bili-space-page .top-video > .top-video__cover { flex: 0 0 110px !important; width: 110px !important; min-width: 110px !important; max-width: 110px !important; align-self: center !important; margin: 0 !important; } body.bili-space-page .top-video > .top-video__info { flex: 1 1 auto !important; width: auto !important; max-width: calc(100% - 110px) !important; min-width: 0 !important; align-self: center !important; margin: 0 !important; padding-left: 12px !important; padding-right: 0 !important; box-sizing: border-box !important; overflow: hidden !important; } /* ------------------------------------------------------------ v132.43 置顶区「封面在左上、文字独立」→ 两者在区块内垂直居中 探针(修复后,视口 360)实测: .top-video top=134 bottom=235 (高 101) .top-video__cover top=134 bottom=196 (高 62) ← 贴着顶边 .top-video__info top=134 bottom=235 (高 101) ← 撑满整块 即:标题改两行后文字块高 101,封面仍是 62 且被上一版写的 align-items:flex-start 钉在顶边 → 视觉上封面"吊"在左上角, 与右侧三行文字(标题 56 + 数据 17 + 描述 20)的中线对不齐。 改法:align-items 改 center,并给两个子级补 align-self:center (防站内对自己写 stretch / start 抢回去)。 区块上下留白本来就是对称的(上方 space-home 的 30px、 下方 section-wrap 的 padding-bottom 24px),故不动外间距。 ------------------------------------------------------------ */ /* 标题:保留站内两行截断,并在第二行末尾给出省略号 */ body.bili-space-page .top-video .top-video__title { display: -webkit-box !important; -webkit-box-orient: vertical !important; -webkit-line-clamp: 2 !important; line-clamp: 2 !important; width: 100% !important; max-width: 100% !important; white-space: normal !important; word-break: break-word !important; overflow: hidden !important; text-overflow: ellipsis !important; } /* 描述(更新说明 / 网盘链接等):单行省略号 */ body.bili-space-page .top-video .top-video__desc { display: block !important; width: 100% !important; max-width: 100% !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; } /* 播放 / 弹幕 / 日期 一行:同样不许撑出容器 */ body.bili-space-page .top-video .top-video__stats { width: 100% !important; max-width: 100% !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; } /* ------------------------------------------------------------ v131.45 首页「视频 ·N 查看更多」标题行:上间距改为与下间距一致 探针(14-37-19)实测坐标: 上一区块(代表作)bottom = -6624 .section-wrap__header top = -6600 ← 上间距 = 24 .section-wrap__header bottom = -6566 .section-wrap__content top = -6550 ← 下间距 = 16(卡片顶) 即:上 24 / 下 16,不一致。 上间距 24 来自区块之间的 margin-bottom(24); 下间距 16 来自 header 与 content 之间的原生留白。 改法:给整个 video-section 区块加 margin-top: -8px, 把整块上提 8px(header 与 content 的相对位置不变,仍是 16), 于是上间距 24-8 = 16 = 下间距 16,两边一致。 用 :has() 定位而不写 nth-of-type,区块顺序变了也不会错位。 ------------------------------------------------------------ */ body.bili-space-page .route_home .home-section:has(> .section-wrap.video-section) { margin-top: -8px !important; } /* ------------------------------------------------------------ v131.41 首页滑到最底部:去掉那条没用的分割线及其下方的空白 探针(14-37-19,已滚到底)实测: 最后一张专栏卡 bottom = 735 最后一个 section-wrap bottom = 760 ← 分割线在这一带 .content bottom = 800 ← 文档底 即分割线下方还有 40px 空白,构成为: 最后一个 home-section 的 margin-bottom 24(区块间距,底部不需要) + .content 的 padding-bottom 16(页脚占位,页脚已隐藏) 用户要求:分割线去掉、分割线下方的高度去掉、分割线上方的高度保留。 故这里只清「分割线本身 + 下方 40px」, section-wrap 内部那 25px(分割线上方)原样保留。 ------------------------------------------------------------ */ body.bili-space-page .route_home .home-section:last-of-type, body.bili-space-page .route_home .content > .home-section:last-child { margin-bottom: 0 !important; border-bottom: none !important; } body.bili-space-page .route_home .home-section:last-of-type .section-wrap, body.bili-space-page .route_home .content > .home-section:last-child .section-wrap { margin-bottom: 0 !important; border-bottom: none !important; box-shadow: none !important; } /* 分割线本身:清掉最后一个区块的伪元素与背景线 */ body.bili-space-page .route_home .home-section:last-of-type .section-wrap::after, body.bili-space-page .route_home .home-section:last-of-type .section-wrap::before, body.bili-space-page .route_home .content > .home-section:last-child .section-wrap::after, body.bili-space-page .route_home .content > .home-section:last-child .section-wrap::before { content: none !important; display: none !important; border: none !important; background: none !important; box-shadow: none !important; } /* .content / .space-home 底部若还挂着一条线,一并清掉 */ body.bili-space-page .route_home .content, body.bili-space-page .route_home .space-home { border-bottom: none !important; box-shadow: none !important; background-image: none !important; } body.bili-space-page .route_home .content::after, body.bili-space-page .route_home .space-home::after, body.bili-space-page .route_home .content::before, body.bili-space-page .route_home .space-home::before { content: none !important; display: none !important; border: none !important; background: none !important; box-shadow: none !important; } /* v132.55:同上 —— 排除灯箱,否则空间页点「查看大图」是空的 */ body.bili-space-page img:not([class*="pswp"]), body.bili-space-page video:not([class*="pswp"]), body.bili-space-page picture:not([class*="pswp"]) { max-width: 100% !important; height: auto !important; } body.bili-space-page .masterpiece-block, body.bili-space-page .items, body.bili-space-page .video-list__content, body.bili-space-page .article-list { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; grid-gap: 8px !important; gap: 8px !important; row-gap: 8px !important; column-gap: 8px !important; padding: 0 !important; margin: 0 !important; width: 100% !important; max-width: 100% !important; box-sizing: border-box !important; list-style: none !important; align-items: start !important; align-content: start !important; background: transparent !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; grid-auto-rows: auto !important; } body.bili-space-page .items > .items__item, body.bili-space-page .masterpiece-block > .masterpiece-block__item, body.bili-space-page .video-list__content > .video-list__item, body.bili-space-page .article-list > .article-list__item { display: contents !important; } /* ------------------------------------------------------------ 主页「合集·xxx / 系列·xxx」区块:标题行与视频卡片之间固定 20px 上面把 .video-list__content 的 margin/padding 全部归零, 刚进主页时站内那条原生顶部间距还没补上,文字就会紧贴卡片; 点一次「合集和系列」再切回来,站内样式补上了才恢复正常。 这里把间距统一交给 content 的 margin-top, 并清掉 header 的下边距与容器的 gap,避免两处叠加成双倍。 实测:修复前 .video-list 高 589(34+531+24), 正常态 609(多出的 20 就是这条间距)。 ------------------------------------------------------------ */ body.bili-space-page .route_home .lists-section .video-list { display: block !important; gap: 0 !important; row-gap: 0 !important; column-gap: 0 !important; padding-top: 0 !important; padding-bottom: 0 !important; } body.bili-space-page .route_home .lists-section .video-list__header { margin-top: 0 !important; margin-bottom: 0 !important; padding-bottom: 0 !important; } body.bili-space-page .route_home .lists-section .video-list__content { margin-top: 20px !important; padding-top: 0 !important; } /* ------------------------------------------------------------ v131.20 UP主空间内搜索页(route_search,空间右上角搜索进来的结果页) 原结构:.space-search[360] 是 flex-row > .search-sidebar[150 宽,纵向侧栏] > .side-nav > .side-nav__item[150x52] > .search-content[736(PC 定宽)] > .video > .video-body > .video-list.grid-mode[736] > .upload-video-card[364x282] 改法:sidebar 从左侧纵向栏改成顶部横向胶囊(同投稿页 .upload-sidenav / 动态页 .space-dynamic__left),content 满宽后卡片自然回到 168x172。 ------------------------------------------------------------ */ body.bili-space-page .space-search { display: flex !important; flex-direction: column !important; align-items: stretch !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; gap: 0 !important; } /* 「视频 328 / 动态 357」移到「主页 动态 投稿 合集和系列」下方,横向胶囊 */ body.bili-space-page .search-sidebar { order: 1 !important; display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; max-height: none !important; /* v131.35:同投稿页,间距改用容器外 margin-top 8, padding-top 归零。搜索页紧贴胶囊栏上方的是 .header.space-header (头像+昵称+已关注,56px),它底边 108 → 容器顶 116 → 胶囊顶 116, 容器间距与视觉间距都是 8。 */ padding: 0 8px 8px 8px !important; margin: 8px 0 0 0 !important; box-sizing: border-box !important; /* v131.34:不吸顶,随页面滚走,不遮挡最上面的卡片 v131.37:改用 relative 并给低层级—— relative 不改变文档流位置(仍随页面滚动), 但能确保它参与正常的层叠顺序,不会被背景层压住。 */ position: relative !important; top: auto !important; left: auto !important; right: auto !important; bottom: auto !important; z-index: 1 !important; flex: 0 0 auto !important; overflow: visible !important; background: #f1f2f3 !important; } body.bili-space-page .search-sidebar .side-nav { display: flex !important; flex-direction: row !important; justify-content: center !important; align-items: center !important; gap: 8px !important; width: 100% !important; max-width: 100% !important; padding: 0 !important; margin: 0 auto !important; box-sizing: border-box !important; flex-wrap: wrap !important; position: static !important; } body.bili-space-page .search-sidebar .side-nav__item { display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: center !important; flex: 0 0 auto !important; width: auto !important; min-width: 64px !important; max-width: 100% !important; height: 34px !important; padding: 0 16px !important; margin: 0 !important; box-sizing: border-box !important; border-radius: 17px !important; background: #ffffff !important; color: #18191c !important; font-size: 13px !important; white-space: nowrap !important; position: static !important; } body.bili-space-page .search-sidebar .side-nav__item.active { background: #00aeec !important; color: #ffffff !important; } body.bili-space-page .search-sidebar .side-nav__item__main, body.bili-space-page .search-sidebar .side-nav__item__sub { display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: center !important; gap: 4px !important; width: auto !important; height: auto !important; padding: 0 !important; margin: 0 !important; background: transparent !important; } body.bili-space-page .search-sidebar .side-nav__item__main-text { color: inherit !important; font-size: 13px !important; font-weight: 500 !important; line-height: 34px !important; } body.bili-space-page .search-sidebar .side-nav__item__sub-text { color: inherit !important; font-size: 13px !important; font-weight: 400 !important; line-height: 34px !important; opacity: 0.8 !important; } body.bili-space-page .search-content { order: 2 !important; display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; flex: 1 1 100% !important; padding: 0 8px 16px 8px !important; margin: 0 !important; box-sizing: border-box !important; overflow: visible !important; position: static !important; } /* ===== v132.86:空间内搜索页(route_search)分页条上下间距统一 16px ===== 探针实测(bili_probe_2026-10-03_13-38-05,视口 360,已滚到底): .search-content [360x1869] top=-1069 bottom=**800** pb=**16** └─ .video [344x1853] top=-1069 bottom=**784** ├─ .video-body [344x1787] bottom=**718** ← 末行卡片底 └─ .video-footer [344x**66**] top=718 bottom=784 pt=16 pb=16 └─ .vui_pagenation [344x34] └─ --btns [344x34] top=**734** bottom=**768** ← 按键本体 算现状: 上间距 = 按键顶 734 − 末行卡片底 718 = 16 ✓(已经对了) 下间距 = 按键底 768 → .video 底 784 = 16 再 + .search-content 的 pb 16 → 内容真正结束在 **800** = **32**,是上面的两倍 —— 这就是"下边比上边宽"的来源。 多出来的 16 是脚本自己 v131.20 那条 .search-content { padding: 0 8px 16px 8px } 加的底部留白, 与 footer 自己的 pb 16 叠加了。 修法(沿用 v132.77 的思路:间距收归一处,不再分摊): ① .video-footer 显式 padding:16px 0 + margin:0 —— 上下间距由它一个负责; ② .search-content 的 padding-bottom 归零 —— 去掉叠加的那 16; ③ footer 内分页器的 padding 归零 —— 防站内哪天又给它加 padding, 把 16 顶成 24。 改后几何:按键 734~768,.video 底 784,.search-content 底 784 → 上 16、下 16,对称。 ⚠ 只作用于 route_search(空间内搜索),不动全站搜索页 (body.bili-search-page .search-content 有自己的一套,别处验证过)。 */ body.bili-space-page main.space-main.route_search .search-content, body.bili-space-page main.space-main.route_search > .space-search > .search-content { padding-bottom: 0 !important; /* ← 去掉与 footer pb 叠加的那 16 */ } body.bili-space-page main.space-main.route_search .video-footer, body.bili-space-page main.space-main.route_search > .space-search .video > .video-footer { padding: 16px 0 !important; /* ← 上下间距由这一个值决定 */ margin: 0 !important; box-sizing: border-box !important; } /* footer 内的分页器本体:padding 归零,防 16 被顶成 24 */ body.bili-space-page main.space-main.route_search .video-footer .vui_pagenation, body.bili-space-page main.space-main.route_search .video-footer [class*="pagenation"] { padding: 0 !important; margin: 0 !important; } /* 视频结果:容器满宽 + 两列网格,卡片回到 168x172(同投稿页) */ body.bili-space-page .space-search .video, body.bili-space-page .space-search .video-header, body.bili-space-page .space-search .video-header__top, body.bili-space-page .space-search .video-header .title, body.bili-space-page .space-search .video-header .sub-title, body.bili-space-page .space-search .video-body { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; } /* v131.33 搜索页:整行移除「共找到关于"宝可梦"的 N 个视频 / 条动态」 (v131.31/32 只收了 .title,sub-title 保留;这版连 header 整块收掉) 实测:.video-header[344x20] / .dynamic-header[344x70]。 收掉后胶囊与卡片之间只剩 sidenav 自带的 padding-bottom 8 → 正好 8px。 */ body.bili-space-page .space-search .video-header, body.bili-space-page .space-search .video-header__top, body.bili-space-page .space-search .video-header .title, body.bili-space-page .space-search .video-header .sub-title, body.bili-space-page .space-search .dynamic-header, body.bili-space-page .space-search .dynamic-header__top, body.bili-space-page .space-search .dynamic-header .title, body.bili-space-page .space-search .dynamic-header .sub-title { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* 胶囊底 → 卡片顶 = sidenav padding-bottom 8 + 0 = 8 */ body.bili-space-page .space-search .video-body, body.bili-space-page .space-search .dynamic-content { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin-top: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; margin-right: 0 !important; padding: 0 !important; box-sizing: border-box !important; } body.bili-space-page .space-search .video-list, body.bili-space-page .space-search .video-list.grid-mode, body.bili-space-page .space-search .video-list.hide-stat { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; grid-gap: 8px !important; gap: 8px !important; row-gap: 8px !important; column-gap: 8px !important; align-items: start !important; align-content: start !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; overflow: visible !important; } body.bili-space-page .space-search .upload-video-card, body.bili-space-page .space-search .upload-video-card.grid-mode, body.bili-space-page .space-search .upload-video-card__left, body.bili-space-page .space-search .upload-video-card__main { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* 动态结果:与动态页一致(.search-content 已有 8px 边距, 这里把 item 的左右内边距归零,避免叠加成 16px) v131.21 修正:搜索页动态用的是 .dynamic-list(不是动态页的 .bili-dyn-list), 而且是 PC 定宽 720(.dynamic-list[720] > .dynamic-list__item[720x670]), 在 360 屏上直接溢出。这里把整条链压回 100%。 */ body.bili-space-page .space-search .dynamic, body.bili-space-page .space-search .dynamic-header, body.bili-space-page .space-search .dynamic-content, body.bili-space-page .space-search .dynamic-list, body.bili-space-page .space-search .dynamic-list__item, body.bili-space-page .space-search .bili-dyn-list, body.bili-space-page .space-search .bili-dyn-list__items, body.bili-space-page .space-search .bili-dyn-item, body.bili-space-page .space-search .bili-dyn-item__main { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-space-page .space-search .dynamic-list__item, body.bili-space-page .space-search .bili-dyn-list__item { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 0 8px 0 !important; margin: 0 !important; box-sizing: border-box !important; } /* 720 定宽带来的横向滚动也要掐掉,否则 360 屏会被撑开 */ body.bili-space-page .space-search .dynamic-content, body.bili-space-page .space-search .dynamic-list { display: block !important; overflow-x: hidden !important; padding-left: 0 !important; padding-right: 0 !important; margin-left: 0 !important; margin-right: 0 !important; } body.bili-space-page .space-search .dynamic-list__item .bili-dyn-item, body.bili-space-page .space-search .dynamic-list__item .bili-dyn-item__main, body.bili-space-page .space-search .dynamic-list__item .bili-dyn-item__header, body.bili-space-page .space-search .dynamic-list__item .bili-dyn-item__body, body.bili-space-page .space-search .dynamic-list__item .bili-dyn-item__footer { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; margin-left: 0 !important; margin-right: 0 !important; } /* ------------------------------------------------------------ v131.23 底部分页器(投稿→视频 / 空间搜索→视频)对齐主搜索页 两者对比(都在第 1 页): 主搜索页 .vui_pagenation[360x50] > .vui_pagenation--btns[360x34] 上一页 61x34 | 数字 32x34 | 下一页 61x34 | 无跳转区 空间页 .vui_pagenation--jump.video-pagination[736x34] ← PC 定宽溢出 > .vui_pagenation--btns[478x34] 数字 34x34 | 下一页 100x34 > .vui_pagenation-go[214x34] 「共 16 页 / 618 个,跳至 __ 页」 改法:容器压回 100% 并居中,按钮套用主搜索页同一组尺寸, 跳转区整块隐藏(.vui_pagenation-go)。 v131.24 补:上下间距对齐主搜索页。 主搜索页实测:外层 .flex_center.mt_x50.mb_x50[360x50], 脚本 870 行已把它的 margin 上下压成 8px;分页器 padding 8 0 + btns 34 = 50。 父容器 div[360x3854] = 列表 3788 + 50 + 8 + 8,正好相等 → 父容器没有额外的 padding-bottom。 所以主搜索页的视觉间距是:卡片底 → 8+8 = 16 → 按钮 → 8+8 = 16 → 容器底。 空间页:.video-footer 原生 154 高(= 分页器 34 + 上下各 60), 且父容器 .upload-content / .search-content 自带 padding-bottom 16 (实测 .upload-content[360x3758] − .video[344x3742] = 16)。 若不抵消,底部会变成 8 + 8 + 16 = 24,比主搜索页多 8px。 故 .video-footer 取 margin: 8px 0 -8px(下边 -8 抵消父容器多出的 8), 分页器保持 padding: 8px 0 → 上下视觉间距各 16px,与主搜索页一致。 ------------------------------------------------------------ */ /* 外层:v132.77 改为「间距全由 footer 自己负责」—— 上 16 / 下 16。 旧写法是 mt 8 + 分页器 pt 8 = 16、mb -8 + 分页器 pb 8 + 父容器 pb 16 = 16, 靠三处分摊。新探针(投稿→视频页)实测这套已经散了: .video-footer [344x**34**] mt=8 mb=-8 ← 不是预期的 50 .vui_pagenation--jump [344x34] 与 --btns [344x34] 完全重合 → 分页器那 8px padding 没生效(高度 34 而非 34+8+8=50), 于是上间距只剩 mt 8、下间距只剩 mb(-8)+父容器 pb 16 = 8, 两边各少 8px,比用户要的 16 小了一半。 新做法把间距收归 footer 一个元素,并把父容器与分页器的 padding 全部清零,避免"谁生效谁不生效"的叠加不确定性: footer padding 16px 0 + margin 0,父容器 / 分页器 pb 全归零 ⇒ 上 = 16、下 = 16,两侧完全对称且只由一个值决定。 */ body.bili-space-page .video-footer, body.bili-space-page main.space-main.route_uploadVideo > .space-upload > .upload-content > .video > .video-footer { display: flex !important; flex-direction: row !important; justify-content: center !important; align-items: center !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; padding: 16px 0 !important; margin: 0 !important; box-sizing: border-box !important; overflow: visible !important; background: transparent !important; } /* 父容器底部的 16px 交给 footer 的 padding-bottom,避免叠加成 32 */ body.bili-space-page main.space-main.route_uploadVideo .upload-content, body.bili-space-page main.space-main.route_uploadVideo .video { padding-bottom: 0 !important; } /* 分页器本体不再参与间距(旧 pt/pb 8 已实测失效,这里显式归零, 防它哪天又生效把 16 顶成 24) */ body.bili-space-page .video-footer .vui_pagenation, body.bili-space-page .video-footer .vui_pagenation--jump, body.bili-space-page .video-footer .vui_pagenation--jump.video-pagination { padding: 0 !important; margin: 0 !important; } /* v131.49 「合集和系列」页底部分页器:上下间距与「投稿→视频」页完全一致 两份探针同批实测(09-28_17-45-47 = 投稿→视频,09-28_17-45-34 = 合集和系列): 投稿→视频(基准,已达标) .video-body bottom=3736 ← 最后一张卡片底 .video-footer top=3744 h=50 pt=0 pb=0 mt=8 mb=-8 .video-pagination pt=8 pb=8 .vui_pagenation--btns 3752 → 3786 .upload-content pb=16 → main bottom=3802 ⇒ 卡片底→按钮顶 = 3752-3736 = 16 ⇒ 按钮底→页面底 = 3802-3786 = 16 合集和系列(改前) .lists-content bottom=11891 ← 最后一张卡片底 .lists-footer top=11891 h=110 **pt=60** pb=0 mt=0 mb=0 .audio-list-paginaion pt=8 pb=8(已被上面 .vui_pagenation 通配规则压好) .vui_pagenation--btns 11959 → 11993 .space-lists pb=16 → main bottom=12017 ⇒ 卡片底→按钮顶 = 11959-11891 = 68(多 52,全来自 pt=60) ⇒ 按钮底→页面底 = 12017-11993 = 24(多 8,因为没有 mb=-8 抵消) 两页的分页器本体、父容器 padding-bottom 16 都已经相同, 差异 100% 出在 footer 这一层。所以直接照抄 .video-footer 那组已验证过的参数(padding 0 + margin 8px 0 -8px): 上 = mt 8 + pagination pt 8 = 16 ✓ 下 = pagination pb 8 + mb(-8) + space-lists pb 16 = 16 ✓ ------------------------------------------------------------ */ body.bili-space-page .lists-footer { display: flex !important; flex-direction: row !important; justify-content: center !important; align-items: center !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; padding: 0 !important; margin: 8px 0 -8px 0 !important; box-sizing: border-box !important; overflow: visible !important; background: transparent !important; } /* 高特异性兜底(v131.36 套路),防站内晚加载的同权重 !important */ body.bili-space-page main.space-main.route_listsIndex > .space-lists > .lists-footer { padding: 0 !important; margin: 8px 0 -8px 0 !important; } /* 分页器本体:与主搜索页同款 padding 8px 0 */ body.bili-space-page .vui_pagenation, body.bili-space-page .vui_pagenation--jump, body.bili-space-page .vui_pagenation--jump.video-pagination { display: flex !important; flex-direction: row !important; justify-content: center !important; align-items: center !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; padding: 8px 0 !important; margin: 0 !important; box-sizing: border-box !important; overflow: visible !important; background: transparent !important; } /* 「共 N 页 / N 个,跳至 __ 页」整块隐藏 */ body.bili-space-page .vui_pagenation-go, body.bili-space-page .vui_pagenation--jump .vui_pagenation-go, body.bili-space-page .vui_pagenation .vui_input { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* 按钮行:与主搜索页同款(全宽、gap 4、可横向滚动、隐藏滚动条) */ body.bili-space-page .vui_pagenation--btns { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; justify-content: flex-start !important; align-items: center !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 8px !important; margin: 0 !important; box-sizing: border-box !important; gap: 4px !important; overflow-x: auto !important; overflow-y: hidden !important; -webkit-overflow-scrolling: touch !important; scrollbar-width: none !important; -ms-overflow-style: none !important; } body.bili-space-page .vui_pagenation--btns::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; } /* 单个按钮:数字 32x34、字号 13、圆角 4,与主搜索页一致 */ body.bili-space-page .vui_pagenation--btn { display: inline-flex !important; align-items: center !important; justify-content: center !important; flex-shrink: 0 !important; flex-grow: 0 !important; width: auto !important; min-width: 32px !important; height: 34px !important; line-height: 34px !important; padding: 0 6px !important; margin: 0 !important; box-sizing: border-box !important; white-space: nowrap !important; font-size: 13px !important; text-align: center !important; border-radius: 4px !important; } /* 上一页 / 下一页:空间页原生 100x34,压回主搜索页的 61 宽 */ body.bili-space-page .vui_pagenation--btn-side { min-width: 44px !important; padding: 0 10px !important; font-size: 13px !important; } body.bili-space-page .space-dynamic { display: flex !important; flex-direction: column !important; align-items: stretch !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; gap: 0 !important; } body.bili-space-page .space-dynamic__left { order: 1 !important; display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; max-height: none !important; padding: 8px !important; margin: 0 !important; box-sizing: border-box !important; position: static !important; top: auto !important; left: auto !important; bottom: auto !important; right: auto !important; background: #f1f2f3 !important; flex: 0 0 auto !important; overflow: visible !important; } body.bili-space-page .space-dynamic__left .side-nav { display: flex !important; flex-direction: row !important; justify-content: center !important; align-items: center !important; gap: 8px !important; width: 100% !important; max-width: 100% !important; padding: 0 !important; margin: 0 auto !important; box-sizing: border-box !important; flex-wrap: wrap !important; position: static !important; } body.bili-space-page .space-dynamic__left .side-nav__item { display: flex !important; align-items: center !important; justify-content: center !important; flex: 0 0 auto !important; width: auto !important; min-width: 64px !important; max-width: 100% !important; height: 34px !important; padding: 0 16px !important; margin: 0 !important; box-sizing: border-box !important; border-radius: 17px !important; background: #ffffff !important; color: #18191c !important; font-size: 13px !important; cursor: pointer !important; user-select: none !important; white-space: nowrap !important; position: static !important; } body.bili-space-page .space-dynamic__left .side-nav__item.active { background: #00aeec !important; color: #ffffff !important; } body.bili-space-page .space-dynamic__left .side-nav__item__main { display: flex !important; align-items: center !important; justify-content: center !important; gap: 4px !important; width: auto !important; height: auto !important; padding: 0 !important; margin: 0 !important; background: transparent !important; } body.bili-space-page .space-dynamic__left .side-nav__item__main-text { color: inherit !important; font-size: 13px !important; font-weight: 500 !important; line-height: 34px !important; } body.bili-space-page .space-dynamic__content { order: 2 !important; display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; flex: 1 1 100% !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; overflow: visible !important; } body.bili-space-page .space-dynamic__right { display: none !important; visibility: hidden !important; width: 0 !important; max-width: 0 !important; height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; flex: 0 0 0 !important; position: absolute !important; left: -9999px !important; } body.bili-space-page .space-dynamic-inner, body.bili-space-page .space-dynamic-inner > .content, body.bili-space-page .bili-dyn-list, body.bili-space-page .bili-dyn-list__items { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-space-page .bili-dyn-list__item { padding: 0 8px 8px 8px !important; margin: 0 !important; box-sizing: border-box !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; float: none !important; } body.bili-space-page .bili-dyn-item { position: relative !important; background: #ffffff !important; border: 1px solid #e3e5e7 !important; border-radius: 8px !important; padding: 12px 8px 0 8px !important; margin: 0 !important; box-sizing: border-box !important; overflow: hidden !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; display: block !important; float: none !important; } body.bili-space-page .bili-dyn-item * { max-width: 100% !important; box-sizing: border-box !important; } body.bili-space-page .bili-dyn-item img, body.bili-space-page .bili-dyn-item video, body.bili-space-page .bili-dyn-item picture { max-width: 100% !important; height: auto !important; } body.bili-space-page .bili-dyn-item__main { position: relative !important; padding-left: 0 !important; padding-right: 0 !important; box-sizing: border-box !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; float: none !important; } body.bili-space-page .bili-dyn-item__avatar { position: absolute !important; left: 0 !important; top: 0 !important; width: 56px !important; max-width: 56px !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-space-page .bili-dyn-item__avatar .bili-dyn-avatar, body.bili-space-page .bili-dyn-item__avatar .b-avatar, body.bili-space-page .bili-dyn-item__avatar img { width: 48px !important; height: 48px !important; max-width: 100% !important; border-radius: 50% !important; } body.bili-space-page .bili-dyn-item__header { margin-left: 0 !important; margin-right: 0 !important; padding-left: 64px !important; padding-right: 8px !important; width: auto !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-space-page .bili-dyn-item__more, body.bili-space-page .bili-dyn-more, body.bili-space-page .bili-dyn-more__btn, body.bili-space-page .bili-dyn-more__cascader { display: none !important; visibility: hidden !important; } body.bili-space-page .bili-dyn-content, body.bili-space-page .bili-dyn-content__orig, body.bili-space-page .bili-dyn-content__orig__major { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-space-page .bili-dyn-card-video { display: flex !important; flex-direction: column !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; box-sizing: border-box !important; } body.bili-space-page .bili-dyn-card-video__header { width: 100% !important; max-width: 100% !important; height: auto !important; flex: 0 0 auto !important; box-sizing: border-box !important; } body.bili-space-page .bili-dyn-card-video__cover { width: 100% !important; max-width: 100% !important; height: auto !important; aspect-ratio: 16 / 9 !important; box-sizing: border-box !important; } body.bili-space-page .bili-dyn-card-video__cover .b-img, body.bili-space-page .bili-dyn-card-video__cover .b-img__inner, body.bili-space-page .bili-dyn-card-video__cover img, body.bili-space-page .bili-dyn-card-video__cover picture { width: 100% !important; max-width: 100% !important; height: auto !important; aspect-ratio: 16 / 9 !important; object-fit: cover !important; display: block !important; } body.bili-space-page .bili-dyn-card-video__body { width: 100% !important; max-width: 100% !important; height: auto !important; flex: 1 1 auto !important; padding: 8px 0 0 0 !important; box-sizing: border-box !important; } /* 投稿了视频:隐藏视频封面下方「框住标题/简介文字」的那条左侧半开口边框 */ body.bili-space-page .bili-dyn-card-video__body, body.bili-space-page .bili-dyn-card-video__title, body.bili-space-page .bili-dyn-card-video__desc { border: none !important; border-top: none !important; border-right: none !important; border-bottom: none !important; border-left: none !important; border-width: 0 !important; border-style: none !important; box-shadow: none !important; outline: none !important; } /* 该边框也可能是 ::before / ::after 画的竖条,一并清掉 */ body.bili-space-page .bili-dyn-card-video__body::before, body.bili-space-page .bili-dyn-card-video__body::after, body.bili-space-page .bili-dyn-card-video__desc::before, body.bili-space-page .bili-dyn-card-video__desc::after { content: none !important; display: none !important; border: none !important; background: none !important; box-shadow: none !important; } body.bili-space-page .bili-dyn-card-video__title, body.bili-space-page .bili-dyn-card-video__desc, body.bili-space-page .bili-dyn-card-video__title .bili-ellipsis, body.bili-space-page .bili-dyn-card-video__desc .bili-ellipsis { text-align: center !important; } body.bili-space-page .bili-dyn-card-video__stat, body.bili-space-page .bili-dyn-card-video__stat__item, body.bili-space-page .bili-dyn-card-video__stat__item.danmaku { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; } /* ===== v132.78:「直播了」卡片改为与「投稿了视频」卡片完全一致 ===== 探针实测(up主空间页探针.txt,main.space-main.route_dynamic): 直播卡 a.bili-dyn-card-live.rcmd [310x132] ├─ __header [236x132] ← 封面只占 236,右边 74 留给文字 │ ├─ __tag.state--1 [58x19] absolute(直播中) │ └─ __cover.b-img [236x132] → picture → img [236x132] └─ __body [74x132] pt=12 pb=10 ← 文字区只有 74 宽 ├─ __title [41x52] ← 标题被挤成多行 └─ __desc [41x18] 投稿卡 a.bili-dyn-card-video [310x244] ├─ __header [310x175] ← 封面占满 310(16:9) │ └─ __cover [310x175] → picture → img [310x175] └─ __body [310x69] pt=8 ← 文字区在封面下方,同样占满 310 ├─ __title [210x21] └─ __desc [78x34] mt=6 差在哪:直播卡是「左封面 + 右文字」的横向排布,投稿卡是 「上封面 + 下文字」的纵向排布 —— 这就是两者看起来不一样的原因。 脚本此前只给 -video 写过这套纵向规则(上面几组), -live 一条都没覆盖,所以一直保持站内原生的横向排布。 修法:把 -video 那套逐项原样套到 -live 上 —— flex column + 封面 16:9 满宽 + 文字区 pt 8 落到封面下方。 标题与简介随即拿到 310 的完整宽度,不再被挤成多行。 保留 __tag「直播中」角标:它是直播状态信息(投稿卡的 duration-time 也是同类角标,同样压在封面内角),去掉会丢信息;封面变满宽后它 随封面一起等比放大、相对位置不变,不需要额外处理。 */ body.bili-space-page .bili-dyn-card-live, body.bili-space-page a.bili-dyn-card-live { display: flex !important; flex-direction: column !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; box-sizing: border-box !important; } body.bili-space-page .bili-dyn-card-live__header { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; flex: 0 0 auto !important; box-sizing: border-box !important; } body.bili-space-page .bili-dyn-card-live__cover { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; aspect-ratio: 16 / 9 !important; box-sizing: border-box !important; } body.bili-space-page .bili-dyn-card-live__cover .b-img, body.bili-space-page .bili-dyn-card-live__cover .b-img__inner, body.bili-space-page .bili-dyn-card-live__cover img, body.bili-space-page .bili-dyn-card-live__cover picture { width: 100% !important; max-width: 100% !important; height: auto !important; aspect-ratio: 16 / 9 !important; object-fit: cover !important; display: block !important; } body.bili-space-page .bili-dyn-card-live__body { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; flex: 1 1 auto !important; padding: 8px 0 0 0 !important; box-sizing: border-box !important; } /* 与投稿卡一致:简介与标题之间留 6px(投稿卡 __desc 的 mt 就是 6) */ body.bili-space-page .bili-dyn-card-live__desc { margin-top: 6px !important; } /* 与投稿卡一致:清掉可能框住文字的边框(含可能画成伪元素的竖条) */ body.bili-space-page .bili-dyn-card-live__body, body.bili-space-page .bili-dyn-card-live__title, body.bili-space-page .bili-dyn-card-live__desc { border: none !important; border-top: none !important; border-right: none !important; border-bottom: none !important; border-left: none !important; border-width: 0 !important; border-style: none !important; box-shadow: none !important; outline: none !important; text-align: center !important; } body.bili-space-page .bili-dyn-card-live__body::before, body.bili-space-page .bili-dyn-card-live__body::after, body.bili-space-page .bili-dyn-card-live__desc::before, body.bili-space-page .bili-dyn-card-live__desc::after { content: none !important; display: none !important; border: none !important; background: none !important; box-shadow: none !important; } /* ===== v132.79:动态卡片四角圆角补齐 ===== 探针实测(修改后的直播卡片.txt,v132.78 生效后): 直播卡 a.bili-dyn-card-live [310x259] ovf=**visible** ├─ __header [310x175] → __cover [310x175] → img [310x175] └─ __body [310x84] 投稿卡 a.bili-dyn-card-video [310x244] ovf=hidden ├─ __header [310x175] → __cover [310x175] → img [310x175] └─ __body [310x69] 现象:直播卡只有左侧两角是圆角、右侧两角是直角;投稿卡只有上方两角 是圆角、下方两角是直角。 成因:v132.78 之前这两张卡都是**横向**排布(直播卡左封面右文字), 站内那套圆角是按横向写的 —— 直播卡封面在左,于是只给了左上 / 左下; 投稿卡封面在上,于是只给了上方两角。v132.78 把排布改成纵向后, 圆角声明没有跟着改,就出现了"一半圆角一半直角"。 修法(不猜站内具体写成什么,直接把四角全部重新声明一遍): ① 整卡 border-radius 8px + overflow hidden —— 这一条是总闸, 任何子元素的直角都会被父级裁成圆角,四角一定齐; 直播卡原本 ovf=visible,必须补上(投稿卡本来就是 hidden); 顺带写 position:relative,让 overflow 对内部 absolute 角标 (直播中的 __tag / 时长的 duration-time)也能生效。 ② 封面(__header / __cover / 内层 b-img / img / picture) 单独给 8px 8px 0 0 —— 让封面图自己就是上圆下方, 不依赖父级裁剪(父级裁剪在部分内核下会留 1px 毛边)。 ③ 文字区 __body 给 0 0 8px 8px —— 底边两角收圆。 取值 8px:与本脚本既有的 .bili-dyn-item(整卡白底 8px 圆角)、 .opus-card__cover(8px 8px 0 0)保持同一套口径。 想改大小只需改这一个数字(三处,已标注)。 */ body.bili-space-page .bili-dyn-card-live, body.bili-space-page .bili-dyn-card-video { border-radius: 8px !important; /* ← 圆角大小(1/3) */ overflow: hidden !important; position: relative !important; } body.bili-space-page .bili-dyn-card-live__header, body.bili-space-page .bili-dyn-card-live__cover, body.bili-space-page .bili-dyn-card-live__cover .b-img, body.bili-space-page .bili-dyn-card-live__cover .b-img__inner, body.bili-space-page .bili-dyn-card-live__cover img, body.bili-space-page .bili-dyn-card-live__cover picture, body.bili-space-page .bili-dyn-card-video__header, body.bili-space-page .bili-dyn-card-video__cover, body.bili-space-page .bili-dyn-card-video__cover .b-img, body.bili-space-page .bili-dyn-card-video__cover .b-img__inner, body.bili-space-page .bili-dyn-card-video__cover img, body.bili-space-page .bili-dyn-card-video__cover picture { border-radius: 8px 8px 0 0 !important; /* ← 圆角大小(2/3) */ } body.bili-space-page .bili-dyn-card-live__body, body.bili-space-page .bili-dyn-card-video__body { border-radius: 0 0 8px 8px !important; /* ← 圆角大小(3/3) */ } body.bili-space-page .bili-dyn-item, body.bili-space-page .bili-dyn-item * { text-align: center !important; } /* 正文容器:只做内边距约束,绝不能隐藏(正文 .bili-dyn-content 就在它里面) */ body.bili-space-page .bili-dyn-item__body { margin-left: 0 !important; margin-right: 0 !important; padding-left: 8px !important; padding-right: 8px !important; width: 100% !important; max-width: 100% !important; box-sizing: border-box !important; } /* 去掉卡片内的「转发 / 评论 / 点赞」数量行(整行隐藏,与动态一致) */ body.bili-space-page .bili-dyn-item__footer, body.bili-space-page .bili-dyn-item__action, body.bili-space-page .bili-dyn-action, body.bili-space-page .bili-dyn-action.forward, body.bili-space-page .bili-dyn-action.comment, body.bili-space-page .bili-dyn-action.like { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* 隐藏该行后,卡片底部补回一点留白,避免内容紧贴卡片下边缘 */ body.bili-space-page .bili-dyn-item { padding-bottom: 12px !important; } body.bili-space-page .bili-dyn-content__orig__major img, body.bili-space-page .bili-dyn-content__orig__major picture, body.bili-space-page .bili-dyn-content__orig__major .b-img { margin-left: auto !important; margin-right: auto !important; display: block !important; } body.bili-space-page .bili-dyn-content__orig__major [class*="grid"], body.bili-space-page .bili-dyn-content__orig__major [class*="pics"], body.bili-space-page .bili-dyn-content__orig__major [class*="gallery"], body.bili-space-page .bili-dyn-content__orig__major [class*="goods"], body.bili-space-page .bili-dyn-content__orig__major [class*="commodity"], body.bili-space-page .bili-dyn-content__orig__major [class*="card"] { margin-left: auto !important; margin-right: auto !important; justify-content: center !important; } body.bili-space-page .bili-dyn-item__action { flex: 1 1 0 !important; width: auto !important; max-width: none !important; min-width: 0 !important; box-sizing: border-box !important; display: flex !important; align-items: center !important; justify-content: center !important; } body.bili-space-page .bili-dyn-action { display: flex !important; align-items: center !important; justify-content: center !important; width: 100% !important; gap: 4px !important; } body.bili-space-page .space-upload { display: flex !important; flex-direction: column !important; align-items: stretch !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; gap: 0 !important; } body.bili-space-page .upload-sidenav { order: 1 !important; display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; max-height: none !important; /* v131.35:间距必须在容器「外面」用 margin,不能靠内部 padding-top。 按内部 padding 算的话,容器顶 160 紧贴 nav-bar 底 160,容器间距是 0。 padding-top 归零 + margin-top 8 后:容器顶 168、胶囊顶 168, 容器间距与视觉间距都是 8。 */ padding: 0 8px 8px 8px !important; margin: 0 !important; box-sizing: border-box !important; position: static !important; flex: 0 0 auto !important; overflow: visible !important; background: #f1f2f3 !important; /* v131.34:按用户选择改回不吸顶,随页面一起滚走。 吸顶虽能保证间距,但会把最上面的卡片遮住一部分; 改为 static 后卡片完整显示,胶囊距导航条 8px(自身 padding-top)。 */ position: static !important; top: auto !important; z-index: auto !important; } body.bili-space-page .upload-sidenav .side-nav { display: flex !important; flex-direction: row !important; justify-content: center !important; align-items: center !important; gap: 8px !important; width: 100% !important; max-width: 100% !important; padding: 0 !important; margin: 0 auto !important; box-sizing: border-box !important; flex-wrap: wrap !important; position: static !important; } body.bili-space-page .upload-sidenav .side-nav__item { display: flex !important; align-items: center !important; justify-content: center !important; flex: 0 0 auto !important; width: auto !important; min-width: 64px !important; max-width: 100% !important; height: 34px !important; padding: 0 16px !important; margin: 0 !important; box-sizing: border-box !important; border-radius: 17px !important; background: #ffffff !important; color: #18191c !important; font-size: 13px !important; cursor: pointer !important; user-select: none !important; white-space: nowrap !important; position: static !important; } body.bili-space-page .upload-sidenav .side-nav__item.active { background: #00aeec !important; color: #ffffff !important; } body.bili-space-page .upload-sidenav .side-nav__item__main { display: flex !important; align-items: center !important; justify-content: center !important; gap: 4px !important; width: auto !important; height: auto !important; padding: 0 !important; margin: 0 !important; background: transparent !important; } body.bili-space-page .upload-sidenav .side-nav__item__main-text { color: inherit !important; font-size: 13px !important; font-weight: 500 !important; line-height: 34px !important; } body.bili-space-page .upload-content { order: 2 !important; display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; flex: 1 1 100% !important; padding: 0 8px 16px 8px !important; margin: 0 !important; box-sizing: border-box !important; overflow: visible !important; position: static !important; } /* ------------------------------------------------------------ v131.30 投稿页:移除「TA的视频」,并让筛选胶囊与卡片等距 实测(11-01-27 探针): .upload-sidenav[360x50] = padding 8 + 胶囊 34 + padding 8 .upload-content[360x3742] → .video[344x3734] > .video-header[344x64](.video-header__top > .title「TA的视频」) > .video-body[344x3582](卡片网格) > .video-footer(分页器) 1) 隐藏「TA的视频」:直接收掉整个 .video-header(64 高), 只隐藏 .title 会留下 64px 空白,间距仍然对不上。 2) 间距对齐分页器:v131.24 已把分页器按钮距底定为 16px (pagenation padding-bottom 8 + video-footer margin -8 + upload-content padding-bottom 16)。 胶囊底 → 卡片顶要同样为 16px: sidenav 自带 padding-bottom 8 + .video-body margin-top 8 = 16 ------------------------------------------------------------ */ body.bili-space-page .upload-content .video-header, body.bili-space-page .upload-content .video-header__top, body.bili-space-page .upload-content .video-header .title, body.bili-space-page .upload-content .video-header .sub-title { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } body.bili-space-page .upload-content .video-body { width: 100% !important; max-width: 100% !important; min-width: 0 !important; /* v131.31:胶囊底 → 卡片顶 = sidenav padding-bottom 8 + 0 = 8 (与卡片间距一致。v131.30 的 8px 会让总间距变成 16) */ margin-top: 0 !important; margin-bottom: 0 !important; margin-left: 0 !important; margin-right: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* v131.35:nav-bar 底 → 胶囊栏容器顶 = 8(容器外 margin)。 注意这条在后面,会覆盖前面的 margin: 0,必须写成 8。 */ body.bili-space-page .upload-sidenav { margin-top: 8px !important; margin-bottom: 0 !important; padding-top: 0 !important; } /* v131.36 兜底:用「父 > 子:nth-of-type(1)」把特异性提到最高, 确保 margin-top 8 / padding-top 0 不被任何同名规则盖回去。 */ body.bili-space-page .space-upload > div.upload-sidenav:nth-of-type(1) { margin-top: 8px !important; margin-bottom: 0 !important; padding-top: 0 !important; } body.bili-space-page .space-search > div.search-sidebar:nth-of-type(1) { margin-top: 8px !important; margin-bottom: 0 !important; padding-top: 0 !important; } body.bili-space-page .lists-view-mode, body.bili-space-page .video-mode-switcher, body.bili-space-page .lists-view-mode__action { display: none !important; visibility: hidden !important; } body.bili-space-page .upload-content .video-list, body.bili-space-page .upload-content .video-list.grid-mode, body.bili-space-page .upload-content .video-list.hide-stat, body.bili-space-page .video-list.grid-mode, body.bili-space-page .video-list.hide-stat { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; grid-gap: 8px !important; gap: 8px !important; row-gap: 8px !important; column-gap: 8px !important; padding: 0 !important; margin: 0 !important; width: 100% !important; max-width: 100% !important; box-sizing: border-box !important; list-style: none !important; align-items: start !important; align-content: start !important; background: transparent !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; grid-auto-rows: auto !important; } /* ------------------------------------------------------------ v131.16 「合集和系列」整页(route_listsIndex)左右边距对齐主页 结构:main.space-main.route_listsIndex > .space-lists[360 满屏,无内边距] > .lists-header[360] + .lists-content[360] > .contet-list[360] > .video-list[360] > .video-list__content[360,已是两列网格 gap8] 这一页此前没有命中任何边距规则,卡片 176 宽直接顶到屏幕边缘; 主页 .space-home > .content 是 padding: 0 8px 16px 8px, 这里给 .space-lists 同一组值,内容宽回到 344、卡片 168,与主页一致。 v131.17 修正:只补左右边距,不要动 position / padding-top。 .lists-header 是 sticky 定位(自带 top 偏移躲开吸顶的 navbar), 上一版给它写了 position: static,粘性失效后它就直接跑到 「主页 动态 投稿 合集和系列」那一排按键底下被压住了。 同理 padding-top 也不能清零。 ------------------------------------------------------------ */ body.bili-space-page .space-lists { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 8px !important; padding-right: 8px !important; padding-bottom: 16px !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; float: none !important; overflow: visible !important; } body.bili-space-page .space-lists .lists-content, body.bili-space-page .space-lists .contet-list, body.bili-space-page .space-lists .video-list { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; float: none !important; } /* 标题行同样只补左右,保留它自己的 sticky / top 偏移 */ body.bili-space-page .space-lists .lists-header { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; padding-bottom: 16px !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; float: none !important; } /* v131.49 去掉「合集和系列」这一行大标题。 探针 09-28_17-45-34 实测: main.route_listsIndex top=134(.space-lists mt=30) > .space-lists > .lists-header[344x50] top=134 bottom=184 pt=0 pb=16 > .title[120x34] top=134 bottom=168 text=合集和系列 > .lists-content[344x11707] top=184 .lists-header 里**只有 .title 一个子节点**,没有别的控件, 所以整块 display:none 即可,不需要单独去清 .title (.title 是站内通用类名,单独选中反而容易误伤别的页面)。 隐藏后 .lists-content 直接顶到 main 的 134, 与 navbar 底部(104)之间保留 .space-lists 自带的 mt=30。 上面 v131.17 那条「不要动 .lists-header 的 position / padding-top」 的告诫在这里不适用了——整块都不显示了,sticky 也就无从谈起。 */ body.bili-space-page .space-lists > .lists-header { display: none !important; } body.bili-space-page main.space-main.route_listsIndex > .space-lists > .lists-header { display: none !important; } body.bili-space-page .space-lists .lists-header .title { display: none !important; } /* 标题行与卡片之间,沿用主页「合集·/系列·」的 20px */ body.bili-space-page .space-lists .video-list__content { margin-top: 20px !important; } /* ------------------------------------------------------------ v131.18 合集详情页(route_listDetails,点进某个「合集·xxx」后) 结构:main.space-main.route_listDetails > .space-list-details[360] > .list-header[360x110](.title + .subtitle) > .list-content[360x170] > .list-video-item[59x110,桌面版一排放 5 个] 问题:整页无边距、卡片被压成 59 宽(封面只剩 33 高)。 改法:外层补 8px 左右边距(同主页 .space-home > .content), .list-content 改两列网格,卡片回到 168x172(封面 95 + 信息区 77)。 注意:.list-header 同样是 sticky,这里不写 position / padding-top。 ------------------------------------------------------------ */ body.bili-space-page .space-list-details { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 8px !important; padding-right: 8px !important; padding-bottom: 16px !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; float: none !important; overflow: visible !important; } /* ------------------------------------------------------------ v132.58 合集详情页:分页条上 / 下间距统一为 16px 探针实测(route_listDetails): .list-footer [344x110] top=674 bottom=784 pt=0 **pb=60** > .vui_pagenation[344x50] top=674 bottom=724 pt=8 pb=8 > .vui_pagenation--btns[344x34] top=682 bottom=716 ← 按键本体 按当前数值算(.space-list-details 另有脚本加的 pb=16): 上间距 = 按键 682 − 末行卡片 674 = 8px 下间距 = 容器底 800 − 按键底 716 = 84px (pagenation pb 8 + footer pb 60 + 容器 pb 16) 三者职责混乱:footer 那 60px 是 PC 版留给页面底部的余量, 手机上纯属白占;容器的 16 又和分页条的 pb 叠在一起。 改法:把间距收归分页条一个元素负责 —— · .list-footer padding 全清零(去掉 PB 版那 60px) · .space-list-details padding-bottom 清零(避免与 pb 叠加) · .vui_pagenation padding 改 16px 0 改后:按键 top=690 bottom=724,容器底 740 → 上 16 / 下 16。 选择器用完整链路(main.space-main.route_listDetails > ...), 特异性高于「body.bili-space-page .vui_pagenation」那条 pt/pb 8px, 无需 !important 也能压过,这里仍写上以防站内行内样式。 ------------------------------------------------------------ */ body.bili-space-page main.space-main.route_listDetails > .space-list-details { padding-bottom: 0 !important; } body.bili-space-page main.space-main.route_listDetails .list-footer, body.bili-space-page .space-list-details > .list-footer { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-space-page main.space-main.route_listDetails .list-footer > .vui_pagenation, body.bili-space-page .space-list-details > .list-footer > .vui_pagenation, body.bili-space-page main.space-main.route_listDetails .list-footer > [class*="pagenation"] { padding-top: 16px !important; padding-bottom: 16px !important; margin-top: 0 !important; margin-bottom: 0 !important; } body.bili-space-page .space-list-details .list-header { width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; float: none !important; } /* 标题「合集·xxx」与卡片之间 20px,和主页一致 */ body.bili-space-page .space-list-details .list-header .subtitle, body.bili-space-page .space-list-details .list-header .meta { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin-bottom: 20px !important; box-sizing: border-box !important; } /* v131.50 合集详情页:「合集  6个视频 9月14日更新」整行居中 探针 09-28_18-03-48 实测(route_listDetails): .list-header[344x144] top=134 bottom=278 mb=22 > .title[344x68] (.back[20x20] + .name[316x68])mb=20 > .subtitle[344x20] top=222 **pos=relative** mb=20 > .meta[344x20] mb=20 > .stats[344x20] text=合集  6个视频 9月14日更新 三层盒子都已经是满宽 344,文字靠左只是因为默认 text-align。 所以这里**不改 display**(.subtitle 是 relative、.meta/.stats 可能是 flex,贸然改成 block 会把站内的行内排布打散), 只同时给两条居中通路: text-align:center -> 命中普通块级 / 行内文本 justify-content:center -> 命中 flex 容器 两条互不干扰,谁生效取决于站内实际用的是哪种布局。 */ body.bili-space-page .space-list-details .list-header .subtitle, body.bili-space-page .space-list-details .list-header .subtitle .meta, body.bili-space-page .space-list-details .list-header .subtitle .meta .stats, body.bili-space-page .space-list-details .list-header .meta, body.bili-space-page .space-list-details .list-header .stats { text-align: center !important; justify-content: center !important; } /* 高特异性兜底(v131.36 套路) */ body.bili-space-page main.space-main.route_listDetails > .space-list-details > .list-header > .subtitle > .meta > .stats { text-align: center !important; justify-content: center !important; } /* v131.50 顺带覆盖 .list-header 里除 .title 以外的任何直接子节点。 原因:探针算下来 .list-header 有 16px 对不上账—— .title 68 + mb 20 + .subtitle 20 + mb 20 = 128, 而 .list-header 实测 144 高,多出的 16px 在探针里找不到归属元素。 「默认 / 倒序」排序按键在这份探针里也完全查不到 (5 份探针全文 grep「排序 / 默认 / 倒序」均 0 命中), 很可能就是这块延迟渲染 / 未被探针遍历到的东西。 这条规则只写 text-align / justify-content,命中不了就完全无副作用, 命中了就顺手把它一起居中;真正的定位还要等新探针确认。 */ body.bili-space-page .space-list-details .list-header > *:not(.title) { text-align: center !important; justify-content: center !important; } /* v131.51 合集详情页:简介行「.desc」文字一半跑到屏幕外 + 居中 探针 09-28_18-14-58 实测(route_listDetails): .list-header[344x210] top=134 bottom=344 mb=22 > .title[344x102] (.back[20x20] + .name[316x102])mb=20 > **.desc[677x20]** top=256 left=8 mb=12 ovf=hidden > .subtitle[344x20] top=288(pos=relative)mb=20 真凶就是 .desc:它是站内 PC 版留下的**定宽 677px**, 在 360 的视口里从 left=8 一直伸到 685,右半截整个在屏幕外; 自身 overflow:hidden 又只给 20px 高,等于"一行 + 裁掉右边"。 (顺带结案:上一版探针里 .list-header 对不上账的那 16px, 就是这个 .desc —— 那份探针没有列出它,我当时推测是排序按键, 推错了,排序按键在 .subtitle 里,已由 v131.50 那条规则居中。) 改法:宽度收回 100%(344),高度放开让它按 344 宽自动换行 (这句 21 个字,677px 一行 -> 344px 两行), 再解掉站内的单行省略号机制(nowrap / ellipsis / line-clamp), 最后 text-align + justify-content 双通路居中。 */ body.bili-space-page .space-list-details .list-header .desc, body.bili-space-page .space-list-details .list-header > .desc { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 0 12px 0 !important; padding: 0 !important; box-sizing: border-box !important; float: none !important; line-height: 20px !important; text-align: center !important; justify-content: center !important; white-space: normal !important; word-break: break-word !important; overflow-wrap: anywhere !important; text-overflow: clip !important; -webkit-line-clamp: unset !important; line-clamp: unset !important; overflow: visible !important; background: transparent !important; } /* 高特异性兜底(v131.36 套路):站内这条定宽大概率写在选择器里, 用完整链路压过去,确保 677px 一定被 100% 顶掉。 */ body.bili-space-page main.space-main.route_listDetails > .space-list-details > .list-header > .desc { width: 100% !important; max-width: 100% !important; height: auto !important; max-height: none !important; text-align: center !important; white-space: normal !important; overflow: visible !important; } body.bili-space-page .space-list-details .list-content { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; grid-auto-rows: auto !important; gap: 8px !important; row-gap: 8px !important; column-gap: 8px !important; align-items: start !important; align-content: start !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; float: none !important; overflow: visible !important; } /* 桌面版给 item 算的是 5 列宽(59),这里放开让它吃满格子 */ body.bili-space-page .space-list-details .list-video-item { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; position: static !important; transform: none !important; left: auto !important; top: auto !important; float: none !important; flex: 0 1 auto !important; } /* ============================================================ ========== 通用两列视频卡片 ========== ============================================================ */ body.bili-home-page .container .bili-video-card, body.bili-home-page .container .bili-live-card, body.bili-search-page .bili-video-card, body.bili-search-page .bili-live-card, body.bili-search-page .search-video-card, body.bili-space-page .bili-video-card, body.bili-space-page .bili-live-card { --cover-radio: 56.25% !important; --title-font-size: 13px !important; --title-line-height: 18px !important; --title-padding-right: 0 !important; --no-interest-entry-size: 22px !important; --info-margin-top: 6px !important; --subtitle-font-size: 11px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; max-height: none !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; display: block !important; align-self: start !important; background: #ffffff !important; border: none !important; border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0,0,0,0.08) !important; overflow: hidden !important; box-sizing: border-box !important; position: relative !important; float: none !important; } body.bili-home-page .container .bili-video-card__wrap, body.bili-home-page .container .bili-live-card__wrap, body.bili-search-page .bili-video-card__wrap, body.bili-search-page .bili-live-card__wrap, body.bili-space-page .bili-video-card__wrap, body.bili-space-page .bili-live-card__wrap { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; max-height: none !important; margin: 0 !important; padding: 0 !important; display: block !important; background: transparent !important; border: none !important; border-radius: 8px !important; box-sizing: border-box !important; overflow: hidden !important; } body.bili-home-page .bili-video-card__image--wrap, body.bili-home-page .bili-video-card__image--link, body.bili-home-page .v-img.bili-video-card__cover, body.bili-home-page .bili-video-card .b-img, body.bili-home-page .bili-video-card picture, body.bili-search-page .bili-video-card__image--wrap, body.bili-search-page .bili-video-card__image--link, body.bili-search-page .v-img.bili-video-card__cover, body.bili-search-page .bili-video-card .b-img, body.bili-search-page .bili-video-card picture, body.bili-space-page .bili-video-card__image--wrap, body.bili-space-page .bili-video-card__image--link, body.bili-space-page .v-img.bili-video-card__cover, body.bili-space-page .bili-video-card .b-img, body.bili-space-page .bili-video-card picture { width: 100% !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; border-radius: 8px 8px 0 0 !important; overflow: hidden !important; position: relative !important; display: block !important; float: none !important; box-sizing: border-box !important; } body.bili-home-page .bili-video-card__image--wrap img, body.bili-home-page .bili-video-card__image--link img, body.bili-home-page .v-img.bili-video-card__cover img, body.bili-home-page .bili-video-card .b-img > img, body.bili-home-page .bili-video-card .b-img__inner, body.bili-home-page .bili-video-card picture img, body.bili-search-page .bili-video-card__image--wrap img, body.bili-search-page .bili-video-card__image--link img, body.bili-search-page .v-img.bili-video-card__cover img, body.bili-search-page .bili-video-card .b-img > img, body.bili-search-page .bili-video-card .b-img__inner, body.bili-search-page .bili-video-card picture img, body.bili-space-page .bili-video-card__image--wrap img, body.bili-space-page .bili-video-card__image--link img, body.bili-space-page .v-img.bili-video-card__cover img, body.bili-space-page .bili-video-card .b-img > img, body.bili-space-page .bili-video-card .b-img__inner, body.bili-space-page .bili-video-card picture img { width: 100% !important; max-width: 100% !important; height: auto !important; aspect-ratio: 16 / 9 !important; object-fit: cover !important; display: block !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } body.bili-home-page .bili-video-card__image--wrap picture > *, body.bili-search-page .bili-video-card__image--wrap picture > *, body.bili-space-page .bili-video-card__image--wrap picture > * { width: 100% !important; max-width: 100% !important; height: auto !important; padding: 0 !important; margin: 0 !important; display: block !important; box-sizing: border-box !important; } body.bili-home-page .bili-video-card__info, body.bili-home-page .bili-live-card__info, body.bili-home-page .video-info, body.bili-search-page .bili-video-card__info, body.bili-search-page .bili-live-card__info, body.bili-search-page .video-info, body.bili-space-page .bili-video-card__details, body.bili-space-page .bili-video-card__info, body.bili-space-page .bili-live-card__info { width: 100% !important; padding: 8px !important; margin: 0 !important; box-sizing: border-box !important; text-align: center !important; display: block !important; height: auto !important; max-height: none !important; min-height: 0 !important; background: transparent !important; } /* ===== v132.83:首页直播卡(如「直播中 左右开弓」)—— 隐藏右侧竖三点按键 + 高度与其它视频卡对齐 172px ===== 探针实测(bili_probe_2026-10-03_12-08-23,首页,视口 360): 直播卡(要改的) 视频卡(基准) .bili-live-card [168x**139**] .bili-video-card [168x**172**] └─ __wrap [168x**174**] └─ __wrap [168x172] pos=static pos=**absolute** z=1 ├─ __image--link [168x95] ├─ __image--link [168x95] └─ __info [168x**79**] └─ __info [168x**77**] pt=8 pb=8 pt=8 pb=8 └─ __info--text [152x**63**] └─ __info--right [152x**61**] ├─ __info--no-interest [22x21] ← 三点(要隐藏) 【高度不一致的真因】不是文字多少,而是 **__wrap 是 absolute**: absolute 元素脱离文档流、不撑开父级,所以父级 height:auto 时 只剩下 139(由 grid 行高之类兜出来的),而 wrap 实际是 174。 视频卡的 wrap 是 static,自然撑开父级 → 172。 所以第一步就是把直播卡的 wrap 改回 static,让它照抄视频卡的结构。 【174 → 172 那 2px】来自 __info:直播卡 79(pt8 + text63 + pb8), 视频卡 77(pt8 + right61 + pb8),差的正是 text 63 vs 61。 把 __info 定高 77px,并把 padding-top 由 8 收到 6: 6 + 63 + 8 = 77 ✓ 这样内容**一点都不用压缩**(text 仍是实测的 63,UP 主名底部 421 完整保留),只让文字整体上移 2px、更靠近封面,视觉无感。 (另一条路是把 text 压成 61,但实测 63 里含 2px 行高空白, 定 61 会正好卡在 421 的边界上,内核取整差 1px 就可能裁到字脚。 收 padding 零裁切风险,故取这条。) 【三点按键】.bili-live-card__info--no-interest [22x21] left=322, 压在卡片最右侧(右缘 344 贴边),点开是「不感兴趣」菜单; 视频卡那个同名元素实测是 0x0(hover 才显,手机碰不到), 只有直播卡这个一直露着,所以重点处理它,视频卡的顺带一并隐藏。 ⚠ 只隐藏这个按钮本身,**不动 __info--text 父容器** —— 里面还装着标题和 UP 主名,整块隐藏会把正文一起干掉。 */ body.bili-home-page .bili-live-card__info--no-interest, body.bili-home-page .bili-video-card__info--no-interest, body.bili-search-page .bili-live-card__info--no-interest, body.bili-search-page .bili-video-card__info--no-interest, body.bili-space-page .bili-live-card__info--no-interest, body.bili-space-page .bili-video-card__info--no-interest { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; max-width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; border: none !important; overflow: hidden !important; pointer-events: none !important; } /* 图标本身也收掉,防父容器改结构后留一块空白 */ body.bili-home-page .bili-live-card__info--no-interest--icon, body.bili-home-page .bili-video-card__info--no-interest--icon, body.bili-search-page .bili-live-card__info--no-interest--icon, body.bili-search-page .bili-video-card__info--no-interest--icon, body.bili-space-page .bili-live-card__info--no-interest--icon, body.bili-space-page .bili-video-card__info--no-interest--icon { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; pointer-events: none !important; } /* ① 关键:wrap 改回 static —— 不再脱离文档流,才能撑开父级 */ body.bili-home-page .container .bili-live-card .bili-live-card__wrap, body.bili-home-page .bili-live-card > .bili-live-card__wrap, body.bili-search-page .bili-live-card > .bili-live-card__wrap, body.bili-space-page .bili-live-card > .bili-live-card__wrap { position: static !important; top: auto !important; left: auto !important; right: auto !important; bottom: auto !important; transform: none !important; z-index: auto !important; } /* ② info 定高 77 → 95 + 77 = 172,与视频卡一致 ⚠ v132.85 改回 padding-top:8(v132.83 曾收到 6,代价是 UP 主行 整体上移 2px,比视频卡高 —— 用户反馈"位置高一点")。 腾出的 2px 由 ③ 从文字区底部回收,不靠压缩上边距。 */ body.bili-home-page .bili-live-card .bili-live-card__wrap > .bili-live-card__info, body.bili-home-page .bili-live-card > .bili-live-card__wrap > .bili-live-card__info, body.bili-search-page .bili-live-card > .bili-live-card__wrap > .bili-live-card__info, body.bili-space-page .bili-live-card > .bili-live-card__wrap > .bili-live-card__info { height: 77px !important; /* ← 95(封面) + 77 = 172 = 视频卡高度 */ min-height: 77px !important; max-height: 77px !important; padding-top: 8px !important; /* ← 与视频卡一致(v132.85 由 6 改回 8) */ padding-bottom: 8px !important; padding-left: 8px !important; padding-right: 8px !important; overflow: hidden !important; box-sizing: border-box !important; } /* ③ 文字区定高 61 —— 与视频卡 __info--right 实测值完全相同。 新探针(bili_probe_2026-10-03_12-28-50,同行左右对照,card top 均 363): 左·视频卡 右·直播卡(改前) __info [168x77] top=458 pt=8 __info [168x77] top=458 pt=**6** └─ __info--right [152x**61**] └─ __info--text [152x**63**] top=466 top=464 ├─ tit 466~502 (36) ├─ tit 464~500 (36) └─ bottom 506~527 (21) mt=4 └─ uname **504**~525 (21) mt=4 即:直播卡 UP 主行 504 vs 视频卡 506,**高了 2px**(相对卡片顶 141 vs 143)。这 2px 正是 v132.83 为凑 6+63+8=77 收掉的上边距。 内容与视频卡其实一模一样(tit 36 + mt 4 + 行 21 = 61), 直播卡的 text 之所以是 63,是 a 标签按 inline 排、基线下方带的 2px 降部空白(uname 底 525,text 底 527)。 所以两步收掉这 2px,且**不裁切任何内容**: ① __info--uname 改 display:block —— 消掉 inline 基线留白; ② __info--text 定高 61 —— 兜底,实测 uname 底 525 = text 顶 464 + 61,正好贴底,一行不裁;nowrap 防名字换行撑高。 改后:text 顶 = 458+8 = 466,tit 466~502,uname 506~527 —— 与视频卡的 right / tit / bottom **逐像素重合**。 */ body.bili-home-page .bili-live-card__info--text, body.bili-search-page .bili-live-card__info--text, body.bili-space-page .bili-live-card__info--text { height: 61px !important; /* ← 36(标题) + 4(间距) + 21(UP主行) */ min-height: 61px !important; max-height: 61px !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; box-sizing: border-box !important; } body.bili-home-page .bili-live-card__info--uname, body.bili-search-page .bili-live-card__info--uname, body.bili-space-page .bili-live-card__info--uname { display: block !important; /* ← 消掉 inline 基线下方 2px 留白 */ height: 21px !important; min-height: 21px !important; max-height: 21px !important; line-height: 21px !important; margin-top: 4px !important; margin-bottom: 0 !important; padding: 0 !important; white-space: nowrap !important; overflow: hidden !important; box-sizing: border-box !important; } /* ===== v132.84:修 v132.83 的回归 —— 直播卡内容整块被推到卡片下方, 只剩一条 33px 的边 ===== 新探针实测(bili_probe_2026-10-03_12-17-37,首页,左右同一行对照): 左·视频卡(正常) 右·直播卡(异常) .bili-video-card [168x172] top=251 .bili-live-card [168x172] top=251 └─ __wrap [168x172] top=**251** └─ __wrap [168x172] top=**390** ├─ image--link 251~346 ├─ image--link 390~484 └─ __info 346~423 └─ __info 484~561 卡片 172 高(v132.83 的高度修正已生效 ✓)、wrap 也是 172, 但 wrap 比卡片**低了 139px**(第二张直播卡低 138px,同样现象)。 卡片 overflow:hidden,于是只有 390~423 这 33px 落在卡片里 —— 正是用户看到的"卡内下方只显示一小行"。 ⚠ 139 不是巧合:v132.83 之前卡片的旧高度就是 139。 说明 **卡片里还藏着一个 139 高的块级元素排在 wrap 前面** —— 探针里能看到证据:wrap 的 path 是 '…__wrap:nth-of-type(2)', 即它前面还有一个 div 兄弟;而探针只输出带类名的节点, 这个兄弟是**无类名 div**(全文 0 个无类名 div 被输出), 所以探针里查不到它、只能靠几何量反推。 v132.83 之前 wrap 是 absolute(脱离文档流),这个兄弟独占一行、 卡片高度正好被它撑成 139,肉眼看着正常; 一旦把 wrap 改回 static,它就乖乖排到那个兄弟**后面** → 下移 139。 修法:**把卡片里除 wrap 以外的 div 兄弟全部隐藏**。 它是无类名的占位/懒渲染壳,内容全在 wrap 里,隐藏零副作用; 隐藏后 wrap 自然回到卡片顶部(top=251), 卡片高度也由 wrap 撑成 95 + 77 = 172,与视频卡完全一致。 保险:JS liveCardFit 会用几何量复核(wrap.top − card.top > 2 即 判定仍被压),必要时把 wrap 钉成 absolute 贴回卡片左上角, 并给卡片补一个显式高度 —— 双重兜底,不依赖"隐藏是否生效"。 */ body.bili-home-page .bili-live-card > div:not(.bili-live-card__wrap), body.bili-search-page .bili-live-card > div:not(.bili-live-card__wrap), body.bili-space-page .bili-live-card > div:not(.bili-live-card__wrap) { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; max-width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; border: none !important; overflow: hidden !important; float: none !important; position: absolute !important; pointer-events: none !important; } body.bili-home-page .bili-video-card__info--tit, body.bili-home-page .bili-video-card__info--tit a, body.bili-home-page .bili-video-card h3, body.bili-home-page .bili-video-card h3 a, body.bili-home-page .bili-live-card__info--tit, body.bili-home-page .bili-live-card__info--tit a, body.bili-search-page .bili-video-card__info--tit, body.bili-search-page .bili-video-card__info--tit a, body.bili-search-page .bili-video-card h3, body.bili-search-page .bili-video-card h3 a, body.bili-search-page .bili-live-card__info--tit, body.bili-search-page .bili-live-card__info--tit a, body.bili-space-page .bili-video-card__title, body.bili-space-page .bili-video-card__title > a, body.bili-space-page .bili-video-card__title .bili-ellipsis, body.bili-space-page .bili-video-card__info--tit, body.bili-space-page .bili-video-card__info--tit a { display: -webkit-box !important; -webkit-line-clamp: 2 !important; -webkit-box-orient: vertical !important; overflow: hidden !important; text-align: center !important; font-size: 13px !important; line-height: 18px !important; min-height: 36px !important; max-height: 36px !important; color: #18191c !important; font-weight: 500 !important; margin: 0 !important; padding: 0 !important; word-break: break-all !important; width: 100% !important; } body.bili-home-page .bili-video-card__info--bottom, body.bili-home-page .bili-video-card__info--owner, body.bili-home-page .bili-video-card__info--stat, body.bili-search-page .bili-video-card__info--bottom, body.bili-search-page .bili-video-card__info--owner, body.bili-search-page .bili-video-card__info--stat, body.bili-space-page .bili-video-card__info--bottom, body.bili-space-page .bili-video-card__info--owner, body.bili-space-page .bili-video-card__stats, body.bili-space-page .bili-video-card__subtitle, body.bili-space-page .bili-video-card__date { display: flex !important; justify-content: center !important; align-items: center !important; text-align: center !important; width: 100% !important; margin-top: 4px !important; margin-left: auto !important; margin-right: auto !important; padding: 0 !important; height: 21px !important; min-height: 21px !important; } /* ---- v132.68:空间页主页 —— 代表作标题 3 行 + 隐藏「最近投币的视频」---- 探针实测(空间页主页 · 代表作区块): .bili-video-card__title [152x36] top=466 ← 2 行(18×2),超出省略 .bili-video-card__subtitle [152x21] top=506 ← 文本为空,内嵌 span 是 0x0 代表作卡不显示「UP主名 / 日期」那一行的实际内容(subtitle 是空壳), 等于白占 21 + 4 = 25px,故把标题从 2 行放宽到 3 行补上这段空间。 只覆盖 clamp 行数与高度(54 = 18×3),display / box-orient / overflow 沿用上面那条通用规则 —— 注意 -webkit-line-clamp **只在 display:-webkit-box 下生效**,所以这里把三条一并重写一遍, 免得站内将来改了 display 后 clamp 静默失效(只截不断)。 选择器多加一层 .masterpiece-block:特异性 0,3,0 > 通用规则的 0,2,1, 且只作用于代表作,主页其它两列卡片(投稿 / 合集)仍是 2 行。 */ /* v132.70:标题盒拆成「内容 54 + 下余量 7」两层 —— v132.69 直接把盒高写成 61(54 + 7),结果 -webkit-line-clamp 只管"截到第 3 行并加省略号",并不阻止第 4 行往多出来的 7px 里 排字;盒又带 overflow:hidden,于是第 4 行露出的 7px 被横切一刀 —— 这就是用户看到的那"一行被裁剪的文字"。 修法:clamp 盒精确钉到 3 行的净高 54(18×3),多出的 7px 改用 margin-bottom 补 —— margin 在盒外,第 4 行没有可排版的空间, 自然不会渲染。 外盒 __title 54 + margin-bottom 7 = 61(占满主页那块内容盒) 内盒 > a 54(clamp 3 行,行高 18) → details = pt8 + 61 + pb8 = 77,整卡 95 + 77 = 172 不变。 ⚠ 两层不能都给 margin-bottom —— a 是 title 的子盒,若它也带 7, 会叠成 68、卡高又变;故 margin-bottom 只写在外层一条上。 */ body.bili-space-page .masterpiece-block .bili-video-card__title, body.bili-space-page .masterpiece-block .bili-video-card__info--tit { display: block !important; height: 54px !important; min-height: 54px !important; max-height: 54px !important; margin-top: 0 !important; margin-bottom: 7px !important; padding: 0 !important; overflow: hidden !important; box-sizing: border-box !important; } body.bili-space-page .masterpiece-block .bili-video-card__title > a, body.bili-space-page .masterpiece-block .bili-video-card__title .bili-ellipsis, body.bili-space-page .masterpiece-block .bili-video-card__info--tit a { display: -webkit-box !important; -webkit-line-clamp: 3 !important; -webkit-box-orient: vertical !important; overflow: hidden !important; height: 54px !important; min-height: 54px !important; max-height: 54px !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* ★ v132.69:代表作卡片高度回到 172 —— 与主页 / 投稿卡片完全一致 ★ 用户反馈:代表作卡片比其它卡片高。真因是 v132.68 把标题从 2 行 (36)放宽到 3 行(54),details 从 77 涨到 95,整卡 172 → 190, grid 里两列一行就比别人高出 18px,看起来"不整齐"。 参照物(主页卡片探针 up主空间页…反正卡片高度也一致.txt): .bili-video-card [168x172] ├─ 封面 [168x95] (16:9) └─ __info [168x77] = pt8 + 61 + pb8 ├─ __info--tit [152x36] (2 行) └─ __info--bottom [152x21] (UP主行,margin-top 4) 代表作那一行的 __subtitle 是空壳(探针:文本为空、内嵌 span 0x0, 不显示日期)—— 它占的 21 + 4 = 25px 正是标题多出来的第 3 行的去处。 故:① 隐藏代表作那行空 subtitle(清 display/高度/内外边距, 防站内靠残留尺寸把空位撑回来); ② 标题盒给到 61(= 36 + 4 + 21,与主页内容盒同高), clamp 仍是 3 行 → 3 行 54 装得下,余 7px 作为行间余量。 结果:details = 8 + 61 + 8 = 77,整卡 95 + 77 = 172,与其它卡片一致。 ⚠ 前提:代表作那行 subtitle 恒为空(用户已确认"没显示日期那一行")。 若某些 UP 的卡片确实在那行显示内容,请删掉本条隐藏规则、 并把上面标题的 61 改回 36(退回 2 行),高度同样回到 172。 */ body.bili-space-page .masterpiece-block .bili-video-card__subtitle, body.bili-space-page .masterpiece-block .bili-video-card__info--bottom, body.bili-space-page .masterpiece-block .bili-video-card__date { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* 隐藏「最近投币的视频」整块(探针路径 .coin-section__content > .items) 用 [class*="coin-section"] 兜底:站内这块的外层类名可能带哈希后缀, 写死 .coin-section 一旦改名就失效;空间页里没有第二处会命中这个 子串的模块,泛化是安全的。display:none 之外的各项一并清零, 防站内用 visibility / 残留尺寸把空位撑出来。 */ body.bili-space-page .coin-section, body.bili-space-page .coin-section__content, body.bili-space-page [class*="coin-section"] { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; } /* ---- v132.10:直播卡「UP主名那一行」与综合页对齐 ---- 两份探针对比(都是搜索页 · 168x172 两列卡片): 综合页(card top=60) 封面 60 → 155 (95) info 155 → 232 (77 = pt8 + 61 + pb8) 标题 163 → 199 (36) UP主行 203 → 224 (21,margin-top 4) 直播页(card top=88) 封面 88 → 183 (95) info 183 → 257 (75 = pt8 + 59 + pb8) 标题 191 → 227 (36)← 与综合页同为「卡顶 +103」,已对齐 UP主行 231 → 249 (18,站内原生)← 少了 3px 标题那一行两页完全一致(都是卡顶 +103),差的只有下面这行: · 综合页被上面那条规则钉成了 21px 高 + margin-top 4px · 直播页的 UP主行没人管,保持站内原生的 18px, 于是整行比综合页矮 3px,卡片底也短了 2px (wrap 169 vs 172,卡底留 3px 空隙) 修法:给直播卡的 UP主行套上与综合页一致的几何值 (21px 高 + margin-top 4px), info = 8 + (36+4+21) + 8 = 77,wrap 补到 172。 ⚠ v132.09 的翻车点(本版修正): 上一版给这一行写了 width:100% + justify-content:center, 结果名字那一格(--author,站内是 flex:1)被撑满到 136px, 而 text-align:center 又是从 .bili-live-card__info 继承下来的, 于是: 图标 16 → 32 ← 贴在行的最左边 名字 32 → 168 ← 占满剩余,文字在这 136px 里居中 名字文字实际落在 74~126 附近,与图标之间空出一大段, 看起来就是「UP 图标和名字分家了」。 探针实证(bili_probe_2026-09-30_18-30-41): a.bili-live-card__info--uname [152x21] left=16 svg.bili-live-card__info--uname-icon [16x16] left=16 ← 顶到最左 span.bili-live-card__info--author [136x21] left=32 ← 撑满剩余 修法:这一行改成**收缩到内容宽**(inline-flex), 让「图标 + 名字」作为一个整体、由父级的 text-align:center 整体居中;名字不再 flex:1,而是紧贴图标、左对齐, 超长才省略。图标的原生右间距也别再抹掉。 ---- */ body.bili-home-page .bili-live-card__info--uname, body.bili-home-page .bili-live-card__info--owner, body.bili-home-page .bili-live-card__info--bottom, body.bili-search-page .bili-live-card__info--uname, body.bili-search-page .bili-live-card__info--owner, body.bili-search-page .bili-live-card__info--bottom, body.bili-space-page .bili-live-card__info--uname, body.bili-space-page .bili-live-card__info--owner, body.bili-space-page .bili-live-card__info--bottom, body.bili-home-page .bili-live-card__info--text > *:not(.bili-live-card__info--tit), body.bili-search-page .bili-live-card__info--text > *:not(.bili-live-card__info--tit), body.bili-space-page .bili-live-card__info--text > *:not(.bili-live-card__info--tit) { /* inline-flex:宽度收缩到「图标 + 名字」的实际内容宽, 整体再由父级的 text-align:center 居中 —— 图标不再被 甩到行首,两者始终贴在一起。 */ display: inline-flex !important; flex-wrap: nowrap !important; align-items: center !important; justify-content: center !important; width: auto !important; max-width: 100% !important; min-width: 0 !important; height: 21px !important; min-height: 21px !important; max-height: 21px !important; margin: 4px auto 0 auto !important; padding: 0 !important; line-height: 21px !important; box-sizing: border-box !important; } /* 名字那一格:只占自己内容那么宽,紧跟图标;超长才省略。 ⚠ 不写 text-align:center —— 那会让文字在剩余空间里居中, 与图标之间空出一大段,正是"分家"的成因。 */ body.bili-home-page .bili-live-card__info--author, body.bili-search-page .bili-live-card__info--author, body.bili-space-page .bili-live-card__info--author, body.bili-home-page .bili-live-card__info--uname > span, body.bili-search-page .bili-live-card__info--uname > span, body.bili-space-page .bili-live-card__info--uname > span { flex: 0 1 auto !important; width: auto !important; max-width: 100% !important; min-width: 0 !important; height: 21px !important; max-height: 21px !important; margin: 0 !important; padding: 0 !important; line-height: 21px !important; text-align: left !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; } /* UP 图标:固定 16x16,保留站内原生的右间距(不再 margin:0 抹平) */ body.bili-home-page .bili-live-card__info--uname-icon, body.bili-search-page .bili-live-card__info--uname-icon, body.bili-space-page .bili-live-card__info--uname-icon, body.bili-home-page .bili-live-card__info--uname > svg, body.bili-search-page .bili-live-card__info--uname > svg, body.bili-space-page .bili-live-card__info--uname > svg { flex: 0 0 auto !important; width: 16px !important; height: 16px !important; min-width: 16px !important; max-height: none !important; line-height: normal !important; flex-shrink: 0 !important; } body.bili-home-page .bili-watch-later, body.bili-home-page [class*="watch-later"], body.bili-home-page [class*="watchlater"], body.bili-home-page [class*="add-to-watch-later"], body.bili-home-page [class*="watch-later-btn"], body.bili-home-page .bili-video-card__mask, body.bili-search-page .bili-watch-later, body.bili-search-page [class*="watch-later"], body.bili-search-page [class*="watchlater"], body.bili-search-page [class*="add-to-watch-later"], body.bili-search-page [class*="watch-later-btn"], body.bili-search-page .bili-video-card__mask, body.bili-space-page .bili-card-watch-later, body.bili-space-page .bili-video-card .bili-watch-later, body.bili-space-page .bili-video-card [class*="watch-later"] { display: none !important; visibility: hidden !important; } /* v131.21 投稿页 / 搜索页的卡片外观改为与主页卡片完全一致。 主页和投稿页用的是同一个 .bili-video-card 组件(168x172, 封面 95 + 信息区 77,标题 152x36,副标题 152x21),DOM 尺寸本来就一样, 差的只是这条规则把投稿卡的背景/圆角/阴影全清掉了,看起来比主页卡"素"。 这里只保留 border:none(v131.6 要去掉的投稿卡文字框边框), 背景、圆角、阴影统统交还给原生 -> 与主页同源、必然一致。 搜索页的卡片同样包在 .upload-video-card 里,一并生效。 */ body.bili-space-page .upload-video-card .bili-video-card, body.bili-space-page .upload-video-card .bili-video-card__wrap { border: none !important; overflow: hidden !important; } /* ============================================================ v131.13 UP主主页三处调整 ============================================================ */ /* ---------- 1) 隐藏「收藏夹 ·N」整块 ---------- 结构:section.home-section > .section-wrap.fav-section > .section-wrap__header + .section-wrap__content > .fav-section__content > .items 用 :has() 把外层 section 一并收掉,避免留下空壳占位。 */ /* ---------- 1c) 隐藏「最近点赞的视频」整块(v131.22)---------- 探针实测(洛克宅桑主页): section.home-section:nth-of-type(8) > .section-wrap.like-section[344x964] > .section-wrap__header(标题「最近点赞的视频」) > .section-wrap__content > .like-section__content[344x890] > .items 用 :has() 连带外层 section,避免只收内层留下空壳。 */ body.bili-space-page .route_home .section-wrap.like-section, body.bili-space-page .route_home .like-section__content, body.bili-space-page .route_home .home-section:has(> .section-wrap.like-section) { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; border: none !important; overflow: hidden !important; } /* ---------- 1b) 隐藏「订阅追番 ·N」整块(v131.21)---------- 与收藏夹同一套路:按区块类名收掉,并用 :has() 连带外层 section。 站内这一块的类名在 bangumi / sub / follow 之间换过, 这里三种写法都写上,任一命中即隐藏。 */ body.bili-space-page .route_home .section-wrap.bangumi-section, body.bili-space-page .route_home .bangumi-section__content, body.bili-space-page .route_home .section-wrap.sub-section, body.bili-space-page .route_home .sub-section__content, body.bili-space-page .route_home .section-wrap.follow-section, body.bili-space-page .route_home .follow-section__content, body.bili-space-page .route_home .home-section:has(> .section-wrap.bangumi-section), body.bili-space-page .route_home .home-section:has(> .section-wrap.sub-section), body.bili-space-page .route_home .home-section:has(> .section-wrap.follow-section) { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; border: none !important; overflow: hidden !important; } body.bili-space-page .route_home .section-wrap.fav-section, body.bili-space-page .route_home .fav-section__content, body.bili-space-page .route_home .home-section:has(> .section-wrap.fav-section) { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; border: none !important; overflow: hidden !important; } /* ---------- 2) 专栏卡片 -> 与视频卡片完全一致 ---------- 视频卡实测:168x172 = 封面 95(16:9) + 信息区 77 信息区 = padding8 + 标题36(13px/18px 两行) + 间距4 + 副标题21 + padding8 专栏卡原为 168x166(封面 95 + 底部 61,标题 15px/22.5px 三行), 这里把外框/圆角/阴影/字号行高全部对齐视频卡,并把信息区锁成 77px, 保证两列网格里两种卡片等高、标题基线一致。 */ body.bili-space-page .route_home .article-section .article-list { margin-top: 20px !important; } body.bili-space-page .article-card { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 172px !important; max-height: 172px !important; min-height: 172px !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; display: block !important; position: relative !important; float: none !important; background: #ffffff !important; border: none !important; border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0,0,0,0.08) !important; overflow: hidden !important; } /* 封面区:原生是用 padding-top 比例盒(或定高 95px)撑开、 封面 绝对定位铺满的。上一版在这里写了 height:auto + padding:0,把比例盒清零 -> 封面塌成 0 高, 文字就顶到了卡片最上面。这里只改外观,不再碰 height/padding, 比例交给原生(168x95 本身就是 16:9)。 */ body.bili-space-page .article-card__top { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; box-sizing: border-box !important; /* 兜底:高度算不出来时用 16:9 撑开(定高或比例盒在时不起作用) */ aspect-ratio: 16 / 9 !important; position: relative !important; float: none !important; border: none !important; border-radius: 8px 8px 0 0 !important; overflow: hidden !important; background: transparent !important; } body.bili-space-page .article-card__cover { display: block !important; width: 100% !important; max-width: 100% !important; margin: 0 !important; border: none !important; border-radius: 8px 8px 0 0 !important; overflow: hidden !important; background: transparent !important; } /* 缩略图兜底:万一原生高度没算出来,用 16:9 撑住, 图片铺满并按 cover 裁切,保证任何情况下都有封面 */ body.bili-space-page .article-card__top > .article-card__cover, body.bili-space-page .article-card__top .bili-cover-card__thumbnail { position: absolute !important; inset: 0 !important; width: 100% !important; height: 100% !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-space-page .article-card__top img, body.bili-space-page .article-card__cover img, body.bili-space-page .article-card__top .b-img__inner { width: 100% !important; max-width: 100% !important; height: 100% !important; object-fit: cover !important; display: block !important; margin: 0 !important; padding: 0 !important; border: none !important; border-radius: 0 !important; box-sizing: border-box !important; } body.bili-space-page .article-card__bottom { width: 100% !important; height: 77px !important; min-height: 77px !important; max-height: 77px !important; margin: 0 !important; padding: 8px !important; box-sizing: border-box !important; background: transparent !important; display: flex !important; flex-direction: column !important; justify-content: center !important; align-items: stretch !important; text-align: center !important; overflow: hidden !important; } body.bili-space-page .article-card__bottom .title { display: -webkit-box !important; -webkit-line-clamp: 2 !important; -webkit-box-orient: vertical !important; overflow: hidden !important; text-align: center !important; width: 100% !important; font-size: 13px !important; line-height: 18px !important; min-height: 36px !important; max-height: 36px !important; color: #18191c !important; font-weight: 500 !important; margin: 0 !important; padding: 0 !important; word-break: break-all !important; text-decoration: none !important; } /* 标题内层再套了一层 .vui_ellipsis.multi-mode, 让它退化为普通内联文本,避免两层 clamp 叠出额外高度 */ body.bili-space-page .article-card__bottom .title > * { display: inline !important; font-size: 13px !important; line-height: 18px !important; color: #18191c !important; font-weight: 500 !important; -webkit-line-clamp: unset !important; -webkit-box-orient: unset !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; text-align: center !important; white-space: normal !important; } /* ---------- 3) 顶部 tab 蓝色位置条:改为跟随选中项自身定位 ---------- 原因:脚本用 fixSpaceNavTabs() 把「收藏夹/订阅」等 tab 设为 display:none, 站内那条蓝色条是按 tab 序号/旧布局算出来的绝对定位元素, 隐藏若干 tab 后算出的 left 就对不上了(每次刷新还不一定一样)。 做法:隐藏所有非 的兄弟节点(即那条 JS 定位的滑动条), 改由选中项自身的 ::after 画线,位置永远等于该项实际位置。 */ body.bili-space-page .nav-tab { position: relative !important; } body.bili-space-page .nav-tab > *:not(a) { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } body.bili-space-page .nav-tab::before, body.bili-space-page .nav-tab::after, body.bili-space-page .nav-tab > a.nav-tab__item::before { content: none !important; display: none !important; } body.bili-space-page .nav-tab > a.nav-tab__item { position: relative !important; } body.bili-space-page .nav-tab > a.nav-tab__item::after { content: '' !important; display: block !important; position: absolute !important; left: 0 !important; right: 0 !important; bottom: 0 !important; width: auto !important; min-width: 0 !important; height: 3px !important; margin: 0 !important; padding: 0 !important; border: none !important; border-radius: 2px !important; transform: none !important; transition: none !important; background: transparent !important; } body.bili-space-page .nav-tab > a.nav-tab__item.active::after, body.bili-space-page .nav-tab > a.nav-tab__item.router-link-exact-active::after { background: #00aeec !important; } body.bili-space-page .nav-tab > a.nav-tab__item.active, body.bili-space-page .nav-tab > a.nav-tab__item.router-link-exact-active { color: #00aeec !important; } /* ---------- 4a) 投稿 → 图文:筛选栏「全部图文 / 专栏 / 动态」上下间距 8px ---------- v131.42 结构(探针 09-27_18-12-07 实测,route_uploadOpus): .upload-content[360x2422](脚本设的 padding: 0 8px 16px) > .opus[344x2406] > .opus-header[344x34](全部图文 专栏 动态) > .opus-header__top[344x34] > .content-filter > .content-tab(.active = 全部图文 / 专栏 / 动态) > .opus-body[344x2222] > .opus-feed > .masonry_grid_v2 目标(与卡片间距 8px 一致): ① 胶囊栏「视频 图文 音频」底 → 筛选栏顶 = 8 = upload-sidenav 自带的 padding-bottom 8 + .opus / .opus-header 的 margin-top 归零 ② 筛选栏底 → 卡片顶 = 8 = .opus-header margin-bottom 8 + .opus-body margin-top 归零 站内原生在 header 与 body 之间留了很大空档(旧探针里 .opus 2406 − header 34 − body 2222 = 150),这里一并归零。 ------------------------------------------------------------ */ body.bili-space-page .opus { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* v131.43 修正下边距:v131.42 把 .opus-header 和它的子元素 .opus-header__top 都设了 margin-bottom: 8px,父子两层嵌套叠加 -> 实际间距 16px。这里只保留外层 .opus-header 一个 8, 内层 __top 的 margin 全部归零。 */ /* v131.44 下边距仍偏高 -> 说明 margin 不是唯一来源。 既然 header 内部可能还挂着别的子节点(或站内给它留了固有高度), 这里不再猜内容,直接把「筛选栏这一行的可见高度」锁死为 34px (探针实测筛选胶囊就是 34 高)+ overflow:hidden 裁掉多余, 下方间距就恒等于 margin-bottom 8px,与内容多寡无关。 */ body.bili-space-page .opus-header { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 34px !important; min-height: 34px !important; max-height: 34px !important; margin: 0 0 8px 0 !important; padding: 0 !important; box-sizing: border-box !important; overflow: hidden !important; } body.bili-space-page .opus-header__top { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 34px !important; min-height: 34px !important; max-height: 34px !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; overflow: hidden !important; } /* v131.46 筛选栏整行居中(纯 CSS,不用 JS 量宽度)。 探针 09-28_17-02-40 实测:.content-filter[344x34] 内三个 .content-tab 分别 130 / 82 / 48,gap 8 -> 内容总宽 276, 右侧空 68,故为左对齐(left 依次 8 / 146 / 236)。 只加 justify-content:center 即可让 276 在 344 内左右各留 34, 不动 tab 自身宽度 / padding / flex,避免连带改变胶囊外观。 */ body.bili-space-page .content-filter { display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: center !important; gap: 8px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 34px !important; min-height: 34px !important; max-height: 34px !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; overflow: hidden !important; } body.bili-space-page .content-tab { margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* v131.46 兜底:沿用 v131.36 的思路,把完整链路写进选择器抬高特异性, 防止站内晚加载的同权重 !important 规则把居中顶掉。 */ body.bili-space-page main.space-main .upload-content .opus > .opus-header > .opus-header__top > .content-filter { justify-content: center !important; } /* ---------- 4a-2) 投稿 → 图文:筛选栏竖条居中 + 文字缩小 ---------- v131.47 探针 09-28_17-15-03 实测(居中已生效): .content-filter[344x34](justify-content:center 生效) > .content-tab[130x24] left=42 全部图文 > .content-tab.active[82x24] left=180 专栏 > .content-tab[48x24] left=270 动态 竖条在探针里查不到——探针只遍历真实元素,竖条是 .content-tab 的 伪元素。但宽度把它暴露了:「动态」是最后一个胶囊、身后没有竖条, 48 = 2 字 + 左右内边距;「专栏」2 字却占 82,「全部图文」4 字占 130, 两者都比纯文字多出约 34px。这 34px 就是**在文档流里**的竖条 (约 1px 线 + 左右各 16px 外边距),它长在胶囊内部右侧。 于是「文字 → 竖条」只有 16,而「竖条 → 下一个胶囊的文字」是 16 + 我们设的 gap 8 + 对方内边距 —— 竖条自然偏向左边那个按键, 这正是用户看到的不平分。 解法(依旧纯 CSS): ① 先把站内挂在 ::before / ::after 上的竖条全部停掉, 让胶囊宽度回归纯文字宽度; ② 竖条由我们自己用 ::after 重画,position:absolute + left:100% + margin-left:3.5px —— 即落在 8px 间隙的正中 ((8 − 1) / 2 = 3.5),与左右两个按键严格等距; ③ 字号 14 → 12,行高锁 24 保持胶囊高度与垂直居中不变。 绝对定位的竖条不占文档流,所以 gap 就是纯粹的文字间距, 整行仍由 .content-filter 的 justify-content:center 居中。 ------------------------------------------------------------ */ body.bili-space-page .content-filter > .content-tab { position: relative !important; height: 24px !important; min-height: 24px !important; max-height: 24px !important; margin: 0 !important; padding: 0 !important; border: none !important; box-sizing: border-box !important; font-size: 12px !important; line-height: 24px !important; flex: 0 0 auto !important; } /* ① 停掉站内自绘的竖条(两个伪元素都清,避免漏网或重复) */ body.bili-space-page .content-filter > .content-tab::before { display: none !important; content: none !important; } body.bili-space-page .content-filter > .content-tab::after { display: none !important; content: none !important; } /* ② 我们自己画:只在非最后一个胶囊的右侧、8px 间隙正中 */ body.bili-space-page .content-filter > .content-tab:not(:last-child)::after { display: block !important; content: '' !important; position: absolute !important; top: 50% !important; left: 100% !important; right: auto !important; width: 1px !important; height: 10px !important; margin: 0 0 0 3.5px !important; padding: 0 !important; border: none !important; border-radius: 0 !important; transform: translateY(-50%) !important; background: #e5e9ef !important; } /* 高特异性兜底:与 v131.36 / v131.46 同一套路, 防站内晚加载的同权重 !important 把上面这组顶掉。 */ body.bili-space-page main.space-main .upload-content .opus > .opus-header > .opus-header__top > .content-filter > .content-tab { position: relative !important; margin: 0 !important; padding: 0 !important; border: none !important; font-size: 12px !important; line-height: 24px !important; } /* v131.43:下游这一串的上边距全部归零, 下边距只由 .opus-header 那一个 8px 提供,避免任何一处再叠加。 */ body.bili-space-page .opus-body { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } body.bili-space-page .opus-body .opus-feed, body.bili-space-page .opus-body .opus-feed .masonry_grid_v2, body.bili-space-page .opus-body .opus-feed .masonry_grid_v2 > .container, body.bili-space-page .opus-body .opus-feed .masonry_grid_v2 > .container > .item { margin-top: 0 !important; padding-top: 0 !important; min-height: 0 !important; box-sizing: border-box !important; } /* v131.44 兜底:瀑布流是 JS 布局,站内常给容器留 min-height / 给首个 item 写 transform 偏移。这里把首行之前的一切偏移清掉。 */ body.bili-space-page .opus-body .opus-feed .masonry_grid_v2 > .container > .item:first-child, body.bili-space-page .opus-body .opus-feed .masonry_grid_v2 > .container > .item:nth-of-type(1) { margin-top: 0 !important; padding-top: 0 !important; transform: none !important; translate: none !important; } /* ---------- 4) 投稿 → 图文:瀑布流改两列网格,卡片对齐主页专栏卡 ---------- 结构:.opus-feed > .masonry_grid_v2 > .container[814 宽,JS 瀑布流] > .item(绝对定位 + transform) > .opus-card > .opus-card--with-cover > [a.opus-card__cover(缩略图 + stats), a.opus-card__title] 原卡 189x187(封面 142 + 标题 46),瀑布流列宽由 JS 算。 (!important 的作者样式优先级高于站内 JS 写在 style 属性里的普通声明, 所以 transform / position / width 都能直接压住。) */ /* 外层只做"满宽容器",绝不能再设成网格: 一旦 .masonry_grid_v2 也是两列网格,它唯一的子元素 .container 就会变成占一列的网格项,宽度掉到 (344-8)/2 = 168, 卡片再被挤成 80x172,整块内容就都堆在屏幕左半边了。 */ body.bili-space-page .opus-feed .masonry_grid_v2 { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; position: static !important; overflow: visible !important; } body.bili-space-page .opus-feed .masonry_grid_v2 > .container { display: grid !important; grid-template-columns: repeat(2, minmax(0, 1fr)) !important; grid-auto-rows: auto !important; gap: 8px !important; row-gap: 8px !important; column-gap: 8px !important; align-items: start !important; align-content: start !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; position: static !important; overflow: visible !important; list-style: none !important; } body.bili-space-page .opus-feed .masonry_grid_v2 > .container > .item { position: static !important; transform: none !important; translate: none !important; left: auto !important; top: auto !important; right: auto !important; bottom: auto !important; inset: auto !important; width: auto !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; display: block !important; visibility: visible !important; opacity: 1 !important; float: none !important; } /* 卡片本体:与主页专栏卡同一套外观(白底 / 8px 圆角 / 同一条阴影 / 172 高) */ body.bili-space-page .opus-card, body.bili-space-page .opus-card--with-cover { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 172px !important; min-height: 172px !important; max-height: 172px !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; display: block !important; position: relative !important; float: none !important; background: #ffffff !important; border: none !important; border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0,0,0,0.08) !important; overflow: hidden !important; } body.bili-space-page .opus-card__cover { display: block !important; position: relative !important; float: none !important; width: 100% !important; max-width: 100% !important; height: 95px !important; min-height: 95px !important; max-height: 95px !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; border: none !important; border-radius: 8px 8px 0 0 !important; overflow: hidden !important; background: transparent !important; } body.bili-space-page .opus-card__cover > .bili-cover-card__thumbnail, body.bili-space-page .opus-card__cover > a.bili-cover-card__thumbnail { position: absolute !important; inset: 0 !important; width: 100% !important; height: 100% !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-space-page .opus-card__cover img, body.bili-space-page .opus-card__cover .b-img__inner { width: 100% !important; max-width: 100% !important; height: 100% !important; object-fit: cover !important; display: block !important; margin: 0 !important; padding: 0 !important; border: none !important; border-radius: 0 !important; box-sizing: border-box !important; } /* 标题区:高度仍是 77px(= 卡片 172 − 封面 95),但内部不再留白。 v131.48 探针 09-28_17-02-40 实测: a.opus-card__title[168x77](pt 8 / pb 8,overflow hidden) > div.vui_ellipsis.multi-mode[152x36] ← 只有这一个子节点 关键差异:别的页面卡片(主页 / 投稿视频 / 专栏)标题下方还有一行 日期·播放量之类的元信息,所以 77px 里必须给那行留位置; 而图文卡的标题盒里**只有文字**,没有任何元信息行, 旧规则照抄 77px 结构 + 2 行截断,就白扔掉了 77 − 8×2 − 36 = 25px 的空白。 可用文字高度 = 77 − 上下 padding 8×2 = 61px: 2 行 × 18 = 36(旧,浪费 25) 3 行 × 20 = 60(新,只剩 1px 余量,几乎填满) 4 行 × 18 = 72 > 61(放不下,会被 overflow 裁掉半行) 所以取 3 行,并把行高从 18 提到 20 —— 字号仍是 13px 不变, 多出的 2px 行距既补足高度,也让三行不至于挤在一起。 */ body.bili-space-page .opus-card__title { display: block !important; width: 100% !important; max-width: 100% !important; height: 77px !important; min-height: 77px !important; max-height: 77px !important; padding: 8px !important; margin: 0 !important; box-sizing: border-box !important; text-align: center !important; text-decoration: none !important; background: transparent !important; overflow: hidden !important; } body.bili-space-page .opus-card__title > * { display: -webkit-box !important; -webkit-line-clamp: 3 !important; line-clamp: 3 !important; -webkit-box-orient: vertical !important; overflow: hidden !important; text-align: center !important; width: 100% !important; font-size: 13px !important; line-height: 20px !important; min-height: 60px !important; max-height: 60px !important; color: #18191c !important; font-weight: 500 !important; margin: 0 !important; padding: 0 !important; word-break: break-all !important; white-space: normal !important; } /* v131.48 兜底:站内 .vui_ellipsis.multi-mode 自己带一套 line-clamp / max-height(探针里它被压成 152x36 就是这套在起作用), 用完整链路抬特异性,确保 3 行 / 60px 压得住。 */ body.bili-space-page .opus-card .opus-card--with-cover > a.opus-card__title > .vui_ellipsis.multi-mode { display: -webkit-box !important; -webkit-line-clamp: 3 !important; line-clamp: 3 !important; -webkit-box-orient: vertical !important; overflow: hidden !important; width: 100% !important; max-width: 100% !important; height: 60px !important; min-height: 60px !important; max-height: 60px !important; font-size: 13px !important; line-height: 20px !important; text-align: center !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; word-break: break-all !important; white-space: normal !important; text-overflow: ellipsis !important; } /* ------------------------------------------------------------ v132.59 投稿 → 图文:纯文字卡片(.opus-card--only-text)补封面占位 探针实测(route_uploadOpus,图文页): 带封面卡 .opus-card--with-cover[168x172] ├─ a.opus-card__cover [168x95] (封面 + 左下角点赞数) └─ a.opus-card__title [168x77] (pt/pb 8,文字 3 行 60) 纯文字卡 .opus-card--only-text [168x65] ← 只有 65 高,矮了一大截 ├─ a > .opus-card__text [148x20] (pt/pb 10) └─ .opus-card__icons [148x17] mt=8 (点赞数) 两者同在 .item[168x172] 里,视觉上一高一矮很不整齐。 改法(纯 CSS,无需 JS —— 类名 .opus-card--only-text 稳定): ① 卡片高度拉到 172,padding-top 95 让出封面位 → 剩下 172 − 95 = 77,正好等于带封面卡片的标题区高度; ② ::before 画占位封面:absolute 钉在顶部 95 高、满宽, flex 居中放「未发现封面图」,浅蓝底 + B站蓝字, 圆角 8px 8px 0 0 与 .opus-card__cover 完全一致; ③ 文字区 a 与 .opus-card__title 同款(77 高 / 上下 8 / 3 行 60 居中); ④ 点赞数移到占位封面左下(top 74 / left 8), 与带封面卡片里 .bili-cover-card__stats 压在封面左下角的位置对齐。 ::before 加 pointer-events:none,不挡文字链接与任何点击。 ------------------------------------------------------------ */ body.bili-space-page .opus-card--only-text { position: relative !important; display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: 172px !important; min-height: 172px !important; max-height: 172px !important; /* 顶部 95 让给占位封面,余下 77 = 带封面卡片的标题区高度 */ padding: 95px 0 0 !important; margin: 0 !important; box-sizing: border-box !important; background: #ffffff !important; border: none !important; border-radius: 8px !important; overflow: hidden !important; } /* ② 占位封面:浅蓝底 + 居中「未发现封面图」 */ body.bili-space-page .opus-card--only-text::before { content: "未发现封面图" !important; position: absolute !important; top: 0 !important; left: 0 !important; right: 0 !important; width: auto !important; height: 95px !important; display: flex !important; align-items: center !important; justify-content: center !important; background: #e6f4ff !important; color: #00a1d6 !important; font-size: 13px !important; line-height: 20px !important; font-weight: 500 !important; text-align: center !important; border-radius: 8px 8px 0 0 !important; pointer-events: none !important; z-index: 0 !important; } /* ③ 文字区:与 .opus-card__title 同款 */ body.bili-space-page .opus-card--only-text > a { display: block !important; width: 100% !important; height: 77px !important; min-height: 77px !important; max-height: 77px !important; padding: 8px !important; margin: 0 !important; box-sizing: border-box !important; text-align: center !important; text-decoration: none !important; background: transparent !important; overflow: hidden !important; } body.bili-space-page .opus-card--only-text .opus-card__text, body.bili-space-page .opus-card--only-text .opus-card__text > .vui_ellipsis.multi-mode { display: -webkit-box !important; -webkit-line-clamp: 3 !important; line-clamp: 3 !important; -webkit-box-orient: vertical !important; overflow: hidden !important; width: 100% !important; max-width: 100% !important; height: 60px !important; min-height: 60px !important; max-height: 60px !important; font-size: 13px !important; line-height: 20px !important; text-align: center !important; color: #18191c !important; font-weight: 500 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; word-break: break-all !important; white-space: normal !important; } /* ④ 点赞数:挪到占位封面左下,对齐带封面卡片的 .bili-cover-card__stats */ body.bili-space-page .opus-card--only-text .opus-card__icons { position: absolute !important; top: 74px !important; left: 8px !important; width: auto !important; height: 17px !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; display: flex !important; align-items: center !important; color: #00a1d6 !important; font-size: 11px !important; line-height: 17px !important; pointer-events: none !important; z-index: 1 !important; } /* ---------- 4a-3) 投稿 → 图文 → 专栏:隐藏封面底部的播放量浮层 ---------- v131.52 探针 09-28_18-44-13 实测(route_uploadOpus,专栏 tab): .opus-collection[344x209] mb=30 > .opus-collection-wrap[344x208] > .opus-collection-list[344x208] pb=30 ← 一行 5 张 > .upload-opus-collection-card[51x178] ×5 > .upload-opus-collection-card__top[51x68] > a.upload-opus-collection-card__cover[51x68] (pt=68.4062px,用 padding 撑封面比例) > .bili-cover-card__thumbnail[51x68](absolute) > **.bili-cover-card__stats[51x34]** (absolute z=2,pt=14 pb=4,top=230→264) text = 30万 1篇专栏 / 111.7万 4篇专栏 … > .upload-opus-collection-card__bottom[51x74] (a.title[51x44] + .subtitle[51x26]) 用户要去掉的「眼睛图标 30万 / 眼睛图标 111.7万」就是 .bili-cover-card__stats 这层浮在封面底部的半透明条。 ⚠ 一个硬限制:探针显示 .bili-cover-card__stats **没有任何子元素**, 「30万」和「1篇专栏」是同一个 div 里的两串文本节点 (眼睛图标走伪元素 / 图标字体),CSS 能选到的最小粒度就是这个 div 整体 —— 没法只挑出播放量那一串而留下「N篇专栏」。 所以这里整层 display:none,「N篇专栏」会一并消失。 真要只留「N篇专栏」,就只能上 JS 遍历文本节点,与"尽量用 CSS"相悖。 封面本体不受影响:比例是靠 a 上的 padding-top:68.4062px 撑的, 与 stats 无绝对定位依赖,隐藏后封面不会塌陷。 作用域严格锁在 .upload-opus-collection-card 内 —— 全部图文 tab 的 .opus-card 封面同样挂着 .bili-cover-card__stats (显示 379 / 420 之类播放量),那处用户没要求动,绝不能误伤。 ------------------------------------------------------------ */ body.bili-space-page .opus-collection-list .upload-opus-collection-card .bili-cover-card__stats { display: none !important; visibility: hidden !important; opacity: 0 !important; } body.bili-space-page .upload-opus-collection-card__cover > .bili-cover-card__stats { display: none !important; } /* 高特异性兜底(v131.36 套路):完整链路压过去, 防站内晚加载的同权重 !important 把浮层放回来。 */ body.bili-space-page main.space-main.route_uploadOpus .opus-collection > .opus-collection-wrap > .opus-collection-list > .upload-opus-collection-card > .upload-opus-collection-card__top > a.upload-opus-collection-card__cover > .bili-cover-card__stats { display: none !important; visibility: hidden !important; opacity: 0 !important; } /* ---------- 4a-4) 投稿 → 图文 → 专栏:5 卡一行的间距统一 8px ---------- v131.53 探针 09-28_18-44-13 实测(route_uploadOpus,专栏 tab): .opus-collection[344x209] left=8 mb=30 > .opus-collection-wrap[344x208] left=8 > .opus-collection-list[344x208] left=8 pb=30 > .upload-opus-collection-card[51x178] ×5 5 张卡的实测 left 与宽度: ①left=8 ②left=81 ③left=154 ④left=228 ⑤left=301,每张 w=51 由此反推现状: 卡间间隙 = 81-59=22 / 154-132=22 / 228-205=23 / 301-279=22 末卡右边 = 301+51 = 352 = 容器右边(8+344) 5×51 + (22+22+23+22) = 255 + 89 = 344 ✓ 正好占满 即:**左右边距本来就是 8px**(首卡 left=8 距屏幕左边 8, 末卡右边 352 距屏幕右边 360-352 = 8),需要改的只有卡间间隙。 22/22/23/22 这种"余数摊不平"的分布,是 flex + space-between 把 344-255=89 的剩余空间平分给 4 个间隙的结果(89/4=22.25)。 改法:不跟站内的定宽 + space-between 纠缠,直接把容器改成 5 等分网格 + gap 8,卡片宽度交还给网格(width:100% 填满列): 列宽 = (344 - 4×8) / 5 = 312 / 5 = 62.4px 卡间 = 8px 恒定,不再有 22/23 的抖动 首末 = 8px(网格铺满 344,与容器 padding 一致)→ 距屏幕边缘仍 8px ✓ 用 grid 而非 flex 的原因:62.4 是小数,flex 定宽要写 calc 且 仍可能因舍入差 1px;grid 的 1fr 由浏览器精确分配,天然铺满。 注意保留 .opus-collection-list 自带的 padding-bottom:30 (那是这一行到下方内容的纵向间距,不在本次需求内), 所以只写 grid / gap / width,不碰 padding 与 margin。 ------------------------------------------------------------ */ body.bili-space-page .opus-collection-list { display: grid !important; grid-template-columns: repeat(5, minmax(0, 1fr)) !important; grid-auto-rows: auto !important; grid-auto-flow: row !important; gap: 8px !important; row-gap: 8px !important; column-gap: 8px !important; justify-content: stretch !important; justify-items: stretch !important; align-items: start !important; align-content: start !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; float: none !important; } /* 卡片宽度交还网格:站内那套 51px 定宽必须让位, 否则 5×51 + 4×8 = 287 < 344,右边会空出 57px, "末卡距屏幕右边 8px"就不成立了。 */ body.bili-space-page .opus-collection-list > .upload-opus-collection-card { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; float: none !important; box-sizing: border-box !important; } /* 卡内各层同样满宽跟随,否则封面 / 标题仍按 51px 排, 卡片变宽后右侧会留白 */ body.bili-space-page .opus-collection-list .upload-opus-collection-card__top, body.bili-space-page .opus-collection-list .upload-opus-collection-card__bottom, body.bili-space-page .opus-collection-list a.upload-opus-collection-card__cover, body.bili-space-page .opus-collection-list .upload-opus-collection-card__bottom > .title, body.bili-space-page .opus-collection-list .upload-opus-collection-card__bottom > .subtitle, body.bili-space-page .opus-collection-list .upload-opus-collection-card .vui_ellipsis { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } /* 高特异性兜底(v131.36 套路) */ body.bili-space-page main.space-main.route_uploadOpus .opus-collection > .opus-collection-wrap > .opus-collection-list { display: grid !important; grid-template-columns: repeat(5, minmax(0, 1fr)) !important; gap: 8px !important; row-gap: 8px !important; column-gap: 8px !important; } body.bili-space-page main.space-main.route_uploadOpus .opus-collection > .opus-collection-wrap > .opus-collection-list > .upload-opus-collection-card { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; } /* ============================================================ ========== 专栏文集页(/read/readlist/rlXXX)头部整块居中 ========== v131.54 探针 09-29_07-17-17 实测(body.bili-opus-page,360x951): #app[360x943] > .container[344x879] left=8 pt=27 pb=30 ← 8..352,本身已居中 > .list[344x822] top=99 > .list-header[344x226] left=8 pt=20 pb=20 mb=10 > **.list-header__right[294x186] left=38** pt=6 > .title[275x36] left=57 「宝可梦剧场版 神速的炎帝与圈圈熊觉醒」 > .up-info[275x42] left=57 mt=13 mb=10 > .up-info__holder[42x42] left=57 (头像 24 + 挂件 42) > .up-name[89x42] left=103 (.up-name-text 50 + .nameplate 25) > button.follow-btn.on[49x24] left=206 「已关注」 > .desc[275x30] left=57 mb=12 > div[30x30] left=57 「4篇专栏」 > div[38x30] left=97 「7591个字」 > div[57x30] left=145 「1117550次阅读」 > div[50x30] left=212 「2023-11-26更新」 > div[50x30] left=272 「文集号:771595」 > .list-content[344x586] 不居中的根因:这一栏是 PC 版的「左封面 + 右信息」两栏布局。 左侧封面位在手机站没渲染出来(探针里查不到 .list-header__left), 但它的占位还在 —— .list-header__right 被推到 left=38 (距容器左边 30,距右边 352-332 = 20),自身又是 294 定宽, 里面的 .title / .up-info / .desc 全是 275 定宽左对齐, 所以标题、UP 行、5 个统计项统统偏在左半边。 ⚠ 还有一处对不上账:.list-header__right 高 186, pt6 + title36 + up-info(13+42+10) + desc(30+12) = 149, 差 37px 在探针里找不到归属元素 —— 用户报的最后一行 「宝可梦同人剧场版」在整份探针里 grep 也是 0 命中, 这 37px 极可能就是它(探针漏遍历,和上一版 .desc 被漏掉同因)。 所以这里**不逐个点名**,改用 .list-header__right > * 通配, 让探针没列出来的那一行也一并被覆盖到。 改法(三步,纯 CSS): ① .list-header 打散成单列块级,隐藏 PC 的左侧封面占位; ② .list-header__right 满宽 344 + margin auto,回到正中; ③ 右栏每个直接子节点满宽 + text-align:center + justify-content:center 双通路(块级文本 / flex 行都能命中), 其中 .up-info 的头像+名字+关注按钮、.desc 的 5 个统计项 都是"整组居中",组内相对位置不变。 只写 margin-left/right: auto,**不写 margin 简写**, 以保住 .up-info 的 mt13/mb10 与 .desc 的 mb12 这些纵向节奏。 ------------------------------------------------------------ */ /* ⑦ 头部整块(标题 / UP 行 / 统计 / 简介)左右各留 8px(v131.59) 探针 09-29_10-24-31 实测(视口 360): .container[344x29700] left=8 ← 8..352,容器本身距屏幕已 8px > .list[344x29643] left=8 > .list-header[344x268] **left=8 宽 344** ← 与容器同宽 > .list-header__right[344x228] left=8 > .title / .up-info / .desc / .introduce 全是 344 即头部内容 344 宽 = 容器 344 宽 -> **距容器左右边距 = 0**, 标题、UP 行、5 个统计项、简介统统顶到容器边上。 改法:.list-header 外框保持 344(仍占 8..352), 只加 padding-left/right: 8px + box-sizing: border-box, 内容盒自然收成 328(占 16..344)—— 距容器左右各 8px。 ⚠ 只写 padding-left/right,**不写 padding 简写**, 以保住站内 .list-header 原本的 pt20 / pb20 纵向节奏。 下面的 .list-header__right 与其子节点都是 width:100%, 会自动继承这 328 —— 不需要逐个改数字。 注意:只缩头部。**章节列表 .list-content 仍是 344(8..352)**, 这是 v131.55 为了和全站其它页对齐才去掉的内边距,本轮不动。---- */ body.bili-opus-page .list > .list-header { display: block !important; width: 100% !important; max-width: 100% !important; padding-left: 8px !important; padding-right: 8px !important; box-sizing: border-box !important; text-align: center !important; } /* 高特异性兜底(v131.36 套路):完整链路压过去, 防站内 padding:0 用同权重 !important 顶回来 */ body.bili-opus-page #app > .container > .list > .list-header { padding-left: 8px !important; padding-right: 8px !important; box-sizing: border-box !important; } /* PC 版左侧封面位:手机站没渲染内容,但占位把右栏推偏了 30px */ body.bili-opus-page .list > .list-header > .list-header__left { display: none !important; width: 0 !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; } body.bili-opus-page .list > .list-header > .list-header__right { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin-left: auto !important; margin-right: auto !important; padding-left: 0 !important; padding-right: 0 !important; box-sizing: border-box !important; float: none !important; text-align: center !important; } /* 右栏每一行:满宽 + 居中(含探针漏掉的那一行) */ body.bili-opus-page .list-header__right > * { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin-left: auto !important; margin-right: auto !important; box-sizing: border-box !important; float: none !important; text-align: center !important; justify-content: center !important; } /* 5 个统计项:本身按内容宽,只补文字居中; flex 场景下不许被拉伸,否则"整组居中"会变成"每项等分铺满" v131.55 修正:这里原来写的 flex: 0 0 auto(禁止收缩)是错的。 探针 09-29_09-04-35 实测后果 —— 5 项各取自然单行宽 67 / 75 / 74 / 59 / 105,合计 380 + 间隙 ≈ 412 > 344, 整行从 left=-34 一直伸到 385,两端都戳出屏幕(视口只有 360)。 改成允许换行:单项仍不收缩(保持一行不断字), 但父容器 .desc 开 flex-wrap,装不下就整项折到第二行。 */ body.bili-opus-page .list-header__right .desc > * { flex: 0 0 auto !important; float: none !important; width: auto !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; line-height: normal !important; white-space: nowrap !important; text-align: center !important; justify-content: center !important; } /* UP 行同理:头像 / 名字 / 关注按钮保持原尺寸,整组居中 v131.57:补 flex-wrap,超长昵称 + 按钮装不下时整组折行,不再溢出屏幕 */ body.bili-opus-page .list-header__right .up-info { flex-wrap: wrap !important; row-gap: 6px !important; column-gap: 4px !important; } body.bili-opus-page .list-header__right .up-info > * { flex: 0 0 auto !important; } /* 高特异性兜底(v131.36 套路):完整链路压过去, 防站内 PC 的定宽 294 / 275 用同权重 !important 顶回来 */ body.bili-opus-page #app > .container > .list > .list-header > .list-header__right { display: block !important; width: 100% !important; max-width: 100% !important; margin-left: auto !important; margin-right: auto !important; padding-left: 0 !important; padding-right: 0 !important; } body.bili-opus-page #app > .container > .list > .list-header > .list-header__right > .title, body.bili-opus-page #app > .container > .list > .list-header > .list-header__right > .up-info, body.bili-opus-page #app > .container > .list > .list-header > .list-header__right > .desc { width: 100% !important; max-width: 100% !important; min-width: 0 !important; text-align: center !important; justify-content: center !important; } /* ============================================================ ========== 专栏文集页:整页手机化(v131.55) ========== 探针 09-29_09-04-35("含隐藏"全量版,7629 节点,body 360x32219) 逐处实测与对策如下。 ------------------------------------------------------------ */ /* ---- ① 头部统计行「233篇专栏 974595个字 5752次阅读 9-16更新 文集号:1018845」 探针实测(v131.54 之后): .desc[344x10] left=8 mb=12 > div[67x10] left=-34 233篇专栏 ← 已经戳到屏幕外 > div[75x10] left=42 974595个字 > div[74x10] left=127 5752次阅读 > div[59x10] left=211 9-16更新 > div[105x10] left=280 文集号:1018845 ← 右端 385 > 360 5 项自然宽合计 380,加间隙约 412,远超 344,所以两端都溢出。 对策:父容器开 flex-wrap,整项折行(不在词中间断), 两行各自居中。实测会分成 4 项 + 1 项两行。---- */ body.bili-opus-page .list-header__right > .desc { display: flex !important; flex-direction: row !important; flex-wrap: wrap !important; justify-content: center !important; align-items: center !important; align-content: center !important; column-gap: 8px !important; row-gap: 4px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; box-sizing: border-box !important; } /* ---- ② 文集简介「你我均是不谙世事的幼兽……」默认全文展开 + 去掉展开按键 探针实测: .introduce[344x38] top=230 > .fold-text[344x19] ovf=hidden ← 只露 1 行(19px) > .unfold-btn[344x19] text=展开 .fold-text 是"全文已在 DOM 里、靠 overflow:hidden 裁成一行"的 典型折叠组件(高度 19 = 单行),所以纯 CSS 放开高度即可展开全文, 不需要 JS 去点那个按键。 按键用 > *:not(.fold-text) 通配隐藏 —— 展开态它会变成「收起」, 类名可能变,通配才不会被漏掉。---- */ body.bili-opus-page .list-header__right .introduce { width: 100% !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; box-sizing: border-box !important; } body.bili-opus-page .list-header__right .introduce .fold-text { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; -webkit-line-clamp: unset !important; line-clamp: unset !important; text-overflow: clip !important; white-space: normal !important; word-break: break-word !important; overflow-wrap: anywhere !important; line-height: 1.7 !important; box-sizing: border-box !important; } body.bili-opus-page .list-header__right .introduce > *:not(.fold-text) { display: none !important; } /* 高特异性兜底(v131.36 套路) */ body.bili-opus-page #app > .container > .list > .list-header > .list-header__right > .introduce > .fold-text { height: auto !important; max-height: none !important; overflow: visible !important; -webkit-line-clamp: unset !important; } /* ---- ③ 章节列表:序号与正文贴死 + 右侧封面整块跑到屏幕外 探针实测(单章): .list-content[344x31854] left=8 padding 20(左右各 20) > div[304x31814] left=28 > .list-content-item[304x137] left=28 pt=20 ├ .list-content-item-index[13x117] left=28 序号「1」 └ .list-content-item-container[304x117] left=41 ← 304 定宽 ├ .__left[304x117] left=41 │ ├ .title[304x29] ovf=hidden │ │ └ .title-text[79x26](长标题时实测 378 宽,被父级裁掉) │ ├ .desc[304x22] mt=5 ovf=hidden(章节摘录,1 行) │ └ .info-bar[304x24] mt=16(分类 / 阅读 / 点赞 / 评论) └ .__right[117x117] **left=365** ← 视口只有 360,整块在屏幕外 两个硬伤: a) 序号 13 宽结束于 41,正文正好从 41 起 —— 间隙 = 0,贴死; b) 13(序号) + 304(正文定宽) = 317 > 304(行宽),整行向右溢出 13, 连带把封面顶到 left=365 屏幕外;封面本身在窄屏也没意义。 对策: · .list-content 去掉左右 20 内边距 -> 行宽 304 变 344,与头部对齐 (首末距屏幕边缘都是 8px,和全站其它页一致) · 行改 flex,序号占固定 36px 列并居中(见下方 ⑥), 正文 flex:1 吃掉剩余宽度、min-width:0 允许收缩 · 封面 .__right 直接 display:none(顺带省掉 233 张懒加载图) · 长标题不再被硬裁:宽度收回 100% + 单行省略号 ---- */ body.bili-opus-page .list > .list-content { padding-left: 0 !important; padding-right: 0 !important; width: 100% !important; max-width: 100% !important; box-sizing: border-box !important; } body.bili-opus-page .list > .list-content > div { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } body.bili-opus-page .list-content-item { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: flex-start !important; justify-content: flex-start !important; /* ⑥ 序号列自带 36px(含与正文的视觉间距),所以行间距归零, 这样正文左边缘恒为 44,不随序号位数变化 */ gap: 0 !important; column-gap: 0 !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } /* ---- ⑥ 章节序号:两位数以上紧贴左边缘 -> 在剩余宽度内居中(v131.58) 探针 09-29_10-24-31 实测(视口 360,行 left=8 / 行宽 344): 序号 1 位:.list-content-item-index[13x29] left=8 正文 left=44 序号 2 位: [26x29] left=8 正文 left=44 序号 3 位: [39x29] left=8 正文 **left=57** 数字是**全角**字形(13px 字号 -> 每字符 13px 宽): 1 位 13px、2 位 26px、3 位 39px。 原写法 min-width:26 + text-align:right 的后果: · 2 位数:26px 正好填满 26px 的列,左边距 = 0 -> 紧贴容器左边缘; · 3 位数:39 > 26,flex 项目被撑宽 13px, 连带把正文从 left=44 顶到 left=57 —— 前 99 章和后面的章节对不齐。 改法(纯 CSS): · 整行 gap 归零,序号列**锁死 36px**(flex 0 0 36px + min/max 同值), 8 + 36 = 44,正文左边缘在任何位数下都稳定在 44,不再左右跳动; · 序号 text-align:center,数字在这 36px 里居中,左右自然留出空隙: 1 位 13px -> 两侧各 11.5px 2 位 26px -> 两侧各 5px(原来左侧 0,这就是"紧贴"的根因) 3 位 39px -> 见下 · 3 位数 39px 仍略大于 36px,两道保险把它压进去: a) font-size 13 -> 12px,全角 3 位降到 36px,刚好装下; b) 指定西文字体族,数字走半角字形(3 位约 20px), 即使 a) 被站内样式顶回也依然宽松; 两道都命中时也不会显得过小 —— 序号本就是辅助信息, 比正文标题(约 15px)略小才符合层级。 · overflow:visible + white-space:nowrap:极端情况下宁可两侧等宽溢出, 也不让数字被裁掉半截或把正文挤走。---- */ body.bili-opus-page .list-content-item-index { flex: 0 0 36px !important; width: 36px !important; min-width: 36px !important; max-width: 36px !important; height: auto !important; align-self: flex-start !important; /* 与 .title 的 29px 行高对齐,让序号和章节标题在同一水平线上 */ line-height: 29px !important; text-align: center !important; font-size: 12px !important; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif !important; white-space: nowrap !important; overflow: visible !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* 高特异性兜底(v131.36 套路):完整链路压过去, 防站内的 width:auto / min-width 用同权重 !important 顶回来 */ body.bili-opus-page .list > .list-content .list-content-item > .list-content-item-index { flex: 0 0 36px !important; width: 36px !important; min-width: 36px !important; max-width: 36px !important; text-align: center !important; font-size: 12px !important; overflow: visible !important; } body.bili-opus-page .list > .list-content .list-content-item { gap: 0 !important; column-gap: 0 !important; } body.bili-opus-page .list-content-item-container { flex: 1 1 auto !important; width: auto !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; box-sizing: border-box !important; } body.bili-opus-page .list-content-item-container__left { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } /* 每章右侧封面:按要求整块隐藏(本来就在屏幕外,留着只会撑破行宽) */ body.bili-opus-page .list-content-item-container__right { display: none !important; width: 0 !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; } body.bili-opus-page .list-content .title { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-opus-page .list-content .title > .title-text { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; box-sizing: border-box !important; } /* 章节摘录:由 1 行放开到 3 行(v131.56) 探针实测 .desc[288x22] ovf=hidden —— 单行 22px 硬裁。 这里改成 -webkit-box + line-clamp:3,配合 height:auto 让第三行露出来, 第 4 行起才省略号截断。 */ body.bili-opus-page .list-content .desc { display: -webkit-box !important; -webkit-box-orient: vertical !important; -webkit-line-clamp: 3 !important; line-clamp: 3 !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: normal !important; word-break: break-word !important; overflow-wrap: anywhere !important; line-height: 1.5 !important; height: auto !important; max-height: none !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } /* 高特异性兜底(v131.36 套路):压过站内同权重 !important 的单行裁切 */ body.bili-opus-page .list > .list-content .list-content-item .list-content-item-container .list-content-item-container__left > .desc { display: -webkit-box !important; -webkit-box-orient: vertical !important; -webkit-line-clamp: 3 !important; line-clamp: 3 !important; overflow: hidden !important; white-space: normal !important; height: auto !important; max-height: none !important; } /* 章节统计行「原创连载 / 阅读 / 点赞 / 评论」整行隐藏(v131.56) 探针实测 .info-bar[288x24] mt=16,是 .__left 的最后一个子节点, display:none 后行高自然收缩,序号与标题的相对位置不受影响。 */ body.bili-opus-page .list-content-item .info-bar, body.bili-opus-page .list-content .list-content-item .list-content-item-container__left > .info-bar { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* ---- ④ 顶部 #BiliMainHeader:一个 64px 的空壳 探针实测:#BiliMainHeader[344x64] top=8 bottom=72,无文字, 里面唯一的 .bili-header.bili-header--mini.bili-header--fixed 已经被脚本压成 [0x0] (hidden)。 即这 64px 是纯空白,手机站直接收掉,正文上移 64px。---- */ body.bili-opus-page #BiliMainHeader { height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* ---- ⑤ 取消关注确认弹窗「您确认要取消关注吗(゚Д゚≡゚д゚)!? 取消 确定」 整块跑到屏幕右外侧(v131.57) 探针 09-29_10-24-31 实测(视口 360): .dialog[344x29700] pos=absolute top=8 left=8 ← 蒙层,铺满整页 > .box[360x173] pos=**fixed** z=9999 top=320 **left=144** ← 144+360 = 504,右边 144px 全在屏幕外 > .text[360x23] top=370 left=144 > .buttons[360x40] top=423 left=144 > button.cancel[120x40] left=174 > button.confirm[120x40] left=354 ← 右端 474,戳出屏幕 根因:.box 是 PC 版的定宽 360 弹窗,用固定 left 偏移定位 (144 ≈ 40%×360),在 360 视口下既没居中也没收缩,右半边被裁掉。 改法(纯 CSS,不动任何 JS): · 蒙层 .dialog 改 fixed + inset:0,跟随视口而不跟着整页滚; · .box 用 left:50% + translate(-50%,-50%) 做视口正中定位, 同时把定宽 360 换成 max-width: calc(100vw - 16px), 窄屏(如 320)自动再收,永不越界; · 盒内留左右 16px 内边距,文案与按钮不会贴边; · .text / .buttons 收回 100% 满宽,跟着新宽度走; · 两个按钮加 flex-wrap,极端窄屏时上下排列而不是横向溢出。 注:translate 与 left/top 一起写,-50% 以自身尺寸为基准, 站内联样式即使改了 left 也不会破坏居中。---- */ body.bili-opus-page .dialog, body.bili-space-page .dialog { position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; width: 100vw !important; max-width: 100vw !important; height: 100vh !important; max-height: 100vh !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; overflow: hidden !important; } body.bili-opus-page .dialog > .box, body.bili-space-page .dialog > .box { position: fixed !important; top: 50% !important; left: 50% !important; right: auto !important; bottom: auto !important; transform: translate(-50%, -50%) !important; width: 344px !important; max-width: calc(100vw - 16px) !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: calc(100vh - 32px) !important; margin: 0 !important; padding-left: 16px !important; padding-right: 16px !important; box-sizing: border-box !important; overflow: visible !important; } body.bili-opus-page .dialog > .box > .text, body.bili-space-page .dialog > .box > .text { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; text-align: center !important; word-break: break-word !important; overflow-wrap: anywhere !important; } body.bili-opus-page .dialog > .box > .buttons, body.bili-space-page .dialog > .box > .buttons { display: flex !important; flex-direction: row !important; flex-wrap: wrap !important; justify-content: center !important; align-items: center !important; gap: 12px !important; column-gap: 12px !important; row-gap: 12px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; box-sizing: border-box !important; } body.bili-opus-page .dialog > .box > .buttons > button, body.bili-space-page .dialog > .box > .buttons > button { flex: 0 1 120px !important; width: 120px !important; max-width: calc(50% - 6px) !important; min-width: 0 !important; height: 40px !important; margin: 0 !important; box-sizing: border-box !important; } /* 高特异性兜底(v131.36 套路):完整链路压过去, 防站内的定宽 360 / 固定 left 用同权重 !important 顶回来 */ body.bili-opus-page #app > .container > .dialog > .box, body.bili-space-page #app > .container > .dialog > .box { position: fixed !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; width: 344px !important; max-width: calc(100vw - 16px) !important; box-sizing: border-box !important; } /* ============================================================ ========== 直播间页(live.bilibili.com/房间号) ========== ============================================================ 探针 bili_probe_2026-09-30_08-03-19 实测(视口 360): body.supportWebp > div.live-room-app[360x4458] > main.app-content(padding-top 74px) > div.link-navbar-ctnr (PC 顶部导航, fixed z=1000, h64) > div.app-body[980x4384] > section.player-and-aside-area[980x428] > #player-ctnr[668x428] > #head-info-vm[668x52] ← 主播信息条 > .fullscreen-container-paddingbox[668x376] (padding-top 375.75px) > #fullscreen-container (absolute) > .player-section[668x292] > #live-player-ctnr > #live-player > video > #gift-control-vm[668x84] ← 礼物栏 > #aside-area-vm (absolute left=680, w300) > #rank-list-vm[300x96] ← 排行榜 > .chat-history-panel[300x223] ← ★评论区(聊天区) > #chat-control-panel-vm[300x109] ← 发送框 > #sections-vm[980x3944] ← 动态/公告 > footer.link-footer > #sidebar-vm (右下角悬浮 实验室/关注) > #room-background-vm (房间背景图) 目标:整页只剩「播放器」+「评论区」两块,评论区落到播放器正下方。 · 播放器:3px #00aeec 边框 + 12px 圆角 + 左右各 8px + 顶部 36px + 16:9 —— 与番剧页 #bilibili-player-wrap 的数值逐项对齐(同一套 calc 公式)。 · 评论区:#aside-area-vm 由 absolute 改回 static,自然排到 #player-ctnr 之后。 全文纯 CSS,JS 只负责给 body 打上 bili-live-room-page(页面判定)。---- */ html:has(body.bili-live-room-page), body.bili-live-room-page { background: #ffffff !important; width: 100% !important; max-width: 100vw !important; min-width: 0 !important; overflow-x: hidden !important; padding-top: 0 !important; margin: 0 !important; } /* ---- 1) 除播放器 + 评论区外,其余板块全部隐藏 ---- */ body.bili-live-room-page #room-ssr-vm, body.bili-live-room-page .link-navbar-ctnr, body.bili-live-room-page .link-navbar-wrap, body.bili-live-room-page nav.link-navbar, body.bili-live-room-page #room-background-vm, body.bili-live-room-page #head-info-vm, body.bili-live-room-page #gift-control-vm, body.bili-live-room-page #rank-list-vm, body.bili-live-room-page #rank-list-ctnr-box, body.bili-live-room-page #gift-screen-animation-vm, body.bili-live-room-page #guard-popup, body.bili-live-room-page #sections-vm, body.bili-live-room-page footer.link-footer, body.bili-live-room-page #sidebar-vm, /* 播放器内的 PK / 礼物菜单 / 贴纸 / 连麦 等覆盖层 */ body.bili-live-room-page #universal-pk-vm, body.bili-live-room-page #gift-menu-vm, body.bili-live-room-page #interactive-sticker-vm, body.bili-live-room-page #multi-voice-index, /* v131.95:连麦(语音连线)入口条 探针实测 #link-mic-vm > .lin-mic-entrance [270x40], 浮在播放器正中(top=134,即画面垂直居中偏上), 上面写着「吃西瓜的夏冰 与主播通话中」,直接压在视频画面上。 它挂在 #businessContainerElement / .web-player-inject-wrap 里, 那两层只被设了 pointer-events:none(防挡点击),文字依然可见。 整条挂掉:父 #link-mic-vm + 子 .lin-mic-entrance 双写, 外加同级可能的 .link-mic-* 变体,纯 CSS 隐藏。 */ body.bili-live-room-page #link-mic-vm, body.bili-live-room-page #link-mic-vm .lin-mic-entrance, body.bili-live-room-page #link-mic-vm .link-mic-entrance, body.bili-live-room-page .lin-mic-entrance, body.bili-live-room-page .link-mic-entrance, body.bili-live-room-page .web-player-icon-roomStatus { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* 兜底:main 下凡是「不是 .app-body」的一律不显示(导航条 / 页脚 / 未知挂件) */ body.bili-live-room-page main.app-content > *:not(.app-body) { display: none !important; visibility: hidden !important; height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* ---- 2) 容器链拉直:980 定宽 -> 满宽,宽屏收窄并居中 ---- */ body.bili-live-room-page main.app-content, body.bili-live-room-page .app-body, body.bili-live-room-page section.player-and-aside-area { width: 100% !important; max-width: 820px !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; margin: 0 auto !important; padding: 0 !important; box-sizing: border-box !important; overflow: visible !important; position: relative !important; float: none !important; left: auto !important; right: auto !important; } /* ---- 3) 播放器:边框 / 边距 / 比例 与番剧页完全一致 ---- */ body.bili-live-room-page #player-ctnr { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; padding: 0 8px !important; /* 与番剧 .plp-player 一致:左右各 8px */ margin: 36px auto !important; /* 与番剧 .plp-player 一致:上下各 36px */ box-sizing: border-box !important; overflow: visible !important; position: relative !important; float: none !important; left: auto !important; right: auto !important; } /* 站内 JS 按 PC 宽度算出的 padding-top: 375.75px(= 668 * 9/16)用来撑 16:9, 窄屏下必须顶掉,改由下方 #fullscreen-container 自己定高 */ body.bili-live-room-page .fullscreen-container-paddingbox { width: 100% !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; box-sizing: border-box !important; } /* 与番剧页 #bilibili-player-wrap 同一套数值: 宽 100%(含 3px 边框)· 高 (100vw - 22px) * 9/16 + 6px —— 100vw - 22px = 360 - 16(左右 8px) - 6(左右边框 3px) = 338,即内容区宽 */ body.bili-live-room-page #fullscreen-container { position: relative !important; width: 100% !important; max-width: 100% !important; height: calc((100vw - 22px) * 9 / 16 + 6px) !important; max-height: calc((100vw - 22px) * 9 / 16 + 6px) !important; min-height: 0 !important; padding: 0 !important; margin: 0 auto !important; border: 3px solid #00aeec !important; border-radius: 12px !important; box-shadow: 0 4px 16px rgba(0, 174, 236, 0.25) !important; overflow: hidden !important; box-sizing: border-box !important; } /* ★★★ 播放器内部整条链:height:100% 跟随父级,消除次像素缝隙 ★★★ v131.93 重写:旧版用显式 calc 给内层链定高,与外层 calc 产生 两份独立取整,左/底部差出约 1px 缝隙。当时用 scale(1.012) 遮盖, 但会裁掉视频边缘 1~2px 内容。 新版改法(纯 CSS,不裁不拉伸): 内层链统一用 height:100% 跟随 #fullscreen-container 的内容盒, 只有一份取整(外层的 calc),不存在两份独立取整的差值, 视频严格铺满容器内容区,左/底部缝隙彻底消失。 站内 JS 行内 !important 仍会给 .player-section 写错误高度 (容器高 - 礼物栏 84px),CSS 压不过 → 由 JS applyLivePlayerSize 用同样的 100% 行内 !important 顶掉(后写覆盖先写)。 ⚠ video 的 transform 必须是 none(不能有 scale), 否则画面会被轻微放大、边缘被 overflow:hidden 裁掉。 ⚠ object-fit: contain 保证视频原始比例不变形。 ---- */ /* ★★★ v131.94:直播间「播放器内部下方 84px 空档 / 画面被压扁」根治 ★★★ 探针实测(bili_probe_2026-09-30_12-43-30): #fullscreen-container [344x196](3px 边框 → 内容盒 338x190) > .player-section [338x106] ← 只有 106,应为 190 106 = 190 - 84 —— 站内 JS 一直按「容器高 - 礼物栏 84px」 给 .player-section 写**行内**几何值(礼物栏早已被隐藏,这 84px 却仍被扣掉),视频被压成扁条、下方空出一条黑边。 行内 !important 样式表压不过(它连 min/max-height 一起写, 所以写 100% 的 min-height 也救不回来),因此不跟它抢属性: display:contents 让 .player-section 根本不生成盒子 —— 元素一旦不生盒,写在它身上的全部几何声明(height / min-height / max-height / top / bottom…)无论行内与否一律失效。 #live-player-ctnr 于是直接以 #fullscreen-container 为定位父级, height:100% = 内容盒 190px,画面满铺、零裁剪、零拉伸, 且只剩外层 calc 一份取整,v131.93 消灭的次像素缝隙不会回归。 (下方内层链规则里的 min-height:100% 与 JS 的 stripPlayerSectionInline 分别作为二、三道保险。) */ body.bili-live-room-page #fullscreen-container .player-section { display: contents !important; } body.bili-live-room-page #fullscreen-container .player-section, body.bili-live-room-page #live-player-ctnr, body.bili-live-room-page #live-player-ctnr > .h-100.p-relative, body.bili-live-room-page #live-player, body.bili-live-room-page #live-player video { position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; max-width: 100% !important; height: 100% !important; min-height: 100% !important; max-height: 100% !important; margin: 0 !important; padding: 0 !important; border: none !important; border-radius: 0 !important; transform: none !important; box-sizing: border-box !important; } body.bili-live-room-page #live-player video { object-fit: contain !important; background-color: #000 !important; } /* 弹幕层:保留,但压回播放器范围内(站内给它加了 15° 旋转 transform,会溢出) */ body.bili-live-room-page #live-player .web-player-danmaku, body.bili-live-room-page #live-player .danmaku-item-container { position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; overflow: hidden !important; z-index: 10 !important; } /* 业务注入层(PK / 礼物菜单 / 贴纸等都挂在它下面)已全部隐藏, 留着会是一张盖在播放器上的透明膜,拦掉播放器的点击,直接放行指针事件 */ body.bili-live-room-page #live-player .web-player-inject-wrap, body.bili-live-room-page #businessContainerElement { pointer-events: none !important; overflow: hidden !important; } /* ---- 4) 评论区:从右侧 absolute 挪到播放器正下方 ---- */ body.bili-live-room-page #aside-area-vm { position: static !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; padding: 0 8px !important; /* 与播放器左右 8px 对齐 */ margin: 8px auto 0 !important; /* 距播放器 8px */ box-sizing: border-box !important; overflow: visible !important; float: none !important; top: auto !important; left: auto !important; right: auto !important; touch-action: pan-y !important; /* v131.96:纵向手势留给页面滚动 */ } /* ★★★ v131.97:聊天区「定高 + 内部滚动」,页面不再需要滚动 ★★★ 上一版(v131.96)改成 height:auto + overflow:visible 让聊天内容参与 页面排版,结果聊天越滚越长、整页无限往屏幕外延伸,而手指落在聊天区 依然纹丝不动 —— 说明聊天区上有东西把纵向手势吃掉了(站内为聊天区 绑的手势逻辑 / touch-action),页面滚动压根没被触发。 换个思路:既然手势在聊天区里必然被聊天区接管,那就让**聊天区自己 成为一个好用的滚动容器**,同时把它限死在屏内: · .chat-history-panel 定高 calc(100dvh - 284px) —— 284px = 顶部 36 + 播放器 196 + 播放器上下 margin 36x2 实测 #aside-area-vm 从 top=268 起算,再留 16px 底边距。 这样「播放器 + 聊天区」总高恒 ≤ 一屏,页面本身不再产生滚动, 聊天内容也就永远不会溢出到屏幕外。想看历史就在聊天区里滑。 · #chat-history-list 恢复 overflow-y:auto,由它承载滚动; · overscroll-behavior:contain —— 聊天区滑到头不要把整页也拽动, 避免"滑聊天区时页面跟着抽"的连带感; · transform:none —— 站内平滑滚动会给列表加 translateY, 会把整块内容平移出可视区; · touch-action:pan-y 覆盖聊天区整棵子树 —— 站内若给其中某层 写了 touch-action:none / 绑了手势,纵向手势会被彻底掐死, 这里逐层强制把纵向手势交还给浏览器。 另配 JS bindLiveChatScroll:钉住列表高度 + 手势兜底。 */ body.bili-live-room-page .chat-history-panel { width: 100% !important; max-width: 100% !important; /* v131.99:284 → 304,多留 36px 底距(面板顶距 268 + 36 = 304) */ height: calc(100vh - 304px) !important; height: calc(100dvh - 304px) !important; min-height: 120px !important; max-height: calc(100vh - 304px) !important; max-height: calc(100dvh - 304px) !important; padding: 0 !important; margin: 0 !important; background: #f6f7f8 !important; border-radius: 12px !important; overflow: hidden !important; box-sizing: border-box !important; touch-action: pan-y !important; overscroll-behavior: contain !important; } body.bili-live-room-page #chat-history-list { width: 100% !important; max-width: 100% !important; height: 100% !important; min-height: 0 !important; max-height: 100% !important; padding: 6px 8px !important; margin: 0 !important; overflow-x: hidden !important; overflow-y: auto !important; overscroll-behavior: contain !important; -webkit-overflow-scrolling: touch !important; transform: none !important; touch-action: pan-y !important; box-sizing: border-box !important; } /* 聊天区整棵子树:纵向手势一律放行,谁也不许掐 */ body.bili-live-room-page #aside-area-vm *, body.bili-live-room-page .chat-history-panel * { touch-action: pan-y !important; } /* 盖在聊天区上方的浮层直接放行指针事件: 探针实测 .chat-overlay-layer [344x67] absolute z=10 压在聊天区顶部, #welcome-area-bottom-vm [344x83](带 canvas 欢迎动画)也悬在同一片区域。 手指一落在聊天区上半部分就打在这些浮层上,手势自然传不到列表。 */ body.bili-live-room-page .chat-overlay-layer, body.bili-live-room-page #welcome-area-bottom-vm, body.bili-live-room-page #welcome-item-video, body.bili-live-room-page .chat-overlay-layer *, body.bili-live-room-page #welcome-area-bottom-vm * { pointer-events: none !important; } body.bili-live-room-page #chat-items, body.bili-live-room-page .chat-item { width: 100% !important; max-width: 100% !important; box-sizing: border-box !important; overflow-wrap: break-word !important; word-break: break-word !important; } /* ★★★ v131.96:隐藏「发送框」与它上方那一行「XX 进入直播间」★★★ 探针实测(bili_probe_2026-09-30_13-41-13)聊天区纵向排布: #chat-history-list 103 ~ 691 #brush-prompt [344x20] 691 ~ 711 ← 「南城浅忆萧 进入直播间」 #chat-control-panel-vm [344x109] 711 ~ 820 ← 输入区(图标行 + 输入框 + 发送) 手机端只用来看直播/弹幕,这两块纯占位,整条摘掉: · #chat-control-panel-vm 整块(含 #control-panel-ctnr-box / .control-panel-icon-row / .chat-input-ctnr / textarea / 发送按钮) · #brush-prompt(进入直播间提示行,z=19 浮在聊天区与发送框之间) */ body.bili-live-room-page #chat-control-panel-vm, body.bili-live-room-page #control-panel-ctnr-box, body.bili-live-room-page .control-panel-icon-row, body.bili-live-room-page .chat-input-ctnr, body.bili-live-room-page .chat-input-area, body.bili-live-room-page #brush-prompt, body.bili-live-room-page .brush-prompt { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; } /* ★★★ v132.00:点聊天内容弹出的「按键面板」不再弹出 ★★★ 探针实测(bili_probe_2026-09-30_14-39-43,点击一条弹幕后): .danmaku-menu.p-fixed.ts-dot-4 [148x212] position:fixed z=30,挂在 .chat-history-panel 下 内含:用户名 / 去 TA 的个人空间 / @TA / 屏蔽发送者 / 举报选中弹幕 / 航海之旅 手机端这些操作一个都用不上(也点不准),面板还会盖住画面。 纯 CSS 整块隐藏即可;触发源 .open-menu 一并去掉 pointer 样式, 点击不再有"可点"的手型,也不会再有面板冒出来。 */ body.bili-live-room-page .danmaku-menu, body.bili-live-room-page .danmaku-menu.p-fixed, body.bili-live-room-page #aside-area-vm .danmaku-menu, body.bili-live-room-page .chat-history-panel .danmaku-menu { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; pointer-events: none !important; } body.bili-live-room-page .chat-history-panel .open-menu, body.bili-live-room-page .chat-history-panel .pointer { cursor: default !important; } /* ---- 5) 全屏(原生全屏 / 网页全屏):前面所有约束一律让位 ---- v131.85 引出的两个全屏问题,根因都在"把 16:9 尺寸锁死"这件事上: · 竖屏全屏「顶部没居中 + 左右有边距」 ← #player-ctnr 的 margin:36px auto 与 padding:0 8px 把播放器推离顶部、 左右各留 8px,视频贴不到屏幕边缘,也没垂直居中。 · 横屏全屏「上下被裁掉」 ← #fullscreen-container 及内层被锁成 calc((100vw - 22px) * 9 / 16), 横屏下 100vw 是**长边**,算出的高度远超屏幕高度,上下直接溢出被裁。 全屏时必须把尺寸控制权完整交回站内。 检测由 JS 完成(原生 fullscreenElement / 站内 class / 尺寸启发式), 命中即给 body 打 bili-live-fs。下面这组选择器多一个类,特异性更高, 能覆盖本页前面写下的全部尺寸约束。 播放器直接 fixed + inset:0 硬贴视口四边: 不再猜站内用的是 fixed 还是 absolute,也不依赖父级链是否 100%, 竖屏横屏都保证「贴边 + 完整」。 */ /* html 单独写一条:不给 定高,body 的 height:100% 无从参照。 刻意不与下面 body 那条合并成选择器列表 —— :has() 一旦不被支持, 整组选择器会被一起丢弃,body 那条也会跟着失效。 */ html:has(body.bili-live-room-page.bili-live-fs) { width: 100% !important; height: 100% !important; max-width: none !important; max-height: none !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; background: #000 !important; } body.bili-live-room-page.bili-live-fs { width: 100% !important; height: 100% !important; max-width: none !important; min-width: 0 !important; max-height: none !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; background: #000 !important; } body.bili-live-room-page.bili-live-fs .live-room-app, body.bili-live-room-page.bili-live-fs main.app-content, body.bili-live-room-page.bili-live-fs .app-body, body.bili-live-room-page.bili-live-fs section.player-and-aside-area, body.bili-live-room-page.bili-live-fs #player-ctnr, body.bili-live-room-page.bili-live-fs .fullscreen-container-paddingbox { width: 100% !important; height: 100% !important; max-width: none !important; min-width: 0 !important; max-height: none !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; border-radius: 0 !important; box-shadow: none !important; background: #000 !important; overflow: hidden !important; box-sizing: border-box !important; float: none !important; transform: none !important; } body.bili-live-room-page.bili-live-fs #fullscreen-container { position: fixed !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; max-width: none !important; min-width: 0 !important; max-height: none !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; border-radius: 0 !important; box-shadow: none !important; background: #000 !important; overflow: hidden !important; box-sizing: border-box !important; z-index: 2147483647 !important; } /* 内层链解除 16:9 的 calc 锁,改回跟随父级铺满 */ /* v131.94:同上,原生全屏态也摘掉 .player-section 的盒子 (站内网页全屏同样会扣 84px;这里用 100% 跟满视口) */ body.bili-live-room-page.bili-live-fs #fullscreen-container .player-section { display: contents !important; } body.bili-live-room-page.bili-live-fs #fullscreen-container .player-section, body.bili-live-room-page.bili-live-fs #live-player-ctnr, body.bili-live-room-page.bili-live-fs #live-player-ctnr > .h-100.p-relative, body.bili-live-room-page.bili-live-fs #live-player, body.bili-live-room-page.bili-live-fs #live-player video, body.bili-live-room-page.bili-live-fs #live-player .web-player-danmaku, body.bili-live-room-page.bili-live-fs #live-player .danmaku-item-container { position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; max-width: none !important; max-height: none !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; border-radius: 0 !important; transform: none !important; box-sizing: border-box !important; } /* contain 保证横屏下画面完整不裁(cover 会填满但切掉上下) */ body.bili-live-room-page.bili-live-fs #live-player video { object-fit: contain !important; background-color: #000 !important; } /* 全屏时评论区不该再占位 */ body.bili-live-room-page.bili-live-fs #aside-area-vm { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* ============================================================ ---- 6) v131.89:站内「网页全屏」body.player-full-win 一律 按**普通态**渲染(纯 CSS 兜底) ---- 退出原生全屏(双击 / 返回键 / ESC)时,站内播放器会自己补一个 player-full-win —— 也就是「网页全屏」:带浏览器地址栏、底部留 84px 礼物栏空档、播放器被写死 668px 定宽。上一版把它当全屏 处理(isLiveFullscreen 命中即铺满视口),于是双击就在 「原生全屏 ↔ 带地址栏的全屏」之间来回跳,永远退不回普通态。 新规则:这个态我们根本不要,见到就当普通态。 · JS:class 一出现立刻摘掉(bindLiveWebFullscreenGuard) · CSS:这里兜底 —— 万一它活了几帧,也强制按普通态排版, 不会出现"带地址栏的全屏" :not(.bili-live-fs) 保证真正的原生全屏不受这组规则影响 (bili-live-fs 只在原生 Fullscreen API 命中时才打)。 ---- */ html:has(body.bili-live-room-page.player-full-win:not(.bili-live-fs)) { width: 100% !important; height: auto !important; max-width: 100vw !important; max-height: none !important; padding: 0 !important; margin: 0 !important; overflow-x: hidden !important; background: #ffffff !important; } body.bili-live-room-page.player-full-win:not(.bili-live-fs) { position: static !important; width: 100% !important; height: auto !important; max-width: 100vw !important; max-height: none !important; padding-top: 0 !important; margin: 0 !important; overflow-x: hidden !important; background: #ffffff !important; } body.bili-live-room-page.player-full-win:not(.bili-live-fs) .live-room-app, body.bili-live-room-page.player-full-win:not(.bili-live-fs) main.app-content, body.bili-live-room-page.player-full-win:not(.bili-live-fs) .app-body, body.bili-live-room-page.player-full-win:not(.bili-live-fs) section.player-and-aside-area { position: relative !important; width: 100% !important; max-width: 820px !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 auto !important; padding: 0 !important; overflow: visible !important; background: #ffffff !important; box-sizing: border-box !important; float: none !important; left: auto !important; right: auto !important; top: auto !important; bottom: auto !important; } body.bili-live-room-page.player-full-win:not(.bili-live-fs) #player-ctnr { position: relative !important; width: 100% !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; max-height: none !important; padding: 0 8px !important; /* 与普通态一致:左右各 8px */ margin: 36px auto !important; /* 与普通态一致:上下各 36px */ box-sizing: border-box !important; overflow: visible !important; left: auto !important; right: auto !important; top: auto !important; bottom: auto !important; } /* 网页全屏给 #fullscreen-container 的 668px 定宽 / fixed 定位 / 满屏高 全部顶掉,回到普通态的 16:9 + 3px 边框 + 12px 圆角 */ body.bili-live-room-page.player-full-win:not(.bili-live-fs) #fullscreen-container { position: relative !important; top: auto !important; right: auto !important; bottom: auto !important; left: auto !important; width: 100% !important; max-width: 100% !important; height: calc((100vw - 22px) * 9 / 16 + 6px) !important; min-height: 0 !important; max-height: calc((100vw - 22px) * 9 / 16 + 6px) !important; padding: 0 !important; margin: 0 auto !important; border: 3px solid #00aeec !important; border-radius: 12px !important; box-shadow: 0 4px 16px rgba(0, 174, 236, 0.25) !important; z-index: auto !important; background: #000 !important; box-sizing: border-box !important; } /* v131.94:网页全屏(player-full-win)是 84px 空档的重灾区 —— 站内正是为了给礼物栏留位置才扣掉这 84px。按普通态数值顶回。 */ body.bili-live-room-page.player-full-win:not(.bili-live-fs) #fullscreen-container .player-section { display: contents !important; } body.bili-live-room-page.player-full-win:not(.bili-live-fs) #fullscreen-container .player-section, body.bili-live-room-page.player-full-win:not(.bili-live-fs) #live-player-ctnr, body.bili-live-room-page.player-full-win:not(.bili-live-fs) #live-player-ctnr > .h-100.p-relative, body.bili-live-room-page.player-full-win:not(.bili-live-fs) #live-player, body.bili-live-room-page.player-full-win:not(.bili-live-fs) #live-player video { position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; max-width: 100% !important; height: 100% !important; min-height: 100% !important; max-height: 100% !important; padding: 0 !important; margin: 0 !important; border: none !important; border-radius: 0 !important; transform: none !important; box-sizing: border-box !important; } /* 评论区:网页全屏会把播放器下的内容全吃掉,这里强制回到播放器正下方 */ body.bili-live-room-page.player-full-win:not(.bili-live-fs) #aside-area-vm { display: block !important; visibility: visible !important; position: static !important; width: 100% !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; max-height: none !important; padding: 0 8px !important; margin: 8px auto 0 !important; box-sizing: border-box !important; overflow: visible !important; float: none !important; top: auto !important; left: auto !important; right: auto !important; } /* ============================================================ ---- 7) v131.90:真·原生全屏的纯 CSS 兜底(不依赖任何 JS class)---- :fullscreen 只有原生 Fullscreen API 才会命中;站内自造的"假全屏" (position:fixed 铺满视口、仍带浏览器地址栏)命中不了。 所以这条规则同时干两件事: · 原生全屏时铺满视口(与第 5 段的 .bili-live-fs 等价,互为备份) · 不在原生全屏时绝不生效 —— 假全屏因此永远走普通态排版 刻意写成独立的一条规则,不与其它选择器并列表: :has() / :fullscreen 万一不被支持,这条被整条丢弃即可, 不会连累同规则里的其它选择器。 ---- */ html:has(body.bili-live-room-page):has(:fullscreen) { width: 100% !important; height: 100% !important; max-width: none !important; max-height: none !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; background: #000 !important; } body.bili-live-room-page:has(:fullscreen) { width: 100% !important; height: 100% !important; max-width: none !important; min-width: 0 !important; max-height: none !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; background: #000 !important; } body.bili-live-room-page:has(:fullscreen) .live-room-app, body.bili-live-room-page:has(:fullscreen) main.app-content, body.bili-live-room-page:has(:fullscreen) .app-body, body.bili-live-room-page:has(:fullscreen) section.player-and-aside-area, body.bili-live-room-page:has(:fullscreen) #player-ctnr, body.bili-live-room-page:has(:fullscreen) .fullscreen-container-paddingbox { width: 100% !important; height: 100% !important; max-width: none !important; min-width: 0 !important; max-height: none !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; border-radius: 0 !important; box-shadow: none !important; background: #000 !important; overflow: hidden !important; box-sizing: border-box !important; float: none !important; transform: none !important; } body.bili-live-room-page:has(:fullscreen) #fullscreen-container { position: fixed !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; max-width: none !important; min-width: 0 !important; max-height: none !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; border-radius: 0 !important; box-shadow: none !important; background: #000 !important; overflow: hidden !important; box-sizing: border-box !important; z-index: 2147483647 !important; } /* v131.94::fullscreen 伪类兜底态同样摘掉 .player-section 的盒子 */ body.bili-live-room-page:has(:fullscreen) #fullscreen-container .player-section { display: contents !important; } body.bili-live-room-page:has(:fullscreen) #fullscreen-container .player-section, body.bili-live-room-page:has(:fullscreen) #live-player-ctnr, body.bili-live-room-page:has(:fullscreen) #live-player-ctnr > .h-100.p-relative, body.bili-live-room-page:has(:fullscreen) #live-player, body.bili-live-room-page:has(:fullscreen) #live-player video, body.bili-live-room-page:has(:fullscreen) #live-player .web-player-danmaku, body.bili-live-room-page:has(:fullscreen) #live-player .danmaku-item-container { position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; max-width: none !important; max-height: none !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; border-radius: 0 !important; transform: none !important; box-sizing: border-box !important; object-fit: contain !important; } body.bili-live-room-page:has(:fullscreen) #aside-area-vm { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* ============================================================ ---- 8) v131.91:屏蔽播放器「长按」 ---- 长按会同时冒出两个东西: A) 浏览器自己的「选中文字 / 复制分享」浮层 —— 纯 CSS 就能掐掉:touch-callout + user-select 全关。 B) 播放器自带的调试面板(探针实测: #live-player > ul,z=99,里面是 「画面异常?切换解码策略试试 / 播放器日志信息 / 视频统计信息 / Room Player 3.11.9 / 关闭」) —— 双击退出全屏时大概率被带出来,挡住画面; 连点还会点中面板里的按键。 CSS 直接隐藏(结构选择器 #live-player > ul 稳定), JS 另给同类面板打 .bili-live-ctx-menu 兜底(按文本识别)。 pointer-events:none 再加一层保险:就算它真的被触发, 也点不到任何一项。 */ body.bili-live-room-page #fullscreen-container, body.bili-live-room-page #fullscreen-container .player-section, body.bili-live-room-page #live-player-ctnr, body.bili-live-room-page #live-player-ctnr > .h-100.p-relative, body.bili-live-room-page #live-player, body.bili-live-room-page #live-player video, body.bili-live-room-page #live-player .web-player-danmaku, body.bili-live-room-page #live-player .danmaku-item-container { -webkit-touch-callout: none !important; -webkit-user-select: none !important; -moz-user-select: none !important; -ms-user-select: none !important; user-select: none !important; } body.bili-live-room-page #live-player > ul, body.bili-live-room-page #fullscreen-container .bili-live-ctx-menu, body.bili-live-room-page .bili-live-ctx-menu { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; opacity: 0 !important; pointer-events: none !important; } /* ---- v132.57:隐藏「点击取消静音」提示 ---- 探针实测(bili_probe_2026-10-02_13-10-34): #live-player > div.ntMzdJGK5xCLVyRrSZKp [150x32] / left=41 / top=187 position:absolute / z-index:40 ├─ img.LvQXQu436nrg7xhvy0gR [18x18] 喇叭图标 └─ p.Vei429vuPGSkiUk6mvNg [95x32] 「点击取消静音」 这是浏览器自动播放策略导致的静音提示,手机端不需要 (真要出声,点播放器上的喇叭或音量键即可),留着反而挡在画面底部中央。 难点:这层的类名是 CSS Module 哈希(ntMzdJGK5xCLVyRrSZKp), 站内每次构建都会换,CSS 写不死 → 由 JS 按文本识别后打上 .bili-live-mute-toast,这里只负责隐藏。 */ body.bili-live-room-page .bili-live-mute-toast { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; opacity: 0 !important; pointer-events: none !important; } /* ============================================================ ---- 9) v131.91:控制栏只留「时间」+「清晰度」,居中 ---- 探针 11-21-56 实测(视口 800,单击播放器弹出的那条): #web-player-controller-wrap-el > div.live-web-player-controller.svelte-4rgwwa > div.control-area.svelte-4rgwwa [778x36] > div.left-area [148x36] 含「47:43」时间 > div.right-area [606x36] 含「自动」清晰度 + 一串图标 窄屏下右侧那排图标挤成一团、显示不全也点不准,全部隐藏; 时间按键与清晰度按键由 JS 按文本识别后打 data-bili-live-keep="1", 这里只做「非标记即隐藏」+ flex 居中 —— 隐藏和排版全在 CSS。 ---- */ body.bili-live-room-page .control-area { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: center !important; justify-content: center !important; width: 100% !important; max-width: 100% !important; padding: 0 !important; } body.bili-live-room-page .control-area > .left-area, body.bili-live-room-page .control-area > .right-area, body.bili-live-room-page .live-web-player-controller > .left-area, body.bili-live-room-page .live-web-player-controller > .right-area { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: center !important; justify-content: center !important; flex: 0 0 auto !important; width: auto !important; min-width: 0 !important; max-width: none !important; height: auto !important; margin: 0 !important; padding: 0 !important; float: none !important; position: static !important; } /* 两个区之间留 8px;空的那个区直接不占位(:has 支持时) */ body.bili-live-room-page .control-area > .left-area { margin-right: 8px !important; } body.bili-live-room-page .control-area > .right-area { margin-left: 0 !important; } body.bili-live-room-page .control-area > .left-area:not(:has([data-bili-live-keep])), body.bili-live-room-page .control-area > .right-area:not(:has([data-bili-live-keep])) { display: none !important; width: 0 !important; margin: 0 !important; overflow: hidden !important; } body.bili-live-room-page .control-area .left-area > *:not([data-bili-live-keep]), body.bili-live-room-page .control-area .right-area > *:not([data-bili-live-keep]) { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; height: 0 !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; pointer-events: none !important; } /* 保留的两个键:居中、字号统一、不再被压缩 */ body.bili-live-room-page .control-area [data-bili-live-keep] { display: flex !important; visibility: visible !important; flex: 0 0 auto !important; align-items: center !important; justify-content: center !important; width: auto !important; min-width: 0 !important; max-width: none !important; height: auto !important; margin: 0 !important; padding: 0 4px !important; opacity: 1 !important; overflow: visible !important; } /* ============================================================ ---- 10) v131.92:三处细节 ---- ① 播放器右上角 36x36 的「反馈」图标 探针实测:它是 #live-player 的**直接子级** svg#图层_1, [36x36] top=39 left=301,常年盖在画面右上角。窄屏下用不上, 直接隐藏(控制栏内的图标都在更深的层级里,不受影响)。 ② 画面与边框之间约 1px 的露边(v131.93 根治) 根因是「两份取整」: #fullscreen-container 高 calc((100vw - 22px) * 9 / 16 + 6px) = 196.125px → 内容盒 = 196.125 - 6(上下边框) = 190.125px v131.92 时内层链又各自写了一条 calc((100vw - 22px) * 9 / 16) = 190.125px 两条算式分别取整时,左侧 / 底部会差出不到 1px, 露出容器底色 —— 就是看到的那条细缝。 视频页用的是 height:100%(跟随父级内容盒,只有一份取整),所以没事。 v131.92 用 scale(1.012) 放大视频遮盖缝隙, 但会裁掉视频边缘 1~2px 内容(不符合"不裁剪"要求)。 v131.93 根治:内层链全部改用 height:100% 跟随父级内容盒, 只有一份取整,缝隙彻底消失。站内 JS 行内 !important 写死的错误高度由 applyLivePlayerSize 用 100% 行内 !important 顶掉。 容器底色补 #000 兜底保留。 全屏态由 .bili-live-fs / :has(:fullscreen) 那两组 特异性更高的规则接管,不受本条影响。 ③ 画质面板(.quality-wrap .panel) 探针「点击自动按键后」实测: .panel [160x236],transform: matrix(1,0,0,1,-80,-238) → 相对按钮向上弹 238px,最终落在 top=-148 ~ bottom=88, 比按钮(top=190)高出一百多像素。 普通态 / 横屏下它整个跑到可视区外面被裁,只有竖屏全屏 (屏高够)才显示得全。改成视口居中 + 限高可滚动, 任何尺寸、任何朝向都完整可见。 ---- */ /* ① 隐藏「反馈」图标 */ body.bili-live-room-page #live-player > svg, body.bili-live-room-page #live-player > svg[id], body.bili-live-room-page #live-player > [class*="feedback"], body.bili-live-room-page #live-player [class*="feedback"], body.bili-live-room-page #live-player [class*="FeedBack"] { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; pointer-events: none !important; } /* ② v131.93:次像素缝隙已由内层链 height:100% 根治(见第 3 段), 不再需要 scale 放大。video 的 transform 保持 none(上面已写), 画面不被裁剪也不被拉伸。容器底色补黑兜底仍保留。 */ body.bili-live-room-page #fullscreen-container { background: #000 !important; } /* ③ 画质面板:视口居中 + 限高可滚动 */ body.bili-live-room-page .quality-wrap .panel, body.bili-live-room-page .control-area .panel { position: fixed !important; top: 50% !important; right: auto !important; bottom: auto !important; left: 50% !important; transform: translate(-50%, -50%) !important; width: 160px !important; max-width: 200px !important; min-width: 0 !important; height: auto !important; max-height: calc(100vh - 32px) !important; margin: 0 !important; border-radius: 10px !important; overflow-x: hidden !important; overflow-y: auto !important; -webkit-overflow-scrolling: touch !important; z-index: 2147483000 !important; } /* ============================================================ ---- 11) v132.05:弃用站内遮罩层,自建渐变遮罩 ---- 【目标】按键贴播放器底、遮罩从底部连续铺上来, 同时**绝不破坏站内自己的"点击收起 / 延时自动隐藏"**。 【v132.01 / v132.02 为什么把自动隐藏改坏了 —— 复盘】 站内控制栏的唤出/收起,动画走的是控制栏自身的 transform(translateY 滑入滑出),配合高度/透明度。 而那两版给控制栏整条链写了: · height:100% / inset:0 ← 顶掉站内"收起"时的高度 · transform: none !important ← 顶掉站内的滑出动画 结果:站内以为自己收起了,节点却仍铺在画面上 —— 表现出来就是"弹出来就不隐藏,点屏幕也没反应"。 这两类属性(height / transform / inset)是站内的 **状态通道**,外部一律不许碰。 【v132.04 的遮罩为什么会在 84px 处闪一下】 站内遮罩层 .web-player-controller-bg 是 height:56px + bottom:84px(礼物栏让位的残留) 唤出瞬间它先在 84px 处渲染出来,我们的 JS 要等到 下一拍(MutationObserver 30ms / 轮询 250ms)才把 margin 补偿写上去 —— 这一拍的错位就是肉眼看到的"闪一下"。 补偿永远比站内渲染慢一拍,这个闪没法靠加快轮询根治。 【本版做法:干脆不用站内那层,自己画】 ① 站内遮罩层直接 display:none —— 它只是纯视觉层 (本来就已被设成 pointer-events:none), 隐藏它不影响任何交互,也就不存在"闪"了; ② 遮罩改用 #live-player 的 ::after 伪元素: · 常驻、opacity:0,靠 .bili-live-ctrl-on 切到 1, 带 180ms 过渡,跟控制栏滑入同步淡入,不突兀; · 铺满整个播放器(不使用百分比之外的任何几何值), 底部最浓、向上渐隐,本身就不存在"断在 84px"的问题; · pointer-events:none + 纯视觉,点击照样穿透回画面; · z=11 —— 低于控制栏包层 z=13,不挡按键; ③ 按键行仍用 margin 位移补偿(v132.03 起的成熟做法), JS 实测偏移量,不动任何状态通道。 ---- */ /* ① 弃用站内遮罩层(纯视觉层,隐藏不影响交互) */ body.bili-live-room-page .web-player-controller-bg, body.bili-live-room-page #live-player > [class*="controller-bg"] { display: none !important; visibility: hidden !important; pointer-events: none !important; } /* ② 自建遮罩:常驻 + opacity 0/1 过渡,彻底没有"闪一下" */ body.bili-live-room-page #live-player::after { content: '' !important; position: absolute !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; background: linear-gradient( to top, rgba(0, 0, 0, 0.62) 0%, rgba(0, 0, 0, 0.44) 9%, rgba(0, 0, 0, 0.22) 20%, rgba(0, 0, 0, 0.08) 32%, rgba(0, 0, 0, 0) 45% ) !important; opacity: 0 !important; transition: opacity 180ms ease !important; pointer-events: none !important; z-index: 11 !important; } body.bili-live-room-page #live-player.bili-live-ctrl-on::after { opacity: 1 !important; } /* ③ 按键行:下移补偿(只动 margin,不动 transform / height / inset) 探针实证(bili_probe_2026-09-30_17-16,全屏 800x360): .control-area [800x36] top=324 bottom=360(补偿后贴底 ✓) 84 只是首帧兜底(限全屏态),真实偏移由 JS 实测 (播放器底 − 按键底)后写精确值;普通态实测为 0 就写 0。 */ body.bili-live-room-page.bili-live-fs .control-area, body.bili-live-room-page:has(:fullscreen) .control-area { margin-bottom: -84px !important; } /* ============================================================ ---- 12) v132.06:首屏零闪 —— 顶部栏等"一出生就该藏" ---- 现象:刚打开直播间,官方 PC 顶部导航会先显示一下(还带着 被窄屏挤压后的变形),过一瞬才消失。 根因:本页所有隐藏规则都挂在 body.bili-live-room-page ××× 这个前缀上,而 bili-live-room-page 是 JS 给 打的。 脚本虽然是 document-start(样式表在 HTML 解析前就已注入), 但 body 要等 HTML 解析到才存在、class 要等 syncPageClass 跑过才生效 —— 中间这段时间导航栏是"无人管"的状态, 浏览器于是老老实实把它画了出来。 修法:这些元素是直播间**独有**的(#room-ssr-vm 是直播间 SSR 根节点 id,.link-navbar / footer.link-footer / #sidebar-vm 都只在 live.bilibili.com 出现,主站用的是 .bili-header), 所以可以直接用**不带任何 class 前缀**的选择器隐藏 —— 不依赖 body、不依赖 JS 打标,样式表一注入就成立, 元素刚被解析出来就是 display:none,一帧都不会画出来。 ⚠ 刻意放在样式表**最后**:同为 !important 且特异性相同时 后写覆盖先写,放在末尾保证它压过前面任何一条同分规则。 ⚠ 用 html:root 前缀抬特异性到 (1,1,1),与 body.bili-live-room-page #room-ssr-vm 同分,靠位置取胜。 ⚠ main.app-content 的 padding-top:74px 是顶部栏留的位置, 顶部栏一藏它就是一块无来由的顶部空白,一并归零。 ---- */ html:root #room-ssr-vm, html:root #room-ssr-vm *, html:root .link-navbar-ctnr, html:root .link-navbar-wrap, html:root nav.link-navbar, html:root .link-navbar, html:root #head-info-vm, html:root #gift-control-vm, html:root #rank-list-vm, html:root #rank-list-ctnr-box, html:root #sections-vm, html:root #sidebar-vm, html:root #room-background-vm, html:root #gift-screen-animation-vm, html:root #guard-popup, html:root footer.link-footer { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; max-height: 0 !important; padding: 0 !important; margin: 0 !important; border: none !important; overflow: hidden !important; pointer-events: none !important; } /* 顶部栏留的那 74px,一并抹掉,避免首屏内容先被推下去再弹回来 */ html:root main.app-content, html:root main.app-content.p-relative { padding-top: 0 !important; } /* ============================================================ ---- 13) v132.20:他人「关注 / 粉丝」页适配 ---- 参照探针(他人关注页面0952.txt / 用户列表参考2.txt)。 【改前 · PC 左右两栏,窄屏被挤成 195 + 267】 .nav-bar.space-navbar [360x52] top=52 ← 主页 动态 投稿 合集和系列 main.space-main.route_relationFollow [360x1690] top=104 └─ .space-follow [360x1690] ├─ .relation-aside [195x716] left=0 ← 左侧竖排 │ └─ .vui_collapse │ ├─ .vui_collapse_item │ │ ├─ .vui_collapse_item_header [190x62] "Ta的关注" │ │ └─ .vui_collapse_item_content │ │ └─ .vui_sidebar → "全部关注 56" │ └─ .vui_collapse_item │ ├─ .vui_collapse_item_header [190x62] "Ta的粉丝" │ └─ .vui_collapse_item_content │ └─ .vui_sidebar → "Ta的粉丝 131" └─ .follow-main [267x1660] left=211 mt=30 pb=40 ├─ .follow-main-title [249x36] mb=30 └─ section.relation-content [249x1504] └─ .items → .item [121x118] ← 两列小卡 【改后】 ① 左栏那两个按键(全部关注 56 / Ta的粉丝 131) 移到导航栏下方,整行**居中**横排; ② 分组标题「Ta的关注」「Ta的粉丝」隐藏 —— 它们只是 PC 的分组标签,手机上与按键文字重复; ③ 列表改成单列,卡片尺寸/间距照抄搜索页→用户页: .media-list.row pt=8 .col_6…mb_x60 [344x86] left=8 mb=16 头像 a.mr_md [86x86] left=8 .user-content [243x84] left=109 ← 与头像间距 15 → 卡片 344 = 头像 86 + 间距 15 + 内容 243,正好吻合。 ---- */ /* ① 容器:PC 的「左 195 + 右 267」改成上下堆叠(aside 本就在前) */ body.bili-space-page .space-follow, /* v132.22:粉丝页用的是 .space-fans / .fans-main / .fans-main-head, 与关注页的 .space-follow / .follow-main / .follow-main-title 是两套类名(探针 Ta的粉丝6000多的页面没能修复.txt 实测), 所以下面每条规则都要把两套名字一起写上,否则粉丝页完全不生效。 */ body.bili-space-page .space-fans { /* v132.21:不再用 flex 纵向堆叠 —— .relation-aside 本就是 nth-of-type(1)、.follow-main 是 nth-of-type(2),普通块流天然就是上下排列,不需要 flex。 实测粉丝数很大(6556)时整块列表会空白,flex 容器会 参与高度计算,是最可疑的一环,这里退回最稳的块流。 */ display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* ② 左侧按键组:铺满整行 + 横向居中 */ body.bili-space-page .relation-aside { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 8px !important; overflow: visible !important; box-sizing: border-box !important; } body.bili-space-page .relation-aside .vui_collapse { display: flex !important; flex-direction: row !important; justify-content: center !important; align-items: center !important; gap: 8px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } body.bili-space-page .relation-aside .vui_collapse_item, body.bili-space-page .relation-aside .vui_collapse_item_content, body.bili-space-page .relation-aside .vui_sidebar { display: flex !important; flex-direction: row !important; justify-content: center !important; width: auto !important; max-width: none !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; overflow: visible !important; box-sizing: border-box !important; } /* ②-b 隐藏分组标题「Ta的关注」「Ta的粉丝」 */ body.bili-space-page .relation-aside .vui_collapse_item_header { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* ②-b2 按键外面还套了一两层无类名容器 (探针:.vui_sidebar > .follow-sidebar-item > a.router-link-active > .vui_sidebar-item;粉丝组则是 .vui_sidebar > div > .vui_sidebar-item,中间那层没有类名) 这些层在 PC 下都是 190 定宽,不一起放开的话 胶囊会被撑成一整行宽,看着不像按键。 */ body.bili-space-page .relation-aside .vui_sidebar > *, body.bili-space-page .relation-aside .vui_sidebar > * > * { display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: center !important; width: auto !important; max-width: none !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; overflow: visible !important; box-sizing: border-box !important; } /* ②-c 按键做成小胶囊(与站内白卡同一套观感) */ body.bili-space-page .relation-aside .vui_sidebar-item, body.bili-space-page .relation-aside .follow-sidebar-item > a { display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: center !important; gap: 6px !important; width: auto !important; max-width: none !important; min-width: 0 !important; height: 32px !important; min-height: 32px !important; max-height: 32px !important; margin: 0 !important; padding: 0 12px !important; background: #ffffff !important; border: none !important; border-radius: 16px !important; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08) !important; overflow: hidden !important; box-sizing: border-box !important; } body.bili-space-page .relation-aside .vui_sidebar-item-title { display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: center !important; gap: 4px !important; width: auto !important; max-width: none !important; min-width: 0 !important; height: auto !important; margin: 0 !important; padding: 0 !important; } body.bili-space-page .relation-aside .vui_sidebar-item-title svg, body.bili-space-page .relation-aside .relation-aside__icon, body.bili-space-page .relation-aside .follow-sidebar-item__icon { width: 16px !important; height: 16px !important; min-width: 0 !important; flex-shrink: 0 !important; } body.bili-space-page .relation-aside .vui_sidebar-item-right { width: auto !important; max-width: none !important; height: auto !important; margin: 0 !important; padding: 0 !important; font-size: 12px !important; } /* ③ 右侧列表容器:铺满 + 左右 8px(与搜索页→用户页一致) */ body.bili-space-page .follow-main, body.bili-space-page .fans-main { width: 100% !important; max-width: 100% !important; min-width: 0 !important; /* v132.22:粉丝页 .fans-main 带着站内原生的 mt=30 + pb=40, 且宽度是 PC 的 487、left=360 —— 整块被推到屏幕右侧看不见, 这就是"6000 多粉丝页面内容不显示"的真因(不是渲染问题)。 这里宽度压到 100% + 外边距归零即可。 */ margin: 0 !important; padding: 0 8px 8px !important; box-sizing: border-box !important; } body.bili-space-page .follow-main-title, body.bili-space-page .fans-main-head, body.bili-space-page .fans-main-title { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; margin: 0 0 8px 0 !important; padding: 0 !important; box-sizing: border-box !important; } body.bili-space-page section.relation-content, body.bili-space-page .relation-content > .items { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* 单列:原来是两列 121 宽的小卡,这里放开成一整行。 v132.21:不再写 display:block(会顶掉站内原有的 grid/flex, 长列表下可能整块不渲染),改成 ① grid 就强制单列(不是 grid 则这条被忽略,无害) ② 子项 width:100%,flex-wrap 布局下同样每行一个 两条都只"放宽",不推翻站内布局方式。 */ body.bili-space-page .relation-content > .items { grid-template-columns: minmax(0, 1fr) !important; /* v132.24:实测间距是 16 不是 8 —— .items 是 grid,站内带 row-gap:8px, 而 .item 自己又写了 margin-bottom:8px,两者叠加成 16。 探针反推:.items 高 2440,24 张卡 ×86 = 2064, 376 / 23 个间隔 ≈ 16.35 → 确为 16。 这里把 grid 的 gap 归零,间距全交给 .item 的 mb:8px。 */ row-gap: 0 !important; gap: 0 !important; column-gap: 0 !important; grid-row-gap: 0 !important; } /* v132.21:内容量很大时列表整块空白的兜底 —— 若站内给条目加了 content-visibility:auto(视口外跳过渲染), 配合我们改过的高度就可能算出 0 高度、整页空白。 这里显式关掉跳过渲染,保证所有条目都参与布局与绘制。 */ body.bili-space-page .relation-content, body.bili-space-page .relation-content > .items, body.bili-space-page .items > .item { content-visibility: visible !important; contain: none !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; } /* ⚠ .relation-card 的 overflow 保持上面定的 hidden —— 圆角要靠它裁掉头像/封面的直角,这里只关"跳过渲染"。 */ body.bili-space-page .relation-card { content-visibility: visible !important; contain: none !important; } body.bili-space-page .items > .item { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; /* v132.23:卡片间距 16 → 8,与"底部分页条距屏幕底部"一致 */ margin: 0 0 8px 0 !important; padding: 0 !important; float: none !important; box-sizing: border-box !important; } /* ④ 卡片:改成「头像 86 + 间距 15 + 内容 243」横排, 与搜索页→用户页的 344x86 完全一致 */ body.bili-space-page .relation-card { display: flex !important; flex-direction: row !important; align-items: center !important; gap: 15px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 86px !important; max-height: none !important; margin: 0 !important; padding: 0 !important; background: #ffffff !important; border: none !important; border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08) !important; overflow: hidden !important; box-sizing: border-box !important; } /* 头像 86x86(站内原生 80,且内层 canvas 是 120,一并钉住) */ body.bili-space-page .relation-card-avatar, body.bili-space-page .relation-card-avatar .b-avatar, body.bili-space-page .relation-card-avatar .b-avatar__canvas, body.bili-space-page .relation-card-avatar .b-avatar__layers, body.bili-space-page .relation-card-avatar .b-avatar__layer, body.bili-space-page .relation-card-avatar picture, body.bili-space-page .relation-card-avatar img { flex: 0 0 86px !important; width: 86px !important; min-width: 86px !important; max-width: 86px !important; height: 86px !important; min-height: 0 !important; max-height: 86px !important; margin: 0 !important; padding: 0 !important; object-fit: cover !important; box-sizing: border-box !important; } /* 内容区 243 宽,三行竖排(名字 / 签名 / 关注按钮) */ body.bili-space-page .relation-card-info { display: flex !important; flex-direction: column !important; justify-content: center !important; align-items: stretch !important; gap: 4px !important; flex: 1 1 auto !important; width: auto !important; max-width: none !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 8px 0 0 !important; overflow: hidden !important; box-sizing: border-box !important; } body.bili-space-page .relation-card-info__uname, body.bili-space-page .relation-card-info__sign { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; white-space: nowrap !important; overflow: hidden !important; text-overflow: ellipsis !important; box-sizing: border-box !important; } body.bili-space-page .relation-card-info-option, body.bili-space-page .relation-card-info-option .follow-btn, body.bili-space-page .relation-card-info-option .follow-btn-wrapper { width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; overflow: visible !important; box-sizing: border-box !important; } /* ============================================================ ---- 13b) v132.21:头像装饰层错位 / 隐藏大标题与导航 / 去分割线 ---- 【① 头像右下角的装饰层被裁】 探针 bili_probe_2026-10-01_10-36-21: .relation-card [344x86] top=197 bottom=283 ← overflow:hidden a.relation-card-avatar [86x86] top=197 left=8 .b-avatar__layer.center [86x86] left=8 ← 头像照,正常 .b-avatar__layer:nth-of-type(2) [86x86] top=279 left=90 └─ .b-avatar__layer__res.local-4 [82x82] 第二层是头像框/挂件类的装饰层,落在 (90,279) —— 相对头像左上角偏移了 (82,82),几乎整个跑到头像外面, 卡片 283 就裁掉了,于是只剩顶部 4px 露在卡片里, 正是用户看到的"只显示顶上一小部分"。 修法:把它钉回头像框内(top/left:0 + 尺寸同头像), 装饰层就正好套在头像上,不再溢出、不再被裁。 ---- */ /* v132.22 修正:v132.21 把这一层钉成了 86x86 铺满头像, 结果"闪电图标把头像整个挡住了"。 正确布局(用户确认): · 位置:头像**右下角** · 尺寸:面积约为头像的 1/16 → 边长 = 86 / 4 ≈ 21.5 → 取 22px · 效果:**压住头像右下角一小块**,而不是盖住整张头像 定位基准:.b-avatar 是 position:relative 且尺寸就是头像 86x86, 所以 right:0 / bottom:0 正好落在头像右下角,压住一部分。 z-index 抬到 3,保证画在头像照片(.center 层)之上。 ---- */ body.bili-space-page .relation-card-avatar .b-avatar__layer:not(.center), body.bili-space-page .relation-card-avatar .b-avatar__layer:not(.center) > * { position: absolute !important; top: auto !important; left: auto !important; right: 0 !important; bottom: 0 !important; width: 22px !important; min-width: 0 !important; max-width: 22px !important; height: 22px !important; min-height: 0 !important; max-height: 22px !important; margin: 0 !important; padding: 0 !important; transform: none !important; /* ⚠ 上面第 ④ 段把 .b-avatar__layer 整条链写成了 flex: 0 0 86px(那是给头像本体用的)。 flex-basis 会盖过 width,所以这里必须显式 flex:none, 否则徽标会被 flex-basis 撑回 86px。 */ flex: none !important; z-index: 3 !important; box-sizing: border-box !important; } /* 【② 隐藏大标题「全部关注 / Ta的粉丝」】 探针 [47] .follow-main-title [344x36] —— 它和上面那两个 按键的文字完全重复,手机上纯占地方。 ---- */ body.bili-space-page .follow-main-title, body.bili-space-page .fans-main-head, body.bili-space-page .fans-main-title { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* 【③ 隐藏「主页 / 动态 / 投稿 / 合集和系列」那一行】 探针 [3] .nav-bar.space-navbar [360x52] top=52。 隐藏后按键组直接顶到搜索栏下方,更紧凑。 ⚠ 作用域必须限定在**关注/粉丝页**:这一行是空间页切换 「主页/动态/投稿」的入口,在主页等页面隐藏了就没法切 tab。 所以用 :has(main.space-main.route_relationFollow/Fans) 精确命中,别的空间页不受影响。 ⚠ 只隐藏站内这一行,不碰自建搜索栏 #my-bili-searchbar。 ---- */ body.bili-space-page:has(main.space-main.route_relationFollow) .nav-bar.space-navbar, body.bili-space-page:has(main.space-main.route_relationFans) .nav-bar.space-navbar, body.bili-space-page main.space-main.route_relationFollow ~ .nav-bar.space-navbar, body.bili-space-page main.space-main.route_relationFans ~ .nav-bar.space-navbar, body.bili-space-page #app:has(> main.space-main.route_relationFollow) > .nav-bar.space-navbar, body.bili-space-page #app:has(> main.space-main.route_relationFans) > .nav-bar.space-navbar { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* 站内空间页自带的搜索框一并隐藏(不含自建搜索栏) */ body.bili-space-page:has(main.space-main.route_relationFollow) .header.space-header, body.bili-space-page:has(main.space-main.route_relationFans) .header.space-header, body.bili-space-page:has(main.space-main.route_relationFollow) .header.space-header .search, body.bili-space-page:has(main.space-main.route_relationFans) .header.space-header .search-input, body.bili-space-page:has(main.space-main.route_relationFollow) .nav-bar__main-right, body.bili-space-page:has(main.space-main.route_relationFans) .nav-bar__main-right, body.bili-space-page:has(main.space-main.route_relationFollow) .space-search, body.bili-space-page:has(main.space-main.route_relationFans) .space-search { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* ===== v132.80:关注页 —— 隐藏「新建分组」与站内搜索框, 第一个卡片距顶改为 36px ===== 探针实测(bili_probe_2026-10-03_11-42-51,视口 360x1246,自己的关注页): .relation-aside [360x76] top=0 pt=8 pb=8 └─ .vui_collapse [344x60] top=8 └─ .vui_collapse_item [290x60] └─ .vui_collapse_item_content [290x60] ├─ .relation-collapse-create [112x52] mb=8 ← 「新建分组」(要隐藏) └─ .vui_sidebar [178x60] ← 分组导航(保留) .relation-content-header [344x34] top=76 ├─ __left [0x34] (空) └─ __right [239x34] └─ .vui_input.fav-list-header-filter__search [239x34] ← 搜索框(要隐藏) .items > 第一个 .item [344x86] top=110 算一下现状:卡片 top = aside 76 + header 34 = 110,目标 36。 关键数字:aside 那 76 里,60 来自 .relation-collapse-create (52 + mb 8 = 60)—— 它是 aside 里最高的元素,把整条撑到 8 + 60 + 8 = 76。所以隐藏它之后 aside 会自己缩到 8 + 32(胶囊高)+ 8 = 48,header 归零后卡片 top = 48, 离 36 还差 12。 与其去凑 12 这个数(会随胶囊高度变化而失效),不如直接给 aside 一个确定高度 36px + 内容垂直居中 —— 胶囊 32 高,上下各留 2px, 卡片 top 就稳定是 36,不依赖任何内部高度计算。 ⚠ .relation-content-header 整行隐藏而非只藏搜索框: 探针实测 __left 是 0 宽、整行只装了搜索框一个东西, 留着就是一条 34px 的空条。 ⚠ 这里的「搜索框」是站内 .fav-list-header-filter__search, 与自建的 #my-bili-searchbar 无关(v132.21/23 已单独处理它)。 */ /* ① 隐藏「新建分组」按键 */ body.bili-space-page .relation-aside .relation-collapse-create, body.bili-space-page .relation-collapse-create { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; border: none !important; overflow: hidden !important; pointer-events: none !important; /* 防隐藏后仍占点击热区 */ } /* ② 隐藏站内搜索框整行 */ body.bili-space-page .relation-content-header, body.bili-space-page .relation-content-header__left, body.bili-space-page .relation-content-header__right, body.bili-space-page .fav-list-header-filter__search, body.bili-space-page .relation-content-header .vui_input { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; border: none !important; overflow: hidden !important; pointer-events: none !important; } /* ③ aside 钉为 36px 高、内容垂直居中 —— 卡片 top 就稳定是 36 */ body.bili-space-page .relation-aside { display: flex !important; flex-direction: row !important; justify-content: center !important; align-items: center !important; width: 100% !important; max-width: 100% !important; height: 36px !important; /* ← 目标间距就是这一个数 */ min-height: 36px !important; max-height: 36px !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important; /* 内部 32 高胶囊居中,上下各 2 */ box-sizing: border-box !important; } /* ===== v132.81:关注页 —— 隐藏 5 个分组胶囊 + 隐藏 UP 主名片浮层 ===== 探针实测(bili_probe_2026-10-03_11-53-04,v132.80 已生效): .relation-aside [360x**36**] top=0 ← v132.80 的 36px 已生效 └─ .vui_collapse [355x32] top=2 ├─ .vui_collapse_item(nth 1) [291x32] │ └─ .vui_sidebar → 4 个胶囊:全部关注12 / 特别关注0 / │ 悄悄关注0 / 默认分组12 └─ .vui_collapse_item(nth 2) └─ .vui_sidebar → 第 5 个胶囊:我的粉丝 1 .follow-main [360x1136] top=**36** ← 第一个卡片距顶 36 ✓ bili-user-profile [360x224] **left=-315** pos=fixed z=1004 【① 隐藏 5 个胶囊】 5 个胶囊全部在 .relation-aside > .vui_collapse 内(4 + 1 两组), 所以直接把 .vui_collapse 整块隐藏即可,不用一个个点名。 ⚠ 关键:aside 本身**必须保留** —— v132.80 把它钉成了 36px 高, 这正是"第一个卡片距顶 36px"的实现方式。胶囊没了、aside 还在, 那 36px 就变成一段纯留白,间距自动保持 36; 若顺手把 aside 也隐藏,卡片会直接顶到屏幕边上(top=0)。 所以这里只藏内容、不动容器。 【② 隐藏 UP 主名片浮层】 探针实测 bili-user-profile [360x224] / rect left=**-315** / pos=fixed / z=1004 —— 它是站内"悬停头像弹出的 UP 主信息卡" (PC 的 hover 名片),手机上没有 hover,却会被触屏点击触发; 弹出后位置由站内按 PC 坐标算,跑到 left=-315, 只在屏幕左缘露出 0~45px 的一条,看着就是"莫名弹出的框"。 注意 z 实测是 1004 而不是 v131.28 那条规则写的 2147483647, 说明那条"钉在视口内 top:36px"的既有规则没能压过站内的定位 (站内很可能写在行内且带 !important,作者样式压不过)。 既然定位压不住、而关注页点头像本来就是进主页(名片在此页无用), 直接整块隐藏最干净。 ⚠ 作用域只限关注页(用 :has 精确命中)—— 空间动态页等其它页面 的名片是 v131.28 特意适配过的有用功能,不能一起干掉。 */ body.bili-space-page .relation-aside > .vui_collapse, body.bili-space-page .relation-aside .vui_collapse { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; border: none !important; overflow: hidden !important; pointer-events: none !important; } /* UP 主名片浮层:只在关注页隐藏 */ body.bili-space-page:has(main.space-main.route_relationFollow) bili-user-profile, body.bili-space-page:has(main.space-main.route_relationFans) bili-user-profile, body.bili-space-page main.space-main.route_relationFollow ~ bili-user-profile { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; border: none !important; overflow: hidden !important; pointer-events: none !important; } /* ===== v132.82:关注 / 粉丝页 —— 隐藏卡片右侧「⋮ 竖三点」按键 ===== 探针实测(bili_probe_2026-10-03_12-00-24 与 11-53-04,两张卡一致): .relation-card-info-option [235x35] ← 卡片右下那一排 ├─ div.follow-btn(nth 1) │ └─ div.menu-popover [235x35] ← 关注 / 已关注按钮(保留) └─ div.menu-popover:nth-of-type(2) [36x25] left=308 └─ i.vui_icon.sic-BDC-more_vertical_fill.icon [24x25] left=320 这个 36x25 的小块就是竖排三个点(⋮),点开是「发私信」等菜单, 手机上用不到,且它压在卡片最右侧(left=308,右缘 344 正好贴边), 容易误触。 ⚠ 关键约束:同一层有两个 div.menu-popover,**第一个是关注按钮**, 只能藏第二个,写错就会把「已关注」按钮一起干掉。 所以用三层保险,任一命中即可: ① div.menu-popover:nth-of-type(2) —— 直接按位置选中; ② :has(> i[class*="more_vertical"]) —— 按内部图标特征选中 (第 1 个里面装的是 .follow-btn__trigger,不含这个类名); ③ 图标本身 i[class*="more_vertical"] 也隐藏 —— 防父容器被站内改结构后留下一块 36x25 的空白。 ⚠ 不隐藏 .relation-card-info-option 这个父容器 —— 里面还有 关注按钮,整块隐藏会连按钮一起没了。 作用域按 .relation-card-info-option 限定:这个类名只在关注 / 粉丝列表卡里出现,不会误伤其它页面。 */ body.bili-space-page .relation-card-info-option > div.menu-popover:nth-of-type(2), body.bili-space-page .relation-card-info-option > .menu-popover:has(> i[class*="more_vertical"]), body.bili-space-page .relation-card-info-option .menu-popover:has(i[class*="more_vertical"]) { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; max-width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; border: none !important; overflow: hidden !important; pointer-events: none !important; } /* 图标本身:防父容器改结构后留下空白 */ body.bili-space-page .relation-card-info-option i[class*="more_vertical"], body.bili-space-page .relation-card-info-option .icon[class*="more_vertical"] { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; height: 0 !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; pointer-events: none !important; } /* 【④ 去掉按键下方的分割线】 探针:.vui_collapse_item [159x33] vs .vui_collapse_item_content [159x32] —— 多出来的 1px 就是组内那条横线(border-bottom)。 边框和伪元素画的两个来源一起清掉。 ⚠ 不清 box-shadow:按键胶囊的立体感靠它。 ---- */ body.bili-space-page .relation-aside .vui_collapse_item, body.bili-space-page .relation-aside .vui_collapse_item_content, body.bili-space-page .relation-aside .vui_sidebar, body.bili-space-page .relation-aside .vui_sidebar-item, body.bili-space-page .relation-aside .follow-sidebar-item, body.bili-space-page .relation-aside, body.bili-space-page .space-follow { border: none !important; border-top: none !important; border-bottom: none !important; border-left: none !important; border-right: none !important; background-image: none !important; } body.bili-space-page .relation-aside .vui_collapse_item::before, body.bili-space-page .relation-aside .vui_collapse_item::after, body.bili-space-page .relation-aside .vui_sidebar-item::before, body.bili-space-page .relation-aside .vui_sidebar-item::after, body.bili-space-page .relation-aside .follow-sidebar-item::before, body.bili-space-page .relation-aside .follow-sidebar-item::after { display: none !important; content: none !important; height: 0 !important; border: none !important; } /* ============================================================ ---- 13c) v132.23:徽标对位 / 按键图标统一 / 隐藏搜索栏 / 间距 ---- 探针 bili_probe_2026-10-01_11-13-50(关注页,223 关注 / 6556 粉丝) 【① 闪电徽标在空心圆内往左上偏】 .relation-card-avatar [86x86] top=100 bottom=186 left=8 └─ .b-avatar__layer:nth-of-type(2) [22x22] top=164 left=72 └─ .b-avatar__layer__res.local-4 [22x22] top=162 left=70 外层已经正确落在头像右下角(72..94 / 164..186 = 头像右下角), 但**内层**相对外层偏了 (-2,-2),于是圆内的闪电整体往左上挪, 外层右下还空出 2px。 → 内层三种来源(top/left、margin、transform)全部钉零, 让它与外层严格重合。 ---- */ body.bili-space-page .relation-card-avatar .b-avatar__layer:not(.center) > * { position: absolute !important; top: 0 !important; left: 0 !important; right: auto !important; bottom: auto !important; width: 100% !important; height: 100% !important; min-width: 0 !important; max-width: none !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; transform: none !important; translate: none !important; /* ⚠ 不写 inset 简写:它会把上面四条 top/left/right/bottom 一起重置掉(同优先级下后写覆盖先写)。 站内若用 inset:-2px 简写定位,由上面这四条长写 !important 按优先级压过即可,不需要再写 inset。 */ box-sizing: border-box !important; } /* 【② 「Ta的粉丝」左边图标比「全部关注」的大且不居中】 探针:两个图标盒子都是 [16x16](top=68),尺寸本来一致; 差别在**画法** —— 第一个是 .follow-sidebar-item__icon 矢量图 第二个是 图标字体 图标字体是按 baseline 排的,且字号可能大于 16px, 于是字形溢出盒子、看起来更大且偏上。 → 统一成 inline-flex + 居中 + 字号/行高 16, 并把字体的 ::before 字号也钉住(字形画在伪元素上)。 ---- */ body.bili-space-page .relation-aside .vui_sidebar-item-title > svg, body.bili-space-page .relation-aside .vui_sidebar-item-title > i, body.bili-space-page .relation-aside .follow-sidebar-item__icon, body.bili-space-page .relation-aside .relation-aside__icon { display: inline-flex !important; align-items: center !important; justify-content: center !important; flex: 0 0 16px !important; width: 16px !important; min-width: 16px !important; max-width: 16px !important; height: 16px !important; min-height: 0 !important; max-height: 16px !important; margin: 0 !important; padding: 0 !important; font-size: 16px !important; line-height: 16px !important; vertical-align: middle !important; box-sizing: border-box !important; } body.bili-space-page .relation-aside .relation-aside__icon::before, body.bili-space-page .relation-aside .follow-sidebar-item__icon::before, body.bili-space-page .relation-aside .vui_sidebar-item-title > i::before { font-size: 16px !important; line-height: 16px !important; display: block !important; width: 16px !important; height: 16px !important; text-align: center !important; } /* 【③ 仅在本页隐藏顶部自建搜索栏】 #my-bili-searchbar 是脚本自建的 fixed 搜索栏(360x52), #app 的 padding-top:52px 就是给它留的位; 两者要一起去掉,否则页面顶部会空出 52px。 ⚠ 作用域名限定在关注/粉丝页:别的页面搜索栏照常显示。 ⚠ 用 :has() 精确命中 route_relationFollow / route_relationFans。 ---- */ body.bili-space-page:has(main.space-main.route_relationFollow) #my-bili-searchbar, body.bili-space-page:has(main.space-main.route_relationFans) #my-bili-searchbar { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; } body.bili-space-page:has(main.space-main.route_relationFollow) > #app, body.bili-space-page:has(main.space-main.route_relationFans) > #app { padding-top: 0 !important; } /* 【④ 底部分页条:与直播页完全一致】 v132.24 重写 —— v132.23 那条把 vui_pagenation 写坏了: · 泛化的 [class*="pagenation"] 连 --btns 一起命中, 给它加了 justify-content:center —— 按钮总宽超出 360 时 居中会让左侧溢出到滚动区外,看起来就"只剩一个 5"; · padding:0 又把站内的 pt/pb 8px 抹掉了。 直播页实测(bili_probe 直播页底部…txt): .mt_x50.mb_lg.flex_center [360x50] top=742 bottom=792 mt=8 mb=8 .vui_pagenation [360x50] pt=8 pb=8 .vui_pagenation--btns [360x34] top=750 bottom=784 ovf=auto 上一页按钮 [61x34] left=8 数字按钮 [32x34] 上一张卡 bottom=734 → 分页条顶 742 = **8px** 分页条底 792 → 屏幕底 800 = **8px** 套到空间页(结构 .follow-main > .vui_pagenation > --btns): · .item 自带 mb:8 → 分页条 mt 用 0,否则会叠成 16 · .vui_pagenation 只保留 pt/pb 8(与直播页一致) · .follow-main 原生 pb:8 → 分页条底距页面底 8px ✓ → 按钮距最后一张卡 = 8(mb) + 8(pt) = 16 按钮距页面底 = 8(pb) + 8(pb) = 16 两边相等 ⚠ --btns 必须**左对齐 + 横向可滚动**,与直播页一致: 按钮排不下时靠横滑看,不能用 center(会溢出被裁)。 ---- */ body.bili-space-page .follow-main > .vui_pagenation, body.bili-space-page .fans-main > .vui_pagenation, body.bili-space-page .follow-main > [class*="pagenation"], body.bili-space-page .fans-main > [class*="pagenation"] { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; /* 卡片间距已由 .item 的 mb:8 给出,这里 mt 必须 0,否则叠成 16 */ margin: 0 !important; padding: 8px 0 !important; box-sizing: border-box !important; } /* --btns:左起排布 + 横向滚动,按钮不收缩、不换行 */ body.bili-space-page .follow-main .vui_pagenation--btns, body.bili-space-page .fans-main .vui_pagenation--btns { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; justify-content: flex-start !important; align-items: center !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 34px !important; margin: 0 !important; padding: 0 8px !important; overflow-x: auto !important; overflow-y: hidden !important; box-sizing: border-box !important; } body.bili-space-page .follow-main .vui_pagenation--btns > *, body.bili-space-page .fans-main .vui_pagenation--btns > * { flex: 0 0 auto !important; flex-shrink: 0 !important; white-space: nowrap !important; height: 34px !important; min-height: 34px !important; max-height: 34px !important; } /* 列表容器自身不留额外底部间距,间距统一由 .follow-main 的 8px 负责 */ body.bili-space-page .follow-main section.relation-content, body.bili-space-page .fans-main section.relation-content, body.bili-space-page .relation-content > .items { margin-bottom: 0 !important; padding-bottom: 0 !important; } /* ============================================================ ---- 14) v132.25:搜索页分页条上下间距(修复翻页后归零)---- 两份探针对比(同为「搜索页 → 综合页」,仅页数不同): 第 1 页(search-page-all) .search-page-all > div > div.flex_center.mt_x50.mb_x50 [360x50] top=742 bottom=792 mt=8 mb=8 ← 正常 .vui_pagenation [360x50] pt=8 pb=8 .vui_pagenation--btns [360x34] top=750 bottom=784 video-list.row bottom=734 → 卡片底 → 按钮顶 = 750−734 = 16 按钮底 → 容器底 = 800−784 = 16 第 2 页(容器类名变成 search-page-video!) .search-page-video.i_wrapper > div.flex_center.mt_x50.mb_lg [360x50] top=750 bottom=800 mt=0 mb=0 ← 出问题 video-list.row bottom=750 → 卡片底 → 按钮顶 = 758−750 = 8 (应为 16) 按钮底 → 容器底 = 800−792 = 8 (应为 16) 直播页(基准,一直是对的): .search-page-live > div > div.mt_x50.mb_lg.flex_center [360x50] top=742 bottom=792 mt=8 mb=8 → 上下各 16px 【根因】v132.12 为修骨架屏窄屏,加了「容器链满宽」兜底, 其中一条是 html body.bili-search-page #app .search-page > div { margin-top:0; margin-bottom:0 } 特异性 (1,2,3),远高于 1941 行那条 body.bili-search-page [class*="mt_x50"] { margin:8px 0 } 第 1 页的分页条是 .search-page-all 的**孙级**(中间隔了一层 无类名 div),不满足 "> div",所以躲过一劫; 第 2 页容器变成 .search-page-video,分页条正好是它的 **直接子级**,被这条命中 → margin 双双归零。 修法:补一条特异性更高的规则(多带一个类/属性选择器), 把所有子页的分页条外层统一拉回 mt=8 / mb=8, 与直播页完全一致。 ---- */ html body.bili-search-page #app .search-page > .flex_center.mt_x50, html body.bili-search-page #app .search-page > .mt_x50.mb_lg, html body.bili-search-page #app .search-page > .mt_x50.mb_x50, /* ⚠ 不写裸的 "> .flex_center":会误伤 .search-page 下其它居中容器。 只认带 mt_x50 的(B站分页条专用间距类),风险最小。 */ html body.bili-search-page #app .search-page > div[class*="mt_x50"], body.bili-search-page .search-page > div[class*="mt_x50"], body.bili-search-page [class*="mt_x50"].flex_center { margin-top: 8px !important; margin-bottom: 8px !important; } /* 分页器本体与按钮排布:与直播页一致(pt/pb 8、按钮左起横滑) */ html body.bili-search-page #app .search-page > div[class*="mt_x50"] .vui_pagenation, body.bili-search-page .search-page > div[class*="mt_x50"] .vui_pagenation { padding: 8px 0 !important; margin: 0 !important; } /* ============================================================ ---- 15) v132.26:综合页分页条上下间距对称 ---- 两份探针(搜索页→综合→1 / →2)实测一致: .video-list.row [360x3788] top=-3054 bottom=734 pt=8 pb=8 → 最后一张卡片底 = 734 − 8 = 726 分页外层 mt=8 → 742;.vui_pagenation pt=8 → 按钮顶 750 → 卡片底 726 → 按钮顶 750 = **24px** → 按钮底 784 → 页面底 800 = **16px** (差 8px) 直播页(基准,一直是对的): .live-room-cards.row pt=0 pb=0 → 最后卡片底 = row 底 = 734 → 734 → 按钮顶 750 = 16px → 按钮底 784 → 页面底 800 = 16px 两边相等 根因:综合页的列表行 .video-list.row 有 pt/pb 各 8, 底部那 8px 又叠加了一次分页条的 mt=8,于是上边 24、下边 16。 修法:只把列表行的 padding-bottom 归零(上边 8 保留), 最后一张卡与列表行底齐平 → 16 / 16 对称,与直播页一致。 ⚠ 用 div 前缀抬特异性:原有规则最高到 html body.bili-search-page #app .video.i_wrapper.search-all-list > .video-list.row (id1 / class6 / type2), 这里补上 div 类型选择器(type4)才能压过它。 ---- */ html body.bili-search-page #app div.search-page-video > div.video-list.row, html body.bili-search-page #app div.video.i_wrapper.search-all-list > div.video-list.row, html body.bili-search-page #app div.search-page > div.video-list.row { padding-bottom: 0 !important; } /* ============================================================ ---- 16) v132.27:播放页「控制栏按键弹出的面板」改为视口居中 ---- 适用范围:视频播放页(body.bili-video-page)/ 列表播放页 (body.bili-video-page.bili-list-page,同时带两个类,所以 只写 bili-video-page 就覆盖了)/ 番剧播放页(bili-bangumi-page)。 参照物:直播间页(bili-live-room-page)已有的规则 .quality-wrap .panel / .control-area .panel → position:fixed; top/left:50%; transform:translate(-50%,-50%) max-height:calc(100vh - 32px); overflow-y:auto border-radius:10px; z-index:2147483000 本段把同一组值原样搬到三个播放页的面板上。 【改前 · 面板被钉在播放器角落】(番剧页探针实测,播放器仅 338x190,位于 left=11 top=39 → 11..349 / 39..229) ul.bpx-player-ctrl-playbackrate-menu [70x166] top=34 left=186 absolute div.bpx-player-ctrl-quality-menu-wrap [95x166] top=34 left=134 absolute div.bpx-player-ctrl-setting-box [132x140] top=82 left=159 absolute div.bpx-player-dm-setting-wrap [320x359] top=0 left=20 fixed z=1001 前三个是 absolute、相对控制栏按键定位,于是整块挤在播放器 右下角那一小片里(倍速面板 166 高 ≈ 播放器 190 高,几乎顶满, 选项还得贴着画面边缘);弹幕面板虽已是 fixed,但 top:0 贴在屏幕顶边、没有垂直居中,359 高的内容也无从滚动。 【改后】四个面板统一:fixed + 视口中心 + 限高可滚动, 与直播间一致。 ⚠ 为什么 fixed 不会被播放器裁掉: 番剧页 .bpx-player-container / .big-nano-player / #bilibili-player 等祖先 tf 均为 none(探针实测), 且弹幕面板原本就是 fixed 并能画出 359 高(远超播放器 190 高),说明这条链路上没有 transform/filter 制造 包含块、也没有 overflow 裁切,fixed 直接以视口定位。 ⚠ 旧规则(quality/playbackrate 的 bottom:22px、setting-box 的 bottom:0 right:0、dm-setting-wrap 的 top:0)与本段 特异性相同但写在前面,本段在后 → 由本段胜出; 为保险起见 right/bottom 也显式写成 auto。 ---- */ body.bili-video-page .bpx-player-ctrl-quality-menu-wrap, body.bili-video-page .bpx-player-ctrl-playbackrate-menu, body.bili-video-page .bpx-player-ctrl-setting-box, body.bili-video-page .bpx-player-ctrl-subtitle-box, body.bili-video-page .bpx-player-ctrl-eplist, body.bili-video-page .bpx-player-dm-setting-wrap, body.bili-bangumi-page .bpx-player-ctrl-quality-menu-wrap, body.bili-bangumi-page .bpx-player-ctrl-playbackrate-menu, body.bili-bangumi-page .bpx-player-ctrl-setting-box, body.bili-bangumi-page .bpx-player-ctrl-subtitle-box, body.bili-bangumi-page .bpx-player-ctrl-eplist, body.bili-bangumi-page .bpx-player-dm-setting-wrap { position: fixed !important; top: 50% !important; left: 50% !important; right: auto !important; bottom: auto !important; transform: translate(-50%, -50%) !important; /* 宽度随内容:小菜单(倍速 70 / 清晰度 95 / 设置 132) 至少给到 160 与直播间一致;弹幕面板自身 320 保持原样, 但一律不许超过屏宽。 ⚠ 故意**不写** width:auto / height:auto: 若站内是用「height:0 + overflow:hidden」收起面板的, 写死 height:auto 会把关闭态也撑开、面板再也关不掉。 不写则关闭态保持站内给的 0 高度,展开态本来就是 auto, 两种收起实现都安全。 */ min-width: 160px !important; max-width: calc(100vw - 32px) !important; max-height: calc(100vh - 32px) !important; margin: 0 !important; border-radius: 10px !important; overflow-x: hidden !important; overflow-y: auto !important; -webkit-overflow-scrolling: touch !important; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45) !important; z-index: 2147483000 !important; } /* 弹幕面板是「左栏 + 右栏」两列布局、需要 320 宽, min-width:160 对它无害,但显式保底更稳 */ body.bili-video-page .bpx-player-dm-setting-wrap, body.bili-bangumi-page .bpx-player-dm-setting-wrap { min-width: 0 !important; } /* 面板内条目:宽度铺满面板,不再被旧规则的 95px 上限卡住 (旧规则 .bpx-player-ctrl-quality-menu-item max-width:95px 是为贴着按键定位时写的,居中后不需要了) */ body.bili-video-page .bpx-player-ctrl-quality-menu-item, body.bili-bangumi-page .bpx-player-ctrl-quality-menu-item { max-width: none !important; width: 100% !important; } /* ============================================================ ---- 17) v132.28:面板被控制栏那排按键压住 ---- 现象:面板居中后,播放器底部那排按键(播放/倍速/清晰度/全屏…) 反而画在面板**上方**,而且这些按键还能点到。 探针 bili_probe_2026-10-01_12-59-24(列表播放页,暂停态): div.bpx-player-control-wrap.new [338x36] pos=absolute **z=75** └─ .bpx-player-control-bottom └─ .bpx-player-control-bottom-right (static, z auto) ├─ div.bpx-player-ctrl-btn.bpx-player-ctrl-quality │ pos=relative **z=2** ← 每个按钮都建了堆叠上下文 ├─ div.bpx-player-ctrl-btn.bpx-player-ctrl-playbackrate │ pos=relative **z=2** └─ …(倍速/设置/字幕/全屏,全是 z=2) .bpx-player-new-dialogue-area z=999(播放器内,在控制栏之上) 【根因】面板是按键的**后代**,而每个 .bpx-player-ctrl-btn 都是 position:relative + z-index:2 → 各自创建了一个堆叠上下文。 面板的 z-index 再大(本脚本给的 2147483000)也只在 它所属按钮那口"井"里比较,出不去; 而所有按钮的 z-index 都是 2、层级相同,于是**按 DOM 顺序 后来居上** —— 排在后面的按钮(全屏等)就盖在了面板上面, 点击自然也落在按键上。 修法(纯 CSS,两件事): ① 把"带面板的那几个按钮"自己的 z-index 提到极高 (2147483646):它们仍在 control-wrap 的上下文里, 但在这口井内部已是最高,整个子树(含 fixed 面板) 就会压过同级其它 z=2 的按钮。 面板收起时它是 0 尺寸/不可见,提 z 不产生任何视觉影响, 所以可以**无条件**写,不必依赖站内的展开类 (.bpx-state-show)或 :has() 兜底。 ② 面板展开时把整条控制栏抬到播放器内最高: 控制栏自身 z=75,而播放器内的 .bpx-player-new-dialogue-area 是 z=999, 不抬的话它仍会盖在面板上。 ⚠ 只写 1000(够压过 999 即可),不写极大值, 免得影响播放器以外的页面层级。 ---- */ /* ① 只抬「当前展开的那个」按钮 —— ⚠ v132.28 这里犯了个错:把**所有**带面板的按钮都抬到了 同一个 2147483646。它们之间层级又相等了,于是重新回到 「按 DOM 顺序后来居上」: 点开倍速(nth 3)→ 排在它后面的设置(nth 5)同值且更晚 → 照样盖在倍速面板上。这正是"只修好了一部分"的原因 (只有排在最后的那个面板才是干净的)。 现在改成只认展开态:站内会给展开的按钮加 .bpx-state-show, JS 也会补 .bili-panel-host 兜底。其它按钮一律不抬。 */ body.bili-video-page .bpx-player-ctrl-btn.bpx-state-show, body.bili-bangumi-page .bpx-player-ctrl-btn.bpx-state-show, body.bili-player-panel-open .bili-panel-host { z-index: 2147483646 !important; } /* ② 面板打开时把控制栏里**其余**按键统统压到 0 —— 与 DOM 顺序彻底解耦:谁展开谁在最上层。 (特异性 0,4,1 > 上面 v132.28 残留下来的 0,3,1,能压住) */ body.bili-player-panel-open .bpx-player-ctrl-btn:not(.bpx-state-show):not(.bili-panel-host), body.bili-player-panel-open .bpx-player-dm-setting:not(.bili-panel-host) { z-index: 0 !important; } /* ③ 控制栏整体:常态压过播放器内 z=999 的 dialogue-area; 面板展开时抬到顶层(body class 由 JS 打,不依赖 :has) */ body.bili-video-page .bpx-player-control-wrap, body.bili-bangumi-page .bpx-player-control-wrap { z-index: 1000 !important; } body.bili-player-panel-open .bpx-player-control-wrap { z-index: 2147483646 !important; } /* ============================================================ ---- 18) v132.30:全站隐藏「稍后再看」按键 ---- 原有的隐藏规则(7730 行附近)只覆盖了 body.bili-home-page / .bili-search-page / .bili-space-page 三处,视频播放页、列表播放页、番剧播放页、动态页都没管到。 探针实证的两种实现(站内至少有 3 套,类名互不相同): ① 首页 / 搜索页 / 空间页 —— 卡片封面右上角 div.bili-watch-later--wrap pos=absolute z=9 └─ div.bili-watch-later.bili-watch-later--pip ├─ svg.bili-watch-later__icon └─ span.bili-watch-later__tip 文本「稍后再看」 (搜索页探针 bili_probe_2026-09-30_20-35-39 实测) ② 视频播放页右侧推荐卡 —— 封面右上角 div.watch-later-video.van-watchlater.black [28x28] z=20 path: …div.video-page-card-small > .card-box > .pic-box > .watch-later-video… (隐藏掉稍后再看4.txt 实测) ③ 列表播放页右侧推荐卡 —— 封面右上角 div.watch-later-video.van-watchlater.black [28x28] z=20 path: …div.recommend-video-card.video-card > .card-box > .pic-box > .watch-later-video… (隐藏掉稍后再看5.txt 实测) ②③ 是**同一套**组件(van-watchlater),只是宿主卡片类名不同, 所以按 .watch-later-video / .van-watchlater 命中即可同时覆盖 视频播放页与列表播放页。 修法: · 精确类名:把两套实现的所有节点名一次列全,全站生效; · 泛化兜底:[class*="watch-later"] / [class*="watchlater"] 覆盖站内可能存在的其它变体(各页还可能有第 4、第 5 处)。 ⚠ 泛化有"误伤大容器"的风险,故这里**排除**页面级容器 (body/html/#app/main/section/article 等), 再由 JS 用尺寸二次把关(见 hideWatchLaterButtons)。 ---- */ body .bili-watch-later--wrap, body .bili-watch-later, body .bili-watch-later--pip, body .bili-watch-later__icon, body .bili-watch-later__tip, body .bili-card-watch-later, body .watch-later-video, body .van-watchlater, body [class*="watch-later"]:not(body):not(html):not(#app):not(main):not(section):not(article):not(aside), body [class*="watchlater"]:not(body):not(html):not(#app):not(main):not(section):not(article):not(aside) { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; max-width: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; pointer-events: none !important; } /* JS 兜底打的标记(尺寸判据通过后才打,绝不会误伤整页) */ body .bili-hide-watch-later { display: none !important; visibility: hidden !important; pointer-events: none !important; } /* ============================================================ ---- 19) v132.31:动态页视频卡片的「稍后再看」---- 上面 v132.30 那份只认 watch-later / watchlater 这类词, 动态卡片的稍后再看**完全不含这些词**,所以剩下 3 处没被隐藏: ① 主动态页(t.bilibili.com,body.bili-dynamic-page) ② UP主空间页 → 动态 ③ UP主空间页 → 搜索(动态里带「投稿了视频」的卡片) 两份探针实证(隐藏了两处侧边栏…txt / 再来一份.txt): a.bili-dyn-card-video(.hide-border) [270x181] └─ .bili-dyn-card-video__header └─ .bili-dyn-card-video__cover [270x152] └─ .bili-dyn-card-video__cover__mask └─ div.bili-dyn-card-video__mark [28x28] pos=absolute,落在封面右上角 (封面 left=45 宽 270 → mark left=281, 281+28=309 贴着右边缘 315; 封面 top=308 → mark top=314) └─ div.bili-dyn-card-video__mark__tip (hover 提示) 28x28 这个尺寸与播放页的 .watch-later-video.van-watchlater (探针实测同为 28x28)完全一致 —— 就是同一个稍后再看按键的 动态页版本,只是类名换成了 __mark。 ⚠ 别误伤同层的 .bili-dyn-card-video__cover-shadow: 那是右下角的时长(13:17),不是按键。这里只点名 __mark。 ---- */ body .bili-dyn-card-video__mark, body .bili-dyn-card-video__mark__tip, body .bili-dyn-card-video__cover__mask > .bili-dyn-card-video__mark { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-width: 0 !important; min-height: 0 !important; max-width: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; pointer-events: none !important; } /* ============================================================ ---- 20) v132.34:UP主空间 →「音频」页改 3 列手机布局 ---- 适用:main.space-main.route_uploadAudio(投稿 → 音频 标签) 结构:.upload-content > .audio > .audio-body > .audio-list > .audio-item > .audio-card ├─ .audio-card__top │ └─ a.bili-cover-card.audio-card__cover (1:1) │ ├─ .bili-cover-card__thumbnail absolute │ └─ .bili-cover-card__stats absolute(播放量/时长) └─ .audio-card__bottom ├─ a.title → .vui_ellipsis.multi-mode(两行) └─ .subtitle(日期) 【改前】(用户直接粘贴的探针,360 屏) .upload-content [360x341] left=0 .audio [344x325] left=8 ← 距屏幕边缘已是 8 ✓ .audio-list [344x141] left=8 .audio-item [44x141] left=8 / 68 / 128 → 卡宽仅 44、间距 16,三张一共只占 8..172, 右半屏 172..352 全空 —— PC 侧栏尺寸的卡片直接搬到了手机上。 【改后】3 列铺满: .audio-list 宽 344,gap 8 列宽 = (344 − 2×8) / 3 = 328 / 3 ≈ 109.33 第 1 列 left=8,第 3 列 right=352 → 距屏幕边缘仍各 8 ✓ 卡片背景与视频卡完全一致(抄 1834 行那套): background:#fff; border:none; border-radius:8px; box-shadow:0 1px 3px rgba(0,0,0,0.08); overflow:hidden 封面保持原生 1:1(原生是 width:44 + padding-top:44px 撑方, 这里改成 padding-top:100%,宽度变了比例不变)。 标题与日期居中。 ---- */ body.bili-space-page .upload-content .audio-list { display: grid !important; grid-template-columns: repeat(3, minmax(0, 1fr)) !important; grid-gap: 8px !important; gap: 8px !important; row-gap: 8px !important; column-gap: 8px !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; align-items: start !important; align-content: start !important; grid-auto-rows: auto !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; background: transparent !important; float: none !important; list-style: none !important; } body.bili-space-page .upload-content .audio-item { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; float: none !important; } /* 卡片:与视频卡同一张皮 */ body.bili-space-page .upload-content .audio-card { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; background: #ffffff !important; border: none !important; border-radius: 8px !important; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08) !important; overflow: hidden !important; box-sizing: border-box !important; position: relative !important; float: none !important; } /* 封面:原生 width:44 + padding-top:44px 撑 1:1, 宽度变成 109 后改用 padding-top:100%,比例不变 */ body.bili-space-page .upload-content .audio-card__top { display: block !important; width: 100% !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } body.bili-space-page .upload-content .audio-card__cover { display: block !important; position: relative !important; width: 100% !important; max-width: 100% !important; height: auto !important; min-height: 0 !important; max-height: none !important; /* 1:1 —— 百分比 padding 以包含块宽度为基准 */ padding: 100% 0 0 0 !important; margin: 0 !important; box-sizing: border-box !important; overflow: hidden !important; } body.bili-space-page .upload-content .audio-card__cover > .bili-cover-card__thumbnail { position: absolute !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; width: 100% !important; height: 100% !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; box-sizing: border-box !important; } body.bili-space-page .upload-content .audio-card__cover img { width: 100% !important; height: 100% !important; max-width: none !important; max-height: none !important; object-fit: cover !important; margin: 0 !important; padding: 0 !important; display: block !important; } /* 播放量 / 时长:仍压在封面底部,左右各留 4px */ body.bili-space-page .upload-content .bili-cover-card__stats { left: 0 !important; right: 0 !important; bottom: 0 !important; width: 100% !important; max-width: 100% !important; padding: 14px 4px 4px !important; box-sizing: border-box !important; display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: flex-start !important; gap: 6px !important; } /* 底部文字区:标题 + 日期,居中 */ body.bili-space-page .upload-content .audio-card__bottom { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 6px 4px 8px !important; box-sizing: border-box !important; text-align: center !important; } body.bili-space-page .upload-content .audio-card__bottom .title, body.bili-space-page .upload-content .audio-card__bottom a.title, body.bili-space-page .upload-content .audio-card__bottom .vui_ellipsis, body.bili-space-page .upload-content .audio-card__bottom .subtitle, body.bili-space-page .upload-content .audio-card__bottom .subtitle > span { width: 100% !important; max-width: 100% !important; min-width: 0 !important; text-align: center !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; white-space: normal !important; overflow: hidden !important; } /* 标题两行截断、日期单行;行间距沿用站内原生的 4px */ body.bili-space-page .upload-content .audio-card__bottom a.title { font-size: 13px !important; line-height: 18px !important; max-height: 36px !important; -webkit-line-clamp: 2 !important; display: -webkit-box !important; -webkit-box-orient: vertical !important; } body.bili-space-page .upload-content .audio-card__bottom .subtitle { margin-top: 4px !important; font-size: 11px !important; line-height: 16px !important; max-height: 16px !important; color: #9499a0 !important; text-align: center !important; } /* ============================================================ ---- 21) v132.35:音频页底部分页条上下各 16px ---- 用户粘贴的探针(音频页,改 3 列后): .audio-footer [344x?] pt=60px pb=60px ← PC 的大留白 └─ .vui_pagenation.vui_pagenation--jump.audio-pagination └─ .vui_pagenation--btns ├─ 上一页 [61x34] ├─ 1…7 / … / 10 各 [32x34] └─ 下一页 [61x34] left=405 → 405+61=466 > 360 按钮 top=682 bottom=716(高 34) 目标:按键距「最后一张卡片」= 16px,距「屏幕底部」= 16px。 链路(脚本已设的值): .upload-content padding: 0 8px 16px 8px ← 底部 16px 已有 .audio > .audio-body(最后卡片在此,pb=0) .audio > .audio-footer ← 唯一剩下的间距来源 换算: 按键顶 = audio-body 底 + footer 的 padding-top → padding-top 取 16 即得「距最后卡片 16px」✓ 按键底 = footer 内容底(footer pb=0) 页面底 = upload-content 内容底 + upload-content 的 pb 16 → footer pb 取 0,则 页面底 − 按键底 = 16 ✓ ⚠ 分页器自身的 margin / padding 必须一并归零, 否则上面的等式会被它再叠一层。 ⚠ 另外:按钮总宽 466 > 屏宽 360(下一页 left=405), 说明这条按钮行本来就是横向溢出的。这里给 --btns 加 overflow-x:auto + 按钮 flex-shrink:0, 让它横向可滑动、不把页面撑出横向滚动条 (与关注/粉丝页 v132.24 处理办法一致)。 ---- */ body.bili-space-page .upload-content .audio-footer { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; margin: 0 !important; padding: 16px 0 0 0 !important; box-sizing: border-box !important; } body.bili-space-page .upload-content .audio-pagination, body.bili-space-page .upload-content .vui_pagenation { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; } /* 按钮行:左起横滑,不撑破页面 */ body.bili-space-page .upload-content .vui_pagenation--btns { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; justify-content: flex-start !important; align-items: center !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; overflow-x: auto !important; overflow-y: hidden !important; box-sizing: border-box !important; } body.bili-space-page .upload-content .vui_pagenation--btns > * { flex: 0 0 auto !important; flex-shrink: 0 !important; white-space: nowrap !important; } /* ============================================================ ---- 22) v132.36:去掉「TA的音频」大标题 + 卡片上间距 8px ---- ⚠ 上一轮(v132.36 的说明)只写了文字、没落进文件, 用户刷新后完全没有变化。这里补上真实规则。 探针(音频页,3 列已生效): .upload-sidenav [360x42] top=112 bottom=154 pb=8 └─ .side-nav [344x34] top=112 bottom=146 ← 视频/图文/音频 按键底边 .upload-content top=154 └─ .audio top=154 ├─ .audio-header [344x34] top=154 bottom=188 │ ├─ .audio-header__top > .title [100x24] ← 「TA的音频」 │ └─ .audio-header__bottom [344x0] mt=30 └─ .audio-body top=218 mt=30px ← PC 留白 卡片 top=218 → 距按键底 146 = **72px** 目标:距按键 8px。 解法: ① .audio-header 整块 display:none → 154 之后直接是 body ② .audio-body 的 margin-top 由 30 归零 → 卡片顶 = 154 → 154 − 146(按键底)= **8px** ✓ ⚠ 关键坑:.upload-sidenav 自带 **padding-bottom:8px**, 这 8px 本身就是间距。若再给 .audio-body 加 margin-top:8px 就会变成 16px。所以这里必须取 0,不是 8。 ⚠ header 隐藏后 .audio-header__bottom 的 mt:30 也随之失效, 不会再撑高。 ---- */ body.bili-space-page .upload-content .audio-header, body.bili-space-page .upload-content .audio-header__top, body.bili-space-page .upload-content .audio-header__bottom, body.bili-space-page .upload-content .audio-header .title { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } body.bili-space-page .upload-content .audio-body { margin: 0 !important; padding: 0 !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: none !important; box-sizing: border-box !important; } /* 保险:万一 .audio 与 .upload-content 之间还有 margin 折叠, 把 .audio 自身的外边距也归零,保证卡片顶严格落在 154 */ body.bili-space-page .upload-content .audio { margin: 0 !important; padding: 0 !important; } /* ============================================================ ---- 23) v132.37:音乐播放器页(歌曲详情)适配 ---- 适用:/audio/auXXXX、/music/auXXXX(此前脚本完全没适配, body 上没有任何 bili-* 页面 class,故新增 bili-music-page)。 探针(音乐播放器页面.txt,360 屏,未开脚本): #internationalHeader … .mini-header.m-header.mini-type ← PC 头部 #app > div:nth-of-type(1) > .container.contain-div [1160x773] mt=40 left=0 right=0 └─ .row:nth-of-type(1) ├─ .gridview-content-container [1160x773] │ ├─ .row:nth-of-type(1) │ │ ├─ .col-6-med.col-5 [340x305] │ │ │ ├─ img.song-img [300x300] │ │ │ ├─ .song-intro 「简介」 │ │ │ ├─ .song-content.song-fold「以琴为媒,雨音相润。」 │ │ │ └─ .song-type 「歌曲分类:…」 │ │ └─ .col-9-med.col-7 [780x751] │ │ └─ .song-padding > .song-gap │ │ ├─ .song-title 「仙云幻境」 │ │ ├─ .song-time 「2024-12-30」 │ │ ├─ .song-play-num 「播放数 2772」 │ │ ├─ .song-info 「演奏 洛克王国 / 乐器 古筝/弦乐」 │ │ └─ .share-board │ │ ├─ .song-playbtn 「立即播放」★ 保留 │ │ └─ .song-share 分享/收藏/评论 │ └─ .row:nth-of-type(2) 「音乐人的热门歌曲」 └─ .gridview-sidebar 「浏览相似歌曲」 .song-complain-bg / .follow-up-toast / #app > .collection-bg #music-container.audioplayer [1120x60] pos=fixed z=104 └─ .ap-status > .ap-controller ├─ .ap-controller-left [234x60] │ ├─ .ap-cover [60x60] │ ├─ .ap-skipback-button [44x60] ★ 上一首 │ ├─ .ap-play-button [52x60] ★ 播放/暂停 │ └─ .ap-skipforward-button [44x60] ★ 下一首 ├─ .ap-controller-center [516x60] pt/pb 10 │ ├─ 行1:.ap-name / .ap-artist / .ap-time(00:00 / 00:36)★ 时间 │ └─ 行2:.ap-play-track-expand → .ap-play-track │ → .ap-progress-bar / .ap-progress-thumb ★ 进度条 ├─ .ap-controller-right [360x60] 收藏/音量/顺序/列表 └─ .ap-playlist.ap-playlist-hide 播放列表浮层 保留清单(用户指定): · 简介 / 以琴为媒,雨音相润。 / 歌曲分类 · 仙云幻境 / 2024-12-30 / 播放数 2772 / 演奏 洛克王国 / 乐器 古筝/弦乐 · 「立即播放」按键 · 底部:上一首 / 暂停 / 下一首 / 进度条 其余全部隐藏。 ⚠ 封面 img.song-img 不在清单里 → 按"其它全部隐藏"一并隐掉。 想恢复的话把下面 .song-img 那条删掉即可。 ⚠ 底部条里的 .ap-name / .ap-artist(歌名/歌手)与正文重复, 且 360 屏下 center 只剩约 190px 根本排不下 → 一并隐藏, 只留时间 + 进度条。 ⚠ 容器链里 .container.contain-div 是 PC 定宽 1160、 left/right 各 40 的居中盒,必须拉满并把 mt:40 收到 8; 底部播放器是 fixed,所以 body 要留出 60+8 的底部空间, 否则最后一行内容会被它压住。 ---- */ /* ---- 1. 隐藏:头部、侧栏、热门歌曲、分享、弹层 ---- */ body.bili-music-page #internationalHeader, body.bili-music-page .mini-header, body.bili-music-page .bili-header, body.bili-music-page .international-header, body.bili-music-page .gridview-sidebar, body.bili-music-page .gridview-content-container > .row:nth-of-type(2), body.bili-music-page .song-complain-bg, body.bili-music-page .follow-up-toast, body.bili-music-page .collection-bg, body.bili-music-page .song-share, /* 封面(用户保留清单里没有) */ body.bili-music-page .song-img { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* ---- 2. 容器链拉满宽 + 左右 8px + 底部留出播放器空间 ---- */ body.bili-music-page { background: #f4f5f7 !important; } body.bili-music-page #app, body.bili-music-page #app > div:nth-of-type(1), body.bili-music-page .container.contain-div, body.bili-music-page .row, body.bili-music-page .gridview-content-container, body.bili-music-page .col-6-med.col-5, body.bili-music-page .col-9-med.col-7, body.bili-music-page .song-padding, body.bili-music-page .song-gap { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin-left: 0 !important; margin-right: 0 !important; /* v132.39:⚠ 这里原来**没有清 padding** —— 只写了 width:100%。 探针「居中不正确.txt」实测: .col-9-med.col-7 [344x562] left=8 └─ .song-padding [344x554] ← width 已是 344 ✓ └─ .song-gap [294x230] ← 却是 294! 344 − 294 = 50 → .song-padding 自带左右各 25px 内边距。 width:100% 只把**边框盒**撑到 344,内容盒仍被 padding 吃掉 50, 里面的 .song-gap / .song-title / .song-time / .song-play-num / .share-board 一律只有 294 宽,按 294 居中 → 中心 = 8 + 147 = 155,比屏幕中心 180 **偏左 25px**。 「简介 / 施基」在 .col-6-med.col-5 下(无这层 padding), 宽度是完整的 344,所以唯独它俩是正的 —— 与用户描述完全吻合。 修法:补 padding: 0。 */ padding-left: 0 !important; padding-right: 0 !important; padding-top: 0 !important; padding-bottom: 0 !important; box-sizing: border-box !important; float: none !important; } /* 页面主体:顶部 8、左右 8、底部留出 60(播放器)+8 */ body.bili-music-page .container.contain-div { /* v132.38:顶部文字距屏幕顶 36px。 ⚠ 用 padding-top 而不是 margin-top —— margin 会和 #app / 内部 col 的 mt=5 折叠,数值会漂; padding 是硬偏移,song-intro 一定落在 36。 */ margin-top: 0 !important; padding: 36px 8px 68px 8px !important; box-sizing: border-box !important; } body.bili-music-page #app, body.bili-music-page .gridview-content-container { padding-left: 0 !important; padding-right: 0 !important; padding-top: 0 !important; padding-bottom: 0 !important; background: transparent !important; } /* 左右两列改成上下堆叠(DOM 顺序本来就是 左列 → 右列, 正好对应「简介→分类→标题→时间→播放数→演奏/乐器」的阅读顺序) */ body.bili-music-page .gridview-content-container > .row:nth-of-type(1) { display: block !important; } /* v132.38:两列站内自带 mt/mb 各 5px,会把首行文字再往下推, 让 36px 变 41px → 一律归零(列间距离改由 col-9 的 padding-top 给) */ body.bili-music-page .col-6-med.col-5 { margin: 0 !important; } body.bili-music-page .col-9-med.col-7 { margin: 0 !important; padding-top: 8px !important; } /* ---- 3. 正文:字号/行距按手机收一档,各行留 4px ---- */ body.bili-music-page .song-intro, body.bili-music-page .song-type, body.bili-music-page .song-title, body.bili-music-page .song-time, body.bili-music-page .song-play-num, body.bili-music-page .song-info, body.bili-music-page .song-content { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 0 !important; padding: 0 !important; box-sizing: border-box !important; white-space: normal !important; word-break: break-word !important; } body.bili-music-page .song-intro, body.bili-music-page .song-type { font-size: 13px !important; line-height: 20px !important; color: #61666d !important; margin-top: 6px !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; } body.bili-music-page .song-content { font-size: 14px !important; line-height: 22px !important; height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; margin-top: 4px !important; } /* ---- v132.56:隐藏「展开更多」按键 ---- 探针实测(展开更多按键目前没作用隐藏掉.txt): .song-introText [48x17] / left=8 / top=131,文字就是「展开更多」, 位于 .song-content.song-fold(简介正文,344x66)下方。 它在手机端点了没反应(站内的展开逻辑依赖 PC 版容器宽度, 窄屏下算不出可展开状态),留着只会误导用户去点,故整块隐藏。 选择器不带 DOM 层级,只认类名 —— 音乐页是 SPA,节点插入时机不定, body 上有 bili-music-page 类即可命中,无需 JS 兜底。 */ body.bili-music-page .song-introText { display: none !important; visibility: hidden !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; border: 0 !important; overflow: hidden !important; pointer-events: none !important; } /* 附带:按键既已隐藏,被 song-fold 截断的简介就再无途径展开, 这里解掉行数截断让全文显示(脚本此前只解了 max-height / overflow, 实测正文仍是 344x66 —— 三行,说明真正生效的是 -webkit-line-clamp。 display 必须回到 block,line-clamp 只在 -webkit-box 下起作用)。 */ body.bili-music-page .song-content, body.bili-music-page .song-content.song-fold, body.bili-music-page .content-span { display: block !important; -webkit-line-clamp: unset !important; line-clamp: unset !important; max-height: none !important; height: auto !important; overflow: visible !important; text-overflow: clip !important; } body.bili-music-page .song-title { font-size: 18px !important; line-height: 26px !important; font-weight: 500 !important; margin-bottom: 4px !important; } /* v132.38:全部文字居中;song-intro 的 mt 归零, 让它在 contain-div 的 36px padding 之后正好落在 top=36 */ body.bili-music-page .song-intro { margin-top: 0 !important; } body.bili-music-page .song-intro, body.bili-music-page .song-content, body.bili-music-page .content-span, body.bili-music-page .song-type, body.bili-music-page .song-type-title, body.bili-music-page .song-title, body.bili-music-page .song-time, body.bili-music-page .song-play-num, body.bili-music-page .song-info, body.bili-music-page .song-info-content, body.bili-music-page .song-info-div { text-align: center !important; } /* v132.38:song-gap / share-board 实测只有 294 宽(父级 344), 导致「立即播放」按 294 居中 → 中心 155,比屏幕中心 180 偏左 25px。 这里强制铺满,按键才会真正落在屏幕正中。 */ body.bili-music-page .song-gap, body.bili-music-page .share-board, body.bili-music-page .song-title, body.bili-music-page .song-time, body.bili-music-page .song-play-num, body.bili-music-page .song-info { width: 100% !important; max-width: 100% !important; min-width: 0 !important; /* v132.39:padding 一并归零,杜绝任何一层再吃掉宽度 */ padding-left: 0 !important; padding-right: 0 !important; margin-left: 0 !important; margin-right: 0 !important; box-sizing: border-box !important; } body.bili-music-page .song-time, body.bili-music-page .song-play-num { font-size: 12px !important; line-height: 18px !important; color: #9499a0 !important; margin-top: 4px !important; } body.bili-music-page .song-info, body.bili-music-page .song-info-content, body.bili-music-page .song-info-div { height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; font-size: 13px !important; line-height: 20px !important; } body.bili-music-page .song-info { margin-top: 8px !important; overflow: visible !important; } body.bili-music-page .song-info-div { margin-top: 2px !important; } /* ---- 4.「立即播放」按键:share-board 里只剩它,居中并放大 ---- */ body.bili-music-page .share-board { width: 100% !important; max-width: 100% !important; margin: 12px 0 0 0 !important; padding: 0 !important; display: flex !important; justify-content: center !important; box-sizing: border-box !important; } body.bili-music-page .song-playbtn { display: inline-flex !important; align-items: center !important; justify-content: center !important; min-width: 140px !important; height: 40px !important; min-height: 40px !important; margin: 0 auto !important; padding: 0 20px !important; border-radius: 20px !important; box-sizing: border-box !important; font-size: 15px !important; line-height: 40px !important; } /* ---- 5. 底部播放器:只留 上一首/播放暂停/下一首 + 时间 + 进度条 ---- */ body.bili-music-page #music-container, body.bili-music-page .ap-status, body.bili-music-page .ap-controller { width: 100% !important; max-width: 100% !important; min-width: 0 !important; box-sizing: border-box !important; } body.bili-music-page #music-container { left: 0 !important; right: 0 !important; bottom: 0 !important; height: auto !important; min-height: 60px !important; padding: 0 8px !important; background: #ffffff !important; box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.08) !important; } body.bili-music-page .ap-status { padding: 0 !important; } body.bili-music-page .ap-controller { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: center !important; /* v132.38:统一间距 g = 8px · 左:#music-container 自带 padding-left 8 · 右:这里补 padding-right 8 · 块与块(left ↔ center):gap 8 */ gap: 8px !important; padding: 0 8px 0 0 !important; margin: 0 !important; } /* 左:去掉封面,三个按键按原尺寸排 */ body.bili-music-page .ap-controller-left { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: center !important; /* v132.38:4 → 8,与外侧间距统一 */ gap: 8px !important; flex: 0 0 auto !important; width: auto !important; min-width: 0 !important; height: auto !important; margin: 0 !important; padding: 0 !important; } /* v132.38:按钮盒自带左右各 8px 内边距(盒子 44 / 图标 28), 不归零的话「图标距容器左边缘」= 16,而右边时间距边缘 = 8, 两边对不齐。归零后盒子宽度 = 图标宽度,视觉间距严格等于 g=8。 纵向点击区用 min-height:36 保住。 */ body.bili-music-page .ap-controller-left .ap-button { padding: 0 !important; width: auto !important; min-width: 0 !important; max-width: none !important; height: auto !important; min-height: 36px !important; max-height: none !important; margin: 0 !important; display: inline-flex !important; align-items: center !important; justify-content: center !important; box-sizing: border-box !important; } body.bili-music-page .ap-cover { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* 中:吃掉剩余宽度;歌名/歌手与正文重复且排不下 → 隐藏 */ body.bili-music-page .ap-controller-center { flex: 1 1 auto !important; width: auto !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; margin: 0 !important; /* v132.38:改成纵向两行(时间 + 进度条)垂直居中,行距由 gap 给 */ padding: 0 !important; display: flex !important; flex-direction: column !important; justify-content: center !important; align-items: stretch !important; gap: 8px !important; overflow: visible !important; box-sizing: border-box !important; } body.bili-music-page .ap-name, body.bili-music-page .ap-artist { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } body.bili-music-page .ap-controller-center-line { display: flex !important; flex-direction: row !important; align-items: center !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; } body.bili-music-page .ap-time { display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: flex-end !important; gap: 3px !important; flex: 0 0 auto !important; width: auto !important; margin-left: auto !important; font-size: 11px !important; line-height: 16px !important; color: #9499a0 !important; white-space: nowrap !important; } /* 进度条行:铺满,轨道可点可拖 */ body.bili-music-page .ap-play-track-expand { /* v132.38:行距已由 center 的 gap:8 负责,这里归零免得叠成 16 */ margin-top: 0 !important; display: block !important; } body.bili-music-page .ap-play-track, body.bili-music-page .ap-play-bar-wrap, body.bili-music-page .ap-buffer-bar, body.bili-music-page .ap-progress-bar { width: 100% !important; max-width: 100% !important; min-width: 0 !important; } body.bili-music-page .ap-play-track { height: 4px !important; border-radius: 2px !important; background: #e3e5e7 !important; } body.bili-music-page .ap-progress-bar { height: 4px !important; border-radius: 2px !important; background: #00aeec !important; } body.bili-music-page .ap-progress-thumb { width: 12px !important; height: 12px !important; border-radius: 50% !important; background: #00aeec !important; } /* 右:收藏 / 音量 / 顺序 / 播放列表 全部隐藏 */ body.bili-music-page .ap-controller-right, body.bili-music-page .ap-playlist { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* ============================================================ ---- 24) v132.40:「歌曲分类:」与歌词内容居中 ---- v132.38 已经给 .song-type 写过 text-align:center, 但探针 bili_probe_2026-10-01_17-44-57 实测它仍是左对齐: .song-type [344x20] left=8 ├─ span.song-type-title [65x20] left=8 ← 紧贴容器左边 └─ span [91x20] left=73 ← 紧接其后 若只是普通块级 + text-align:center,两段文字总宽约 160, 起点应在 (344−160)/2 ≈ 92,而不是 8。 起点 = 容器左边 → 说明这两个 span 是**排布在主轴起点的盒子**, 即 .song-type 很可能是 display:flex(或 inline-flex), 而 flex 容器里 text-align 对 flex item **无效**, 要靠 justify-content 才推得动。 修法(两条一起上,任何 display 都能居中): · 显式 display:block + width:100% —— 退回普通块级, 此时 text-align:center 生效(推荐路径); · 同时写 justify-content:center —— 万一站内用 !important 锁住了 flex,这条兜底仍能居中。 · 子级 span 改 inline,避免被当成 flex item 拉到起点。 ---- */ body.bili-music-page .song-type, body.bili-music-page .song-type-title, body.bili-music-page .song-type > span, body.bili-music-page .lrc-fold, body.bili-music-page #lrc-content, body.bili-music-page .song-lrc { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; text-align: center !important; justify-content: center !important; align-items: center !important; margin-left: 0 !important; margin-right: 0 !important; padding-left: 0 !important; padding-right: 0 !important; box-sizing: border-box !important; white-space: normal !important; word-break: break-word !important; } /* 「歌曲分类:」与后面的分类值要在同一行内联居中, 不能各自独占一行(display:block 会让两个 span 换行) */ body.bili-music-page .song-type > span, body.bili-music-page .song-type-title { display: inline !important; width: auto !important; max-width: none !important; } /* 歌词容器有 overflow:hidden + 固定 240 高,放开免得只露出一行 */ body.bili-music-page .lrc-fold { height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; margin-top: 12px !important; } body.bili-music-page #lrc-content, body.bili-music-page .song-lrc { height: auto !important; min-height: 0 !important; max-height: none !important; font-size: 13px !important; line-height: 22px !important; color: #61666d !important; } /* ============================================================ ---- 25) v132.41:歌手行居中 / 隐藏展开收起 / 立即播放上间距 36 ---- 探针「未滑动.txt」(有歌词的曲目,360 屏): .song-gap [344x230] top=138 bottom=368 ★ 固定 230 高 ├─ .song-title top=138 bottom=165 (27) mb=4 ├─ .song-time top=169 bottom=187 (18) mt=4 ├─ .song-play-num top=191 bottom=209 (18) mt=4 └─ .song-info top=217 bottom=237 (20) mt=8 ← 内容到此为止 .share-board [344x40] top=380 mt=12 └─ .song-playbtn top=380 ← 立即播放 .song-lrcFold [24x16] top=716 text=收起 ★ 无效按键 ① 歌手行居中 —— 与 v132.40 的「歌曲分类」完全同因: .song-info [344x20] left=8 ├─ .song-info-theme [32x20] left=8 ← 紧贴容器左边 └─ div [41x20] left=40 ← 紧接其后(8+32) 两段总宽 73,居中后起点应在 8+(344−73)/2 ≈ 143, 实际 = 8 → 容器是 flex / 或子级为块级盒,text-align 推不动。 ② 立即播放上间距 380 − 237 = **143px**,目标 36px: 多出来的 131px 来自 .song-gap 的**固定 230 高** (PC 版式里它要容纳右侧元素,手机端内容只有 99 高)。 解法:song-gap 高度改 auto(内容 99)→ 底边落到 237, 再把 share-board 的 margin-top 由 12 改成 36 → 立即播放顶 = 237 + 36 = 273,距上方文字正好 36 ✓ ③「收起 / 展开」按键(.song-lrcFold / .song-lrcUnfold) 点击无效 —— 因为 v132.40 已把歌词容器改成高度自适应、 全量铺开,这个折叠开关失去作用对象 → 直接隐藏。 ---- */ /* ① 歌手行居中:与歌曲分类同一套(block + inline 双路径) */ body.bili-music-page .song-info, body.bili-music-page .song-info-content, body.bili-music-page .song-info-div, body.bili-music-page .song-info-theme, body.bili-music-page .song-info-detail { display: block !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; text-align: center !important; justify-content: center !important; align-items: center !important; margin-left: 0 !important; margin-right: 0 !important; padding-left: 0 !important; padding-right: 0 !important; box-sizing: border-box !important; white-space: normal !important; word-break: break-word !important; } /* 「歌手」与后面的值要同一行内联居中,不能各占一行 */ body.bili-music-page .song-info-theme, body.bili-music-page .song-info-div > div, body.bili-music-page .song-info-detail, body.bili-music-page .song-info-detail > span { display: inline !important; width: auto !important; max-width: none !important; } /* ② 立即播放上间距 36px:song-gap 收回内容高度 + share-board mt=36 */ body.bili-music-page .song-gap { height: auto !important; min-height: 0 !important; max-height: none !important; overflow: visible !important; } body.bili-music-page .share-board { margin-top: 36px !important; } /* ③ 隐藏无效的「展开 / 收起」按键 */ body.bili-music-page .song-lrcFold, body.bili-music-page .song-lrcUnfold { display: none !important; visibility: hidden !important; width: 0 !important; height: 0 !important; min-height: 0 !important; max-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* ★★★ v132.47 起:登录弹层(.bili-mini-mask)适配手机屏 + 隐藏扫码区 ★★★ 现象:点「立即登录」弹出的登录面板整体左偏、大半在屏幕外,只能看到中间一块。 探针实测(bili_probe_2026-10-02_10-19): .bili-mini-mask 360x800 / fixed / z=10010 ← 铺满视口,正常 .bili-mini-content-wp 820x460 / rect left=-230 ← PC 定宽 820 .login-scan-wp 214x271 / left=-173(扫码区,整块在屏外) .bili-mini-line 1x228 / left=87 (中间竖分割线) .bili-mini-login-right-wp 400x293 / left=133(右半,右侧溢出到 533) .bili-mini-close-icon 32x32 / left=538 (关闭键整个跑出屏幕) .login-agreement-wp 620x38 / left=-130 + translate(-310) 真因:面板是 PC 定宽 820,在 360 视口里居中后左右各溢出 230。 ★★★ v132.48 修正:v132.47 剩下三个问题 ★★★ 探针对比(登录面板_10-30-40 视频页 vs 另一个页面_10-33-04 动态页): 面板本体 .bili-mini-content-wp 两页均为 328x898 / top=-49 表单区 .bili-mini-login-right-wp 两页均为 296x293 / left=32 三方登录 .login-sns-wp 两页均为 296x75(内含三项各 99x47) ① 「密码」「忘记密码?」有的页面换行、有的正常: 动态页(正常).form_info 28x20 / input 122x20 / .eye-btn 20x20 / .clickable 70x20 视频页(换行).form_info 24x40 / input 138x20 / .eye-btn 17x20 / .clickable 61x40 —— .form__item 并不是 flex 容器,四个零件按 inline 排,谁先挤爆谁换行; input 的固有宽度由站内按页面算出(视频页 138、动态页 122), 视频页 input 多吃 16px,就把后面的「密码」压成竖排、「忘记密码?」压成两行。 肉眼"明明还有空",是因为那点空间全被 input 的固有宽度占住了。 修法:.form__item 改 display:flex + nowrap, input 改 flex:1 1 0 + min-width:0(只吃掉真正剩下的空间), .form_info / .eye-btn / .clickable 改 flex:0 0 auto + white-space:nowrap。 ② 协议文字一字一行竖排: .login-agreement-wp 实测 0x810 / left=328 —— 宽度被压成 0。 真因:.bili-mini-content-wp 站内是 display:flex(row), 扫码区隐藏后 .bili-mini-login-right-wp 独占 296(left=32), 协议区被挤到它右边(left=328)且宽度塌成 0 → 每个字一行, 顺带把面板高度撑到 898(原本只要 ~400)。 修法:.bili-mini-content-wp 改 display:block,两者上下堆叠; 协议区 100% 宽 + 居中,正好落在「微信/微博/QQ」按键下方。 ③ 面板没居中(top=-49,顶部被裁): 898 的面板在 800 视口里被 flex 的 align-items:center 居中, 上下各溢出 49,顶部直接被裁掉——这是 flex 居中的经典坑。 修法:面板改 margin:auto(内容小于容器时居中,大于容器时不裁顶、可完整滚动), 再加 max-height:calc(100vh - 32px) + overflow-y:auto 双保险。 另修:.bili-mini-customer-title(820x26 / absolute)在部分页面出现, 820 定宽同样溢出,改为 static + 100% + 居中。 选择器一律不带 body class —— 弹层挂在 body 上,任何页面点登录都会出现。 */ .bili-mini-mask { display: flex !important; align-items: center !important; justify-content: center !important; padding: 16px !important; box-sizing: border-box !important; overflow-y: auto !important; overscroll-behavior: contain !important; } .bili-mini-content-wp { /* 站内是 flex row —— 协议区被挤成 0 宽的根因,必须退回块流 */ display: block !important; flex-direction: column !important; flex-wrap: nowrap !important; position: relative !important; left: 0 !important; right: 0 !important; top: auto !important; width: calc(100vw - 32px) !important; max-width: 340px !important; min-width: 0 !important; height: auto !important; min-height: 0 !important; max-height: calc(100vh - 32px) !important; /* margin:auto 而非 align-items:center:内容超高时不裁顶,可完整滚动 */ margin: auto !important; padding: 52px 16px 20px !important; box-sizing: border-box !important; transform: none !important; overflow-x: hidden !important; overflow-y: auto !important; } .bili-mini-close-icon { position: absolute !important; top: 12px !important; right: 12px !important; left: auto !important; bottom: auto !important; width: 32px !important; height: 32px !important; margin: 0 !important; z-index: 30 !important; } /* ① 扫码登录整块 + 中间竖线:手机上用不到 */ .bili-mini-content-wp .login-scan-wp, .bili-mini-content-wp .login-client-qr-code, .bili-mini-content-wp .bili-mini-line { display: none !important; visibility: hidden !important; width: 0 !important; min-width: 0 !important; height: 0 !important; min-height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; } /* ② 右半区与表单:400 定宽 → 撑满面板 */ .bili-mini-login-right-wp, .bili-mini-login-right-wp .login-pwd-wp, .bili-mini-login-right-wp .login-sms-wp, .bili-mini-login-right-wp .tab__form { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin-left: 0 !important; margin-right: 0 !important; padding-left: 0 !important; padding-right: 0 !important; box-sizing: border-box !important; } /* ★ v132.48①:表单行改 flex —— 让 input 只吃剩下的空间, 把「密码」「忘记密码?」从换行里救回来 */ .bili-mini-login-right-wp .form__item { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: center !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; padding-left: 12px !important; padding-right: 12px !important; box-sizing: border-box !important; } .bili-mini-login-right-wp .form__item input { flex: 1 1 0 !important; width: auto !important; max-width: 100% !important; min-width: 0 !important; } .bili-mini-login-right-wp .form__item .form_info, .bili-mini-login-right-wp .form__item .eye-btn, .bili-mini-login-right-wp .form__item .clickable { flex: 0 0 auto !important; max-width: none !important; white-space: nowrap !important; } /* ③ 注册 / 登录 两个按键:等分一行 */ .bili-mini-login-right-wp .btn_wp { width: 100% !important; max-width: 100% !important; display: flex !important; gap: 8px !important; margin-top: 20px !important; box-sizing: border-box !important; } .bili-mini-login-right-wp .btn_wp > div { flex: 1 1 0 !important; width: auto !important; min-width: 0 !important; max-width: 100% !important; } /* ④ 其他方式登录(微信 / 微博 / QQ):整行居中 */ .bili-mini-login-right-wp .login-sns-wp { width: 100% !important; max-width: 100% !important; min-width: 0 !important; margin: 24px 0 0 !important; padding: 0 !important; box-sizing: border-box !important; } .bili-mini-login-right-wp .login-sns-title { width: 100% !important; text-align: center !important; } .bili-mini-login-right-wp .login-sns-content { width: 100% !important; max-width: 100% !important; display: flex !important; justify-content: center !important; gap: 0 !important; margin: 12px 0 0 !important; box-sizing: border-box !important; } .bili-mini-login-right-wp .login-sns-item { flex: 1 1 0 !important; width: auto !important; min-width: 0 !important; max-width: 100% !important; } /* ★ v132.48②:协议说明 —— 面板改回块流后自然落在三方登录按键下方,居中显示 */ .bili-mini-content-wp .login-agreement-wp { position: static !important; left: auto !important; right: auto !important; top: auto !important; bottom: auto !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; transform: none !important; margin: 16px 0 0 !important; padding: 0 !important; text-align: center !important; font-size: 12px !important; line-height: 18px !important; box-sizing: border-box !important; } .bili-mini-content-wp .login-agreement-wp p { font-size: 12px !important; line-height: 18px !important; margin: 0 !important; } /* ⑤ 部分页面顶部的「登录参与社区互动」标题:820 定宽 + absolute → 回文档流居中 */ .bili-mini-content-wp .bili-mini-customer-title { position: static !important; left: auto !important; right: auto !important; top: auto !important; bottom: auto !important; width: 100% !important; max-width: 100% !important; min-width: 0 !important; transform: none !important; margin: 0 0 12px !important; padding: 0 !important; text-align: center !important; box-sizing: border-box !important; } /* ★★★ v132.49:图片查看器缩略图高亮框不跟随当前图 ★★★ 现象:动态 / 空间页展开图片后点任意一张缩略图, 底部那圈高亮框始终停在第一张上。 探针实测(bili_probe_2026-10-02_11-02-04,bili-space-page 已展开那条): .bili-album__watch__track 310x54 / relative / left=25 .bili-album__watch__track__activeitemborder 58x58 / absolute rect left=23 → 相对轨道的 css left 恒为 0 (只叠加自身的 transform translate(-2,-2)),永远钉在第 0 张; .bili-album__watch__track__list 310x54 / translate(0,0) .bili-album__watch__track__item 54x54,六张依次 left = 25 / 83 / 141 / 199 / 257 / 315,第 3 张带 .active(left=141) 其余 9 条未展开的动态里 .active 都落在第 1 项,所以平时看不出来。 → .active 类是跟对的(在第 3 张),错的只是高亮框: 站内没把「当前那张相对轨道的偏移」写回这个 absolute 元素, 窄屏下算出来恒为 0(PC 上轨道足够宽、第 0 张恰好居中,所以不易察觉)。 这里只做尺寸兜底(别被 max-width:100% 压变形), 真正的位置由 JS 逐帧几何重算 —— 见 bindAlbumGuards。 */ .bili-album__watch__track { position: relative !important; } .bili-album__watch__track__activeitemborder { position: absolute !important; max-width: none !important; min-width: 0 !important; margin: 0 !important; transform: translate(-2px, -2px) !important; } /* ★★★ v132.50:图片查看器左右翻页箭头不出来 ★★★ 对照两份探针(bili_probe_2026-10-02_11-15-45 空间页 vs 对照.txt opus 页): opus(正常) content 296x690 / relative;prev 98x690 / absolute / left=32 space(箭头不出来) content 310x723 / relative;prev 102x723 / absolute / left=25 → 两处 DOM 结构、层级、定位方式、宽高比例(约 1/3)完全一致, prev 的 rect.left 也都正好等于 content 的 left,说明元素本身没跑位。 站内没有 .arrow 之类的子节点,prev / next 内部是空的 (探针里 prev 下面直接就是 track),箭头只能由 CSS 伪元素或 background-image 画出来。 窄屏 + 触屏下它不出来的两个典型原因,这里一并兜住: 1) 站内把箭头做成 :hover 才 opacity:1 —— 触屏没有 hover,永远不显示; 2) 站内箭头是伪元素 / 背景图,被窄屏的挤压或 overflow 吃掉。 处理: ① 先无条件把 prev / next 拉回可见(opacity/visibility/display/z-index), 并显式解除 hover 依赖(父级 hover、自身 hover 都写一遍); ② 箭形本身不在 CSS 里硬画(怕和站内 ::before 重出两个), 交给 JS 探测后按需注入 —— 见 bindAlbumGuards。 另修:空间页查看器顶部那条控制栏(收起 / 查看大图 / 向左旋转 / 向右旋转) 探针实测 option 内 span 被压成 17x50、34x50(文字竖排两行,比 32 的 选项本身还高)—— 是站内的 inline 排布 + 宽度不足所致, 改 inline-flex + nowrap + 居中,图标与文字回到一行。 */ .bili-album__watch__content__prev, .bili-album__watch__content__next { display: block !important; visibility: visible !important; opacity: 1 !important; z-index: 5 !important; pointer-events: auto !important; overflow: visible !important; } .bili-album__watch__content:hover .bili-album__watch__content__prev, .bili-album__watch__content:hover .bili-album__watch__content__next, .bili-album__watch:hover .bili-album__watch__content__prev, .bili-album__watch:hover .bili-album__watch__content__next, .bili-album__watch__content__prev:hover, .bili-album__watch__content__next:hover { opacity: 1 !important; visibility: visible !important; } /* 注入的箭头(JS 按需添加 class 后生效) */ .bili-album__watch__content__prev > .bili-album-arrow-fix, .bili-album__watch__content__next > .bili-album-arrow-fix { position: absolute !important; top: 50% !important; left: 50% !important; width: 14px !important; height: 14px !important; margin: -7px 0 0 -7px !important; border-left: 3px solid #fff !important; border-top: 3px solid #fff !important; border-right: none !important; border-bottom: none !important; background: none !important; border-radius: 0 !important; filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.6)) !important; pointer-events: none !important; z-index: 6 !important; } .bili-album__watch__content__prev > .bili-album-arrow-fix { transform: rotate(-45deg) !important; } .bili-album__watch__content__next > .bili-album-arrow-fix { transform: rotate(135deg) !important; } /* ★ v132.51:控制栏「向右旋转」被裁剪 —— 这是 v132.50 自己引入的回归, 这里连同修掉。 回顾:v132.50 为了让文字不竖排,给 option 加了 inline-flex + nowrap, 结果四个按键从 62/78/78/78(正好塞满 296)被撑成 80/104/104/104 = 392, 总宽超过容器(opus 296 / 动态页 310),第 4 个「向右旋转」被挤到 left=313、右边界 417 —— 既超出容器右边界 335,也超出 360 视口,直接被裁。 现在改两条腿走路: ① 内容压紧:字号 12px、图标 12x12、gap 2、左右 padding 4 → 四个按键约 50 + 70*3 = 260,opus 296 与动态页 310 都放得下; ② 容器兜底:control 改 flex + nowrap + overflow-x:auto(滚动条隐藏), 万一将来文字更长也滑得到,绝不再被裁掉。 */ .bili-album__watch__control { display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: center !important; /* v132.66:「收起 / 查看大图 / 向左旋转 / 向右旋转」居中 —— v132.51 只压紧了内容(四个按键约 260 宽)却没管对齐, flex 默认 flex-start → 按键左对齐,容器(opus 296 / 动态页 310) 右侧空出 36 ~ 50px,看起来就是"偏左、左右间距不一致"。 这里补居中:第二行 safe center 是渐进增强 —— 万一将来文字变长、内容宽过容器,safe 会让浏览器退回 flex-start, 配合下面的 overflow-x:auto 仍滑得到,不会像普通 center 那样 把左半截顶到滚动区外面再也滚不回来(flex 居中 + 溢出的经典坑)。 不支持 safe 关键字的旧内核会整条丢弃,退回上一行的 center。 */ justify-content: center !important; justify-content: safe center !important; width: 100% !important; /* 撑满父容器,"容器内居中"才等于"整行居中" */ max-width: 100% !important; margin: 0 auto !important; overflow-x: auto !important; overflow-y: hidden !important; scrollbar-width: none !important; -ms-overflow-style: none !important; box-sizing: border-box !important; } .bili-album__watch__control::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; } .bili-album__watch__control__option { flex: 0 0 auto !important; display: inline-flex !important; flex-direction: row !important; flex-wrap: nowrap !important; align-items: center !important; justify-content: center !important; gap: 2px !important; max-width: none !important; min-width: 0 !important; white-space: nowrap !important; overflow: visible !important; padding-left: 4px !important; padding-right: 4px !important; font-size: 12px !important; line-height: 1.2 !important; box-sizing: border-box !important; } .bili-album__watch__control__option > span { flex: 0 0 auto !important; white-space: nowrap !important; max-width: none !important; font-size: 12px !important; line-height: 1.2 !important; } .bili-album__watch__control__option > svg { flex: 0 0 auto !important; width: 12px !important; height: 12px !important; max-width: none !important; } /* ★ v132.51:缩略图轨道 —— 一行只放「完整」的几张 + 左右翻页箭头可见 ★ 站内轨道的左右翻页箭头其实是 .bili-album__watch__track__prev / __next(12x54 / absolute / z=10) 不是大图两侧那两个 content__prev / __next。 探针实测:轨道宽 296(opus)/ 310(动态页),缩略图 54x54、 步进 58(54 + 4 间距),一行只能放下 5 张整的,第 6 张必然露半截。 这里先把可视窗口对齐到整数张(由 JS 按实测步进算宽度, CSS 只给 max-width / overflow 兜底,不写死数值以免和站内算的不一致), 再把两个翻页箭头钉在轨道内两端并抬高 z-index,确保不被裁、不被缩略图盖住。 */ .bili-album__watch__track__slider { max-width: 100% !important; overflow: hidden !important; box-sizing: border-box !important; /* ★ v132.67:缩略图那一行居中 —— v132.51 把可视窗口收成了「整数张」宽(动态页 286 = 5×58 − 4), 但 slider 是普通块流、默认左对齐,于是 286 贴在 310 的轨道左边, 右侧空出 24px —— 肉眼就是"五个缩略图整体偏左"。 探针实测(居中缩略图.txt): .bili-album__watch__track [310x54] left=25 → 右缘 335 ├─ __slider [286x54] left=25 → 右缘 311 ← 右侧空 24 │ └─ __list translateX(-290),窗口内 5 张:25/83/141/199/257 ├─ __prev [12x54] left=25 ├─ __next [12x54] left=299 这里用 margin:auto 让窗口在轨道内居中(286 → left 37,两侧各留 12)。 ⚠ 不能改 width —— 那是 JS 按实测步进算出来的(albumTrackFitWindow 会以 important 写回),CSS 写死会和它打架;margin 不与它冲突。 ⚠ 窗口一旦居中,翻页箭头与"滚入可视区"的判断原点都必须跟着 从「轨道左缘」换成「窗口左缘」,否则会错开半个余量(12px), 见 JS albumTrackSyncOne 的 winLeft。 */ margin-left: auto !important; margin-right: auto !important; } .bili-album__watch__track__prev, .bili-album__watch__track__next { position: absolute !important; top: 0 !important; z-index: 20 !important; display: block !important; visibility: visible !important; opacity: 1 !important; pointer-events: auto !important; overflow: visible !important; max-width: none !important; min-width: 0 !important; } /* ★★★ v132.52:全屏看图器(PhotoSwipe / .pswp)大图不显示 ★★★ 现象:点「查看大图」后全屏遮罩出来了(黑底、关闭键都在),但图是空的。 探针实测对比(图片正常显示.txt vs 未显示出图片的探针.txt): 正常 .pswp__zoom-wrap tf=matrix(1,0,0,1,0,160) ← Y=(800-480)/2=160 ✓ .pswp__img [360x480] ← 有尺寸 ✓ 异常 .pswp__zoom-wrap tf=matrix(1,0,0,1,80,0) ← X=(360-201)/2≈80 ✓ .pswp__zoom-wrap [0x0] .pswp__img [0x0] ← 尺寸是 0 ✗ → 站内把「贴合视口」的尺寸和居中偏移都算对了(长图应得约 201x800), 唯独把 width / height 写成了 0,于是图渲染不出来。 修法:只补尺寸,绝不碰它已算好的 transform(那才是居中偏移,改了会跑位)。 尺寸由 JS 现算(v132.53 起多图相册未加载时改从居中偏移反推; v132.54 起偏移为 (0,0) 时采信视口满屏),见 pswpImageFit。 这里只放一条无害的兜底:object-fit:contain —— 尺寸正确时本就无变化, 万一 JS 还没来得及跑,也能避免图片被拉伸变形; 它同时是「满屏兜底通路」的安全网:尺寸即使不是原图比例也只留白、不拉伸。 */ /* 灯箱子树一律解除脚本可能施加的通用尺寸限制(防御:将来若有人新增 宽泛的 img 规则,这里的 max-width:none 能兜住"被压成 0"这一类伤害)。 注意只能解除限制,绝不写 width / height —— 尺寸归站内 JS 管。 */ .pswp img, .pswp video, .pswp picture { max-width: none !important; } .pswp__img { object-fit: contain !important; } `; root.appendChild(style); } const PAGE_CLASS_RULES = [ // v132.37:放在最前 —— 音乐页 URL 与其它规则不重叠,但也别被后面抢先 { test: isMusicPage, cls: ['bili-music-page'] }, { test: isSearchPage, cls: ['bili-search-page'] }, { test: isTBiliDynamic, cls: ['bili-dynamic-page'] }, { test: isOpusPage, cls: ['bili-opus-page'] }, { test: isHome, cls: ['bili-home-page'] }, { test: isSpacePage, cls: ['bili-space-page'] }, // 播放列表页:额外带上 bili-video-page,直接复用视频页的全部优化 { test: isListPage, cls: ['bili-video-page', 'bili-list-page'] }, { test: isVideoPage, cls: ['bili-video-page'] }, { test: isBangumiPage, cls: ['bili-bangumi-page'] }, { test: isLiveRoomPage, cls: ['bili-live-room-page'] } ]; const ALL_PAGE_CLASSES = [ 'bili-music-page', 'bili-search-page', 'bili-dynamic-page', 'bili-opus-page', 'bili-home-page', 'bili-space-page', 'bili-video-page', 'bili-list-page', 'bili-bangumi-page', 'bili-live-room-page' ]; function currentPageClasses() { for (let i = 0; i < PAGE_CLASS_RULES.length; i++) { if (PAGE_CLASS_RULES[i].test()) return PAGE_CLASS_RULES[i].cls; } return []; } function syncPageClass() { if (!document.body) return false; const want = currentPageClasses(); const cl = document.body.classList; ALL_PAGE_CLASSES.forEach(c => { if (want.indexOf(c) === -1) cl.remove(c); }); want.forEach(c => cl.add(c)); return true; } function applyPageClassEarly() { if (!syncPageClass()) { const obs = new MutationObserver(() => { if (syncPageClass()) obs.disconnect(); }); obs.observe(document.documentElement, { childList: true, subtree: true }); } // 站内跳转(如 /list/ 与 /video/ 互跳)时同步页面 class window.addEventListener('popstate', syncPageClass); ['pushState', 'replaceState'].forEach(fn => { const orig = history[fn]; if (typeof orig !== 'function') return; history[fn] = function () { const ret = orig.apply(this, arguments); setTimeout(syncPageClass, 0); return ret; }; }); } function fixVideoControls() { if (!isVideoPage() && !isBangumiPage() && !isListPage()) return; if (fixVideoControls._done) return; const playerContainer = document.querySelector('body.bili-video-page .bpx-player-container') || document.querySelector('body.bili-bangumi-page .bpx-player-container'); if (!playerContainer) return; const videoArea = playerContainer.querySelector('.bpx-player-video-area'); if (!videoArea) return; const videoPerch = videoArea.querySelector('.bpx-player-video-perch'); const videoWrap = (videoPerch && videoPerch.querySelector('.bpx-player-video-wrap')) || videoArea.querySelector('.bpx-player-video-wrap'); if (!videoWrap) return; const video = videoWrap.querySelector('video'); if (!video) return; if (videoWrap.parentElement === videoArea && videoArea.firstElementChild === videoWrap) { fixVideoControls._done = true; return; } if (videoPerch && videoWrap.parentElement !== videoArea) { videoArea.insertBefore(videoWrap, videoPerch); } video.playsInline = true; const oldControlWrap = videoArea.querySelector('.bpx-player-control-wrap:not(.new)'); if (!oldControlWrap) return; const controlEntity = oldControlWrap.querySelector('.bpx-player-control-entity'); if (!controlEntity) return; let clickTimer; let hideTimer; const controlWrap = document.createElement('div'); controlWrap.className = 'bpx-player-control-wrap new'; controlWrap.innerHTML = '
'; videoArea.insertBefore(controlWrap, oldControlWrap); controlWrap.appendChild(controlEntity); const controlTop = controlEntity.querySelector('.bpx-player-control-top'); const bottomRight = controlEntity.querySelector('.bpx-player-control-bottom-right'); const isBpxStateShow = () => controlEntity.querySelector('.bpx-player-control-bottom-right > .bpx-state-show'); const isShown = () => playerContainer.getAttribute('ctrl-shown') === 'true'; playerContainer.setAttribute('ctrl-shown', 'false'); const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { const firstNode = mutation.addedNodes[0]; if (firstNode && firstNode.nodeType === Node.ELEMENT_NODE && firstNode.classList.contains('bpx-player-ctrl-web')) { if (video.paused) showControlWrap(); const subtitleBtn = document.querySelector('.bpx-player-ctrl-subtitle'); if (subtitleBtn) { window.addEventListener('click', (event) => { if (!subtitleBtn.contains(event.target)) { subtitleBtn.dispatchEvent(new MouseEvent('mouseleave')); } }); } observer.disconnect(); } }); }); if (bottomRight) observer.observe(bottomRight, { childList: true }); function hideControlWrap(isEnd = false) { if ((!video.paused && !isBpxStateShow()) || isEnd) { playerContainer.setAttribute('ctrl-shown', 'false'); clearTimeout(hideTimer); } else { delayHideTimer(); } } function showControlWrap() { playerContainer.setAttribute('ctrl-shown', 'true'); delayHideTimer(); } function delayHideTimer() { clearTimeout(hideTimer); hideTimer = setTimeout(hideControlWrap, 3000); } function unmute() { video.muted = false; if (video.volume === 0) { const muteIcon = document.querySelector('.bpx-player-ctrl-muted-icon'); if (muteIcon) muteIcon.click(); } } video.addEventListener('ended', () => hideControlWrap(true)); video.addEventListener('play', delayHideTimer); videoWrap.addEventListener('mousemove', (e) => e.stopPropagation()); videoWrap.addEventListener('click', () => { clearTimeout(clickTimer); clickTimer = setTimeout(() => { if (isShown()) hideControlWrap(); else showControlWrap(); if (video.paused) video.play(); else video.pause(); }, 250); }); videoWrap.addEventListener('dblclick', () => { clearTimeout(clickTimer); unmute(); const isPortrait = video.videoHeight / video.videoWidth > 1; if (isPortrait) { const webBtn = document.querySelector('.bpx-player-ctrl-web'); if (webBtn) webBtn.click(); } else if (videoPerch) { videoPerch.dispatchEvent(new MouseEvent('dblclick', { bubbles: true })); } }); controlWrap.addEventListener('mousemove', (e) => e.stopPropagation()); controlWrap.addEventListener('click', (e) => { e.stopPropagation(); delayHideTimer(); }); if (controlTop) controlTop.addEventListener('touchstart', delayHideTimer); fixVideoControls._done = true; } function bindVideoSlideGesture() { if (!isVideoPage() && !isBangumiPage() && !isListPage()) return; if (bindVideoSlideGesture._bound) return; bindVideoSlideGesture._bound = true; let mask = document.getElementById('my-bili-right-mask'); if (!mask) { mask = document.createElement('div'); mask.id = 'my-bili-right-mask'; document.body.appendChild(mask); } const MIN_DISTANCE = 60; const MAX_TIME = 600; // 与 CSS 的 transform 0.26s 对齐,再留 20ms 余量 const ANIM_MS = 280; function getPanel() { if (isListPage()) return document.querySelector('body.bili-list-page .playlist-container--right'); if (isVideoPage()) return document.querySelector('body.bili-video-page .right-container'); if (isBangumiPage()) return document.querySelector('body.bili-bangumi-page .plp-r-wrap'); return null; } function isOpen() { const r = getPanel(); return r && r.classList.contains('show'); } /* v132.62:只保留 decoding=async(让封面解码不阻塞首帧,纯收益)。 ⚠ v132.60 曾一并写 loading="lazy" 与 fetchPriority="low",已撤掉: 站内自己的懒加载 / 占位逻辑会被这两个属性带偏,表现就是 "展开后该显示的地方一片空白"(图片压根没开始加载)。 */ function warmImages(root) { if (!root || !root.querySelectorAll) return; const imgs = root.querySelectorAll('img:not([data-bili-drawer-img])'); for (let i = 0; i < imgs.length && i < 120; i++) { const im = imgs[i]; im.setAttribute('data-bili-drawer-img', '1'); try { im.decoding = 'async'; } catch (e) {} } } /* v132.62:开抽屉的时序 —— 把"预热"和"动画"拆到两帧。 若直接加 .show,首帧就要同时完成:整块首次布局 + 图片解码 + transform 动画,播放中主线程本就被视频占着,必然掉帧。 现在:先强制一次布局并给封面标 decoding=async → 等两帧 → 再起动画, 同时用 holdBackground 把后台任务静默掉。 */ function openPanel() { const r = getPanel(); if (!r || r.classList.contains('show')) return; holdBackground(ANIM_MS + 150); drawerOpeningUntil = Date.now() + 1200; // v132.62:预热窗口内别被自愈打断 document.body.classList.add('bili-drawer-open'); r.classList.add('bili-drawer-warm'); try { void r.offsetHeight; } catch (e) {} // 强制首帧布局,花在动画之前 warmImages(r); // 只标 decoding=async requestAnimationFrame(function () { requestAnimationFrame(function () { r.classList.add('bili-drawer-anim'); r.classList.add('show'); mask.classList.add('show'); setTimeout(function () { r.classList.remove('bili-drawer-anim'); }, ANIM_MS); }); }); } function closePanel() { const r = getPanel(); if (!r || !r.classList.contains('show')) { mask.classList.remove('show'); return; } holdBackground(ANIM_MS + 150); r.classList.add('bili-drawer-anim'); r.classList.remove('show'); mask.classList.remove('show'); setTimeout(function () { r.classList.remove('bili-drawer-anim'); r.classList.remove('bili-drawer-warm'); document.body.classList.remove('bili-drawer-open'); }, ANIM_MS); } mask.addEventListener('click', closePanel); let startX = 0, startY = 0, startTime = 0; // 抽屉里滑动时同样静默后台任务,滑完 320ms 自动恢复 document.addEventListener('scroll', function () { if (isOpen()) holdBackground(320); }, { passive: true, capture: true }); document.addEventListener('touchstart', e => { if (e.touches.length !== 1) return; holdBackground(220); // v132.60:手指落下那一段先腾出主线程 startX = e.touches[0].clientX; startY = e.touches[0].clientY; startTime = Date.now(); }, { passive: true }); document.addEventListener('touchend', e => { if (e.changedTouches.length !== 1) return; const dx = e.changedTouches[0].clientX - startX; const dy = e.changedTouches[0].clientY - startY; const dt = Date.now() - startTime; if (Math.abs(dy) > Math.abs(dx)) return; if (dt > MAX_TIME) return; if (dx < -MIN_DISTANCE && !isOpen()) openPanel(); else if (dx > MIN_DISTANCE && isOpen()) closePanel(); }, { passive: true }); } /* ============================================================ ---- v132.62:抽屉状态自愈 ---- 点抽屉里的视频后站内会换页(或 SPA 重渲染),抽屉节点可能被整个换掉, 而脚本自己挂在 body 上的 bili-drawer-open 与遮罩的 .show 不会跟着走。 残留下来有两个后果: · 播放器动画被 animation-play-state:paused 永久暂停; · 遮罩盖在新页面上,抽屉却不在 —— 看着像"页面蒙了一层灰"。 这里每次 run() 顺手校一次,开销是一次 querySelector。 ---- */ /* ============================================================ ---- v132.63:番剧页「承包榜」JS 兜底 ---- CSS 主选择器 #sponsor_module 是稳定 id,正常情况下一条就够。 这里只防"将来 id 与类名全换成新哈希"的那一天:在番剧页主列里 按文本找含「承包榜」的块,打 .bili-hide-sponsor 交给样式表隐藏。 成本:只在「页面上不存在 #sponsor_module / 未打过标记」时才扫, 且只看 .player-left-components(或 .plp-l)的直接子级 —— 十来个 节点的文本比对,不碰评论区那几千个节点。 ---- */ function bangumiHideSponsor() { if (!isBangumiPage()) return; if (document.getElementById('sponsor_module')) return; // CSS 已命中 if (document.querySelector('[data-bili-sponsor-hidden]')) return; // 已兜过 const root = document.querySelector('.player-left-components') || document.querySelector('.plp-l'); if (!root || !root.children) return; const kids = root.children; for (let i = 0; i < kids.length; i++) { const el = kids[i]; if (!el || el.nodeType !== 1) continue; const txt = (el.textContent || '') + ''; if (txt.indexOf('承包榜') === -1) continue; el.classList.add('bili-hide-sponsor'); el.setAttribute('data-bili-sponsor-hidden', '1'); return; } } /* ---- v132.64:话题页简介默认展开 · JS 兜底 ---- 正常情况样式表(body:has(.topic-detail) .topic-desc 那一组)已经够用; 这里只防「站内把 height / -webkit-line-clamp 写成行内 !important」—— 行内 !important 与样式表 !important 同级,但内联特异性最高,CSS 压不过, 只能逐项 setProperty(..., 'important') 反写。 只碰 .topic-desc 这一小块(两三个节点),不做整页遍历; 不在话题页时连一次 querySelector 都不做。 */ const TOPIC_DESC_UNCLAMP = [ ['display', 'block'], ['height', 'auto'], ['min-height', '0px'], ['max-height', 'none'], ['overflow', 'visible'], ['-webkit-line-clamp', 'unset'], ['line-clamp', 'unset'], ['-webkit-box-orient', 'horizontal'], ['text-overflow', 'clip'], ['white-space', 'normal'] ]; const TOPIC_BTN_HIDE = [ ['display', 'none'], ['visibility', 'hidden'], ['width', '0px'], ['height', '0px'], ['min-width', '0px'], ['min-height', '0px'], ['margin', '0px'], ['padding', '0px'], ['border', 'none'], ['overflow', 'hidden'], ['pointer-events', 'none'] ]; function topicDescAutoExpand() { if (!isTopicPage()) return; let nodes = null; try { nodes = document.querySelectorAll('.topic-desc, .topic-desc__text, .topic-desc__text > span'); } catch (e) { return; } if (!nodes || !nodes.length) return; // 简介还没渲染,下一拍再来 for (let i = 0; i < nodes.length; i++) { const el = nodes[i]; if (!el || !el.style) continue; for (let k = 0; k < TOPIC_DESC_UNCLAMP.length; k++) { try { el.style.setProperty(TOPIC_DESC_UNCLAMP[k][0], TOPIC_DESC_UNCLAMP[k][1], 'important'); } catch (e) { /* 忽略个别浏览器不认识的属性 */ } } } let btns = null; try { btns = document.querySelectorAll('.topic-desc__text-btn, [class*="topic-desc"][class*="btn"]'); } catch (e) { btns = null; } if (!btns) return; for (let j = 0; j < btns.length; j++) { const b = btns[j]; if (!b || !b.style) continue; for (let k = 0; k < TOPIC_BTN_HIDE.length; k++) { try { b.style.setProperty(TOPIC_BTN_HIDE[k][0], TOPIC_BTN_HIDE[k][1], 'important'); } catch (e) { /* 同上 */ } } } } const DRAWER_SHOW_SEL = 'body.bili-video-page .right-container.show, ' + 'body.bili-bangumi-page .plp-r-wrap.show, ' + 'body.bili-video-page .playlist-container--right.show'; const DRAWER_ANY_SEL = 'body.bili-video-page .right-container, ' + 'body.bili-bangumi-page .plp-r-wrap, ' + 'body.bili-video-page .playlist-container--right'; var drawerOpeningUntil = 0; // 开抽屉的两帧预热窗口内不做清理,免得把自己打断 function syncDrawerState() { if (!isVideoPage() && !isBangumiPage() && !isListPage()) return; if (!document.body || !document.body.classList.contains('bili-drawer-open')) return; if (Date.now() < drawerOpeningUntil) return; let shown = true; try { shown = !!document.querySelector(DRAWER_SHOW_SEL); } catch (e) { shown = true; } if (shown) return; document.body.classList.remove('bili-drawer-open'); const m = document.getElementById('my-bili-right-mask'); if (m) m.classList.remove('show'); let nodes = null; try { nodes = document.querySelectorAll(DRAWER_ANY_SEL); } catch (e) { nodes = null; } if (nodes) { for (let i = 0; i < nodes.length; i++) { nodes[i].classList.remove('bili-drawer-anim'); nodes[i].classList.remove('bili-drawer-warm'); } } } function bindFullscreenFix() { if (!isVideoPage() && !isListPage()) return; if (bindFullscreenFix._bound) return; bindFullscreenFix._bound = true; const handleChange = () => { const isFull = !!(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement); if (isFull) { document.body.classList.add('video-fullscreen'); } else { document.body.classList.remove('video-fullscreen'); } }; document.addEventListener('fullscreenchange', handleChange); document.addEventListener('webkitfullscreenchange', handleChange); document.addEventListener('mozfullscreenchange', handleChange); document.addEventListener('MSFullscreenChange', handleChange); document.addEventListener('orientationchange', () => { if (document.fullscreenElement || document.webkitFullscreenElement) { setTimeout(handleChange, 100); } }); } /* ================================================================ 关键词屏蔽(+必含 / -排除)v131.62 官方搜索没有屏蔽词,这里在自建搜索栏的输入语法上补一层: 输入 "+洛克王国 -手游 -世界" · 提交给 B 站的关键词只剩 "洛克王国"(+ 号本身只是标记,会被剥掉) · "手游"、"世界" 存为屏蔽词,结果页把标题/UP主命中它们的卡片整块隐藏 屏蔽词同时写进 URL(?_no=手游|世界)和 sessionStorage: · URL —— 刷新、翻页、切排序后仍然有效,链接分享给别人也带着 · 存储 —— URL 参数万一被站内跳转抹掉时兜底 过滤范围:卡片标题 + UP主 + 底部信息(播放/弹幕/时间那一行), 大小写不敏感,命中任意一个屏蔽词就整块消失。 ⚠ 这一段必须用 JS:CSS 选择器无法按元素的文本内容做匹配 (规范里的 :contains() 从未实装,至今也没有替代方案), 想"标题含某个词就不显示"只能遍历节点判断。 其余部分仍全部走 CSS,这段 JS 只负责打标记 + 插入提示条。 ================================================================ */ const FILTER_PARAM = '_no'; const FILTER_STORE = 'bili_mobile_minus_words'; // 把 " +洛克王国 -手游 -世界 " 拆成 include / exclude 两组 function parseQueryTokens(raw) { const tokens = String(raw || '').split(/\s+/).filter(Boolean); const include = [], exclude = []; tokens.forEach(function (t) { if (t.length > 1 && t.charAt(0) === '-') exclude.push(t.slice(1)); else if (t.length > 1 && t.charAt(0) === '+') include.push(t.slice(1)); else include.push(t); }); return { include: include, exclude: exclude }; } function readBlockedWords() { let raw = ''; try { raw = new URL(location.href).searchParams.get(FILTER_PARAM) || ''; } catch (e) {} if (raw) { try { sessionStorage.setItem(FILTER_STORE, raw); } catch (e) {} } else { try { raw = sessionStorage.getItem(FILTER_STORE) || ''; } catch (e) {} } return raw.split('|').filter(Boolean); } function saveBlockedWords(list) { try { if (list && list.length) sessionStorage.setItem(FILTER_STORE, list.join('|')); else sessionStorage.removeItem(FILTER_STORE); } catch (e) {} } function escapeHTML(s) { return String(s).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); } // 四种搜索结果的卡片根元素(v131.83:原来只写了 .bili-video-card, // 导致屏蔽词只对综合页生效,直播 / 专栏 / 用户页一张都不过滤) const FILTER_CARD_SELECTOR = [ '.bili-video-card', // 综合页 视频(综合页的直播区也是 .bili-live-card) '.bili-live-card', // 直播页 直播间 / 综合页 直播区 '.b-article-card', // 专栏页 '.b-user-info-card' // 用户页 ].join(', '); // 卡片上参与匹配的文本:标题 + UP主 + 信息行 / 摘要 function cardMatchText(card) { const pick = function (sel) { const el = card.querySelector(sel); return el ? (el.textContent || '') : ''; }; const parts = [ // 综合页 · 视频卡 pick('.bili-video-card__info--tit'), pick('.bili-video-card__info--owner'), pick('.bili-video-card__info--bottom'), // 直播卡 pick('.bili-live-card__info--tit'), pick('.bili-live-card__info--uname'), pick('.bili-live-card__info--text'), // 专栏卡(h2 标题 + 摘要 + 信息行) pick('.i_card_title'), pick('.atc-desc'), pick('.atc-info'), // 用户卡(名字 + 粉丝数/简介) pick('.user-content'), pick('.user-name') ].filter(Boolean); // 兜底:万一某个页面的类名又变了,用整卡文本,保证任何卡片都参与匹配 return parts.length ? parts.join(' ') : (card.textContent || ''); } function removeFilterBar() { const bar = document.getElementById('my-bili-filterbar'); if (bar && bar.parentNode) bar.parentNode.removeChild(bar); if (document.body) document.body.classList.remove('has-filter-bar'); renderFilterBar._sig = null; } function renderFilterBar(blocked, hidden) { if (!document.body) return; // 内容没变就不重绘 —— run() 会被 MutationObserver 反复触发, // 每次都重建 innerHTML 会让横向滚动位置被重置、看着一闪一闪 const sig = blocked.join('|') + '#' + hidden; if (renderFilterBar._sig === sig && document.getElementById('my-bili-filterbar')) return; renderFilterBar._sig = sig; let bar = document.getElementById('my-bili-filterbar'); if (!bar) { bar = document.createElement('div'); bar.id = 'my-bili-filterbar'; document.body.appendChild(bar); } document.body.classList.add('has-filter-bar'); bar.innerHTML = '已屏蔽' + blocked.map(function (w) { return '' + escapeHTML(w) + ''; }).join('') + '已过滤 ' + hidden + ' 条' + '清除'; const clear = bar.querySelector('.fb-clear'); if (clear) { clear.addEventListener('click', function () { saveBlockedWords([]); try { const u = new URL(location.href); u.searchParams.delete(FILTER_PARAM); location.href = u.toString(); } catch (e) { location.reload(); } }); } } function applyKeywordFilter() { if (!isSearchPage()) { removeFilterBar(); return; } const blocked = readBlockedWords(); // 先复位:把上一轮(或刚点了"清除")隐藏过的卡片全部放出来,再重算 document.querySelectorAll('[data-blocked="1"]').forEach(function (el) { el.style.removeProperty('display'); el.removeAttribute('data-blocked'); }); if (!blocked.length) { removeFilterBar(); return; } const lowBlocked = blocked.map(function (w) { return w.toLowerCase(); }); let hidden = 0; document.querySelectorAll(FILTER_CARD_SELECTOR).forEach(function (card) { const text = cardMatchText(card).toLowerCase(); let hit = false; for (let i = 0; i < lowBlocked.length; i++) { if (text.indexOf(lowBlocked[i]) !== -1) { hit = true; break; } } if (!hit) return; // 隐藏"网格项"而不是内层卡片 —— 探针实测外层才占栅格单元, // 只藏内层卡片的话外层仍占 168x172,会留下一排空洞 // 综合页 / 直播页:.col_3、.video-list-item.col_3… // 专栏页:.col_6.mb_x40 用户页:.col_6.col_md_4 let item = card; const p = card.parentElement; if (p && /\bcol_/.test(p.className)) item = p; item.setAttribute('data-blocked', '1'); item.style.setProperty('display', 'none', 'important'); hidden++; }); renderFilterBar(blocked, hidden); } // 搜索类型:/live 直播、/article 专栏、/upuser 用户,其余算综合 const SEARCH_TYPES = [ { key: 'all', name: '综合', path: '/all' }, { key: 'live', name: '直播', path: '/live' }, { key: 'article', name: '专栏', path: '/article' }, { key: 'upuser', name: '用户', path: '/upuser' } ]; const MENU_CARET = ''; function currentSearchType() { const p = location.pathname.replace(/\/$/, '') || '/'; let t = 'all'; SEARCH_TYPES.forEach(function (x) { if (p === x.path) t = x.key; }); return t; } // SPA 跳转 / 异步渲染后,重算菜单项的高亮与链接 function syncSearchMenu() { const bar = document.getElementById('my-bili-searchbar'); if (bar && typeof bar._refreshMenu === 'function') bar._refreshMenu(); } function createSearchBar() { if (!isHome() && !isSearchPage() && !isSpacePage()) return; /* v132.26:单例守卫 —— 原来只查「DOM 里有没有 #my-bili-searchbar」, 但 bar 是在 attach() 里才 append 的(body 未就绪时还会 setTimeout 延后)。两次调用连着发生时守卫会双双放行 → body 里出现两个 bar、 两套同 id 的 checkbox(#my-bili-order-cb 等)。 label for="xxx" 只会激活文档中**第一个**同名 id, 于是「点的是后一个 bar 的按钮、展开的却是前一个 bar 的菜单行」—— 表现就是菜单开了但图标不变蓝,再点也收不回去(偶发)。 这里改用一次性标志位,从源头杜绝重复创建。 */ if (createSearchBar._started) return; createSearchBar._started = true; if (document.getElementById('my-bili-searchbar')) return; const onSearch = isSearchPage(); const onSpace = isSpacePage(); const url = new URL(location.href); const currentOrder = url.searchParams.get('order') || ''; const curType = currentSearchType(); // 直播页的排序是「综合排序 / 最新开播」,参数 online / live_time, // 与综合页、专栏页的 pubdate 不是一套,必须分开判断。 const onLive = onSearch && curType === 'live'; const isLatest = onLive ? (currentOrder === 'live_time') : (currentOrder === 'pubdate'); const bar = document.createElement('div'); bar.id = 'my-bili-searchbar'; // 用户搜索页(/upuser)的排序是「默认排序/粉丝数/Lv等级」, // 与综合页的「综合/最新」不是一套参数,这里点了没效果,直接不显示。 const onUpuser = onSearch && curType === 'upuser'; const showLeftBtn = !onSpace && !onUpuser; let leftBtnHTML = ''; if (showLeftBtn) { if (onSearch) { // 左侧按钮:名字改为「排序」,点击逻辑与「切换」一致(展开/收起一行) leftBtnHTML = ` `; } else { leftBtnHTML = `
`; } } // 右侧「切换」按钮 + 两行展开菜单(只在搜索页) let switchBtnHTML = ''; let menuHTML = ''; if (onSearch) { switchBtnHTML = ` `; // 名字统一为「综合 / 最新」,与各页保持一致; // 直播页底层参数仍是官方的 online / live_time(不是 pubdate)。 const orderItems = onLive ? [{ v: '', n: '综合' }, { v: 'live_time', n: '最新' }] : [{ v: '', n: '综合' }, { v: 'pubdate', n: '最新' }]; menuHTML = (onUpuser ? '' : `
${orderItems.map(function (o) { return '' + o.n + ''; }).join('')}
`) + `
${SEARCH_TYPES.map(function (t) { return '' + t.name + ''; }).join('')}
`; } // 两个隐藏 checkbox 必须排在所有被控元素之前(CSS 只有后继兄弟选择器) // 用户页没有「排序」按钮,对应的 checkbox 也不生成 const cbHTML = !onSearch ? '' : (onUpuser ? '' : '') + ''; const placeholderText = onSpace ? '搜索视频、动态' : '搜索视频、番剧、UP主...(用 -词 排除,例:洛克王国 -手游)'; const kw = onSearch ? (url.searchParams.get('keyword') || '') : ''; // 回填时把屏蔽词拼回原文("洛克王国 -手游 -世界"),用户接着编辑不会丢 const blockedNow = onSearch ? readBlockedWords() : []; const fullKw = blockedNow.length ? kw + ' ' + blockedNow.map(function (w) { return '-' + w; }).join(' ') : kw; const escapedKw = fullKw.replace(/"/g, '"').replace(//g, '>'); bar.innerHTML = ` ${cbHTML} ${leftBtnHTML}
${switchBtnHTML} ${menuHTML} `; const attach = () => { if (!document.body) { setTimeout(attach, 50); return; } // 兜底:万一历史上已经建出过多个 bar,只保留当前这一个 Array.prototype.slice.call(document.querySelectorAll('#my-bili-searchbar')) .forEach(function (el) { if (el !== bar && el.parentNode) el.parentNode.removeChild(el); }); document.body.appendChild(bar); // ⚠ 必须用 id 取!bar 里第一个 input 是隐藏 checkbox #my-bili-order-cb, // checkbox 的默认 value 就是字符串 "on",裸 querySelector('input') 会抓到它, // 导致搜索关键词变成 "on"(v131.69 修复)。 const input = bar.querySelector('#my-bili-search-input'); const btn = bar.querySelector('.my-search-btn'); const doSearch = () => { // 拆出 +必含 / -排除,只把正经关键词交给 B 站 const parsed = parseQueryTokens(input.value.trim()); const keyword = parsed.include.join(' '); if (!keyword) return; saveBlockedWords(parsed.exclude); if (onSpace) { const mid = location.pathname.split('/').filter(Boolean)[0] || ''; if (!mid || !/^\d+$/.test(mid)) return; location.href = `https://space.bilibili.com/${mid}/search?keyword=${encodeURIComponent(keyword)}`; return; } // v131.82:原来硬编码跳 /all,导致在直播 / 专栏 / 用户页搜索 // 都会跳回综合页。改成按当前页路径构造: // 综合 /all、直播 /live、专栏 /article、用户 /upuser // 并带上各页需要的官方参数(vt / from_source / search_type)。 const ct = currentSearchType(); let base = 'https://search.bilibili.com/all'; SEARCH_TYPES.forEach(function (t) { if (t.key === ct) base = 'https://search.bilibili.com' + t.path; }); const next = new URL(base); next.searchParams.set('keyword', keyword); if (ct !== 'all') { next.searchParams.set('vt', '63088886'); next.searchParams.set('from_source', 'web_search'); } // 直播页:主播区已隐藏,只搜直播间,与页面「共找到 N 个结果」一致 if (ct === 'live') next.searchParams.set('search_type', 'live_room'); // 屏蔽词带在 URL 上:刷新 / 翻页 / 切排序都还在 if (parsed.exclude.length) next.searchParams.set(FILTER_PARAM, parsed.exclude.join('|')); // 排序保持当前状态:直播页用 live_time,其余用 pubdate if (isLatest) next.searchParams.set('order', ct === 'live' ? 'live_time' : 'pubdate'); location.href = next.toString(); }; input.addEventListener('keydown', e => { if (e.key === 'Enter' || e.keyCode === 13) { e.preventDefault(); doSearch(); } }); btn.addEventListener('click', doSearch); // ---- 菜单项链接:随输入框实时重算,保证切过去用的是当前关键词 ---- const currentKeyword = () => { const parsed = parseQueryTokens(input.value.trim()); return { kw: parsed.include.join(' '), exclude: parsed.exclude }; }; // 切类型:综合走 /all,直播/专栏/用户照抄官方带的 vt 与 from_source const buildSwitchHref = (type) => { const c = currentKeyword(); let base = 'https://search.bilibili.com/all'; SEARCH_TYPES.forEach(function (t) { if (t.key === type) base = 'https://search.bilibili.com' + t.path; }); const next = new URL(base); const kw = c.kw || url.searchParams.get('keyword') || ''; if (kw) next.searchParams.set('keyword', kw); if (type !== 'all') { next.searchParams.set('vt', '63088886'); next.searchParams.set('from_source', 'web_search'); } if (c.exclude.length) next.searchParams.set(FILTER_PARAM, c.exclude.join('|')); return next.toString(); }; // 排序:留在当前搜索类型上,只动 order 参数(不会莫名跳回综合页) const buildOrderHref = (order) => { const c = currentKeyword(); const next = new URL(location.href); const kw = c.kw || next.searchParams.get('keyword') || ''; if (kw) next.searchParams.set('keyword', kw); else next.searchParams.delete('keyword'); if (c.exclude.length) next.searchParams.set(FILTER_PARAM, c.exclude.join('|')); else next.searchParams.delete(FILTER_PARAM); if (order) next.searchParams.set('order', order); else next.searchParams.delete('order'); // 直播页:主播区已隐藏,排序只针对直播间, // 补上官方「直播间」用的 search_type=live_room, // 这样结果数与页面上「共找到 N 个结果」一致。 if (currentSearchType() === 'live') next.searchParams.set('search_type', 'live_room'); return next.toString(); }; const refreshMenuHrefs = () => { const ct = currentSearchType(); const ordNow = new URL(location.href).searchParams.get('order') || ''; const latestNow = ct === 'live' ? (ordNow === 'live_time') : (ordNow === 'pubdate'); bar.querySelectorAll('.my-bili-menu-item').forEach(function (a) { if (a.dataset.type) { a.href = buildSwitchHref(a.dataset.type); a.classList.toggle('is-current', a.dataset.type === ct); } else if (a.hasAttribute('data-order')) { a.href = buildOrderHref(a.dataset.order || ''); a.classList.toggle('is-current', !!a.dataset.order === latestNow); } }); }; // 供 syncSearchMenu 在 SPA 跳转后复用 bar._refreshMenu = refreshMenuHrefs; refreshMenuHrefs(); input.addEventListener('input', refreshMenuHrefs); // ---- 互斥 + 点击外部收起(展开/收起本身是纯 CSS,这里只管状态互斥) ---- const allCb = () => bar.querySelectorAll('.my-bili-menu-cb'); const clearMarks = () => { bar.querySelectorAll('.my-bili-menu-row').forEach(function (r) { r.classList.remove('is-open'); }); bar.querySelectorAll('#my-bili-order-btn, #my-bili-switch-btn') .forEach(function (b) { b.classList.remove('is-open'); }); }; const closeAll = () => { allCb().forEach(function (o) { o.checked = false; }); clearMarks(); }; /* v132.26:展开/收起改由 JS 显式控制,不再依赖 label[for] → checkbox 的浏览器隐式转发。 · preventDefault 掉 label 的默认行为,避免"我们切一次 + 浏览器再切一次"的双重切换; · 状态与 .is-open 标记一起写,样式表两条路都能生效, 底色 / 蓝色图标 / 菜单行永远同步,不会再出现 "菜单开了但图标没变蓝"、也不会出现"点不回去"。 */ const setOpen = function (cb, open) { allCb().forEach(function (o) { o.checked = false; }); clearMarks(); cb.checked = !!open; if (!open || !cb.id) return; const base = cb.id.replace(/-cb$/, ''); const row = bar.querySelector('#' + base + '-row'); const btn = bar.querySelector('#' + base + '-btn'); if (row) row.classList.add('is-open'); if (btn) btn.classList.add('is-open'); }; bar.querySelectorAll('label[for]').forEach(function (lb) { const toggle = function (e) { const cb = bar.querySelector('#' + lb.getAttribute('for')); if (!cb) return; if (e && typeof e.preventDefault === 'function') e.preventDefault(); setOpen(cb, !cb.checked); }; lb.addEventListener('click', toggle); // role="button" tabindex="0":键盘 Enter / 空格同样可用 lb.addEventListener('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') toggle(e); }); }); bar.addEventListener('change', function (e) { const t = e.target; if (!t.classList || !t.classList.contains('my-bili-menu-cb')) return; if (t.checked) { allCb().forEach(function (o) { if (o !== t) o.checked = false; }); } }); document.addEventListener('click', function (e) { if (bar.contains(e.target)) return; closeAll(); }); // 菜单项点完就跳走了,顺手收起,避免 SPA 返回时菜单还开着 bar.querySelectorAll('.my-bili-menu-item').forEach(function (a) { a.addEventListener('click', function () { setTimeout(closeAll, 0); }); }); // 兜底:B站是 SPA,站内跳转后如果 bar 还在,重置菜单状态 window.addEventListener('popstate', closeAll); }; attach(); } /* ============================================================ ---- v132.29:面板展开标记(供上面那段 CSS 用)---- v132.28 只靠 CSS 抬 z-index 有个绕不开的死结:控制栏里每个 .bpx-player-ctrl-btn 都是 position:relative + z-index:2, 各自一口"井";把所有面板按钮都抬到同一个极大值,它们之间 又变成"同层级、比 DOM 顺序",排在后面的照样盖住前面的面板。 实测表现就是"只修好了一部分"(只有最后那个面板是干净的)。 CSS 单靠 :has(.bpx-state-show) 也能做,但 Via 的 WebView 未必 支持 :has,所以这里用 JS 打两个标记,结果确定、与浏览器无关: · body.bili-player-panel-open —— 当前有面板展开 · .bili-panel-host —— 展开的那个按钮 / 弹幕设置宿主 再配合 CSS:只抬 host,其余按键压到 z-index:0。 ---- */ /* ============================================================ ---- v132.60:主线程"静默窗口" holdBackground ---- 播放中打开侧边栏会卡的直接原因,不是抽屉动画本身(它只是一次 transform 合成),而是同一时间还有两个常驻任务在跟视频解码抢主线程: · 面板 z-index 轮询:250ms 一次 8 个面板的 getComputedStyle + getBoundingClientRect,播放中样式一直在脏,每次都是完整的 样式重算 + 布局; · DOM 守卫:播放器每帧改 style / 插弹幕都会把它唤醒。 暂停后这两项都安静下来(样式不再变、播放器不动),所以"暂停就不卡"。 这里给所有后台任务开一个统一的静默窗口:抽屉动画期间 + 收尾期 一律跳过,动画结束自动恢复。 刻意用 var / function 声明:下面的 tick() 在初始化时会立刻跑一次, 用 const 会撞 TDZ。 ---- */ var bgHoldUntil = 0; function holdBackground(ms) { const until = Date.now() + (ms || 0); if (until > bgHoldUntil) bgHoldUntil = until; } function bgHeld() { return Date.now() < bgHoldUntil; } const PLAYER_PANEL_SEL = [ '.bpx-player-ctrl-quality-menu-wrap', '.bpx-player-ctrl-playbackrate-menu', '.bpx-player-ctrl-setting-box', '.bpx-player-ctrl-subtitle-box', '.bpx-player-ctrl-eplist', '.bpx-player-dm-setting-wrap', '.bpx-player-ctrl-volume-box', '.bpx-player-ctrl-more-box' /* ⚠ 故意不写 [class*="-box"] 这种泛化选择器: 站内可能存在常态可见的 xxx-box(例如音量条容器), 一旦被当成"面板已展开",body 就会一直挂着 panel-open, 其余按键被永久压到 z-index:0。宁可漏、不可误判。v132.30 新增:全站隐藏「稍后再看」按键 —— 原规则只覆盖了首页/搜索页/空间页,视频播放页、列表播放页、番剧页、动态页都没管到;站内至少 3 套实现(.bili-watch-later--wrap 系 / .watch-later-video.van-watchlater 系 / .bili-card-watch-later),现精确列名 + 泛化 [class*=watch-later] 全站兜底(:not 排除页面级容器),并加 JS 二次把关(关键词 + 宽高均 ≤120px 才隐藏,绝不误伤整页)。v132.31 补充:动态卡片的稍后再看类名是 .bili-dyn-card-video__mark(探针实测 28x28、封面右上角),完全不含 watch-later 字样,故 v132.30 漏掉;这正是剩下的 3 处 —— 主动态页 / 空间页动态 / 空间页搜索里的「投稿了视频」卡片。连同 __mark__tip 一并隐藏;刻意不动同层的 __cover-shadow(那是时长 13:17)。v132.32 新增:话题页「热门/最新」行 —— 外边距本就是 8px,问题在这行自己高 48px 而文字仅约 22px,上下各约 13px 空白把文字顶在中间,视觉上是 21px;现让该行收缩到文字高度(height auto + 上下 padding 0 + line-height normal),外边距仍各 8px,文字距上下卡片各 8px;两个 tab 间距 24px 用 gap 原样保留。v132.33 修正:同名的后写规则里 min-height:48px 把外壳撑回 48(特异性相同、后写者胜),导致 tab 缩到 22 但上下仍是 21px;改为 0,并给 .switch-tab__tab 补 min-height:0 + line-height:normal。v132.34 新增:UP主空间→「音频」页(route_uploadAudio)改 3 列手机布局 —— 原卡片仅 44 宽、间距 16,三张只占 8..172 右半屏全空;现 .audio-list 改 grid 3 列 gap 8(列宽 (344-16)/3≈109.33),卡片套用与视频卡一致的白底+8px圆角+阴影,封面保持 1:1(padding-top:100%),标题/日期居中,卡片间距与距屏幕边缘均 8px。v132.35 新增:音频页底部分页条——原 .audio-footer 是 PC 的 pt/pb 各 60px 大留白;改 pt:16 / pb:0 并归零分页器自身的 margin/padding,使按键距最后一张卡片 16px、距屏幕底 16px(upload-content 底部已有 16px);另给 --btns 加 overflow-x:auto + 按钮 flex-shrink:0,按钮总宽 466>360 时可横滑、不撑出横向滚动条。v132.36 修正(上一轮漏写文件):隐藏 .audio-header(「TA的音频」大标题)并把 .audio-body 的 margin-top 由 30 归零 → 卡片顶 154,距 .side-nav 按键底 146 正好 8px;⚠ sidenav 自带 pb:8 就是间距,此处 body 的 mt 必须取 0 否则变 16px。v132.37 新增:适配音乐播放器页(/audio/auXXXX、/music/auXXXX),新增 isMusicPage 判定与 body.bili-music-page(此前脚本对该页完全无适配)—— 容器链拉满宽、左右 8px、两列改上下堆叠;隐藏 PC 头部/侧栏·浏览相似歌曲/音乐人的热门歌曲/分享收藏评论/举报浮层/收藏弹层/封面;底部播放器 #music-container 只保留 上一首/播放暂停/下一首 + 时间 + 进度条,隐藏封面、歌名歌手(与正文重复且窄屏排不下)、右侧收藏·音量·顺序·列表与播放列表浮层。v132.38 调整:全部文字与「立即播放」居中(song-gap/share-board 仅 294 宽导致按键中心 155 偏左 25px,现强制铺满);顶部文字距屏幕顶 36px(contain-div 改 padding-top 36 + margin 归零 + 两列 mt/mb 5 归零,避免 margin 折叠致数值漂移);底部统一间距 g=8px —— 上一首距左、按钮间、left↔center、时间/进度条距右全部 8px,按钮左右内边距归零使图标视觉间距严格等于 g。v132.39 修正:正文与「立即播放」仍偏左 25px —— v132.38 只给 .song-padding 写了 width:100% 却没清 padding,它自带左右各 25px 内边距(实测 song-padding 344 → song-gap 仅 294),里面所有文字与 share-board 都按 294 居中(中心 155 vs 屏幕中心 180);现补 padding:0,「简介/施基」因在 col-6 下无此层 padding 所以本来就是正的。v132.40 修正:「歌曲分类:有声节目、其他」与歌词(暂无歌词)仍左对齐 —— 这两个 span 起点 = 容器左边(应在 ~92),说明 .song-type 是 flex 容器,flex 下 text-align 对 flex item 无效;现显式 display:block + width:100% + text-align:center,并补 justify-content:center 兜底(防站内 !important 锁 flex);子级 span 改 inline 保证同行内联居中;歌词容器放开 overflow/固定高。v132.41 新增:①「歌手 hanser」行居中(与歌曲分类同因 —— 子级起点=容器左边,flex 下 text-align 无效,改 block+inline 双路径);②隐藏失效的「展开/收起」按键 .song-lrcFold/.song-lrcUnfold(歌词已全量铺开,开关无作用对象);③「立即播放」距上方文字由 143px 削减到 36px —— 多出的 131px 来自 .song-gap 固定 230 高(PC 版式,手机端内容仅 99 高),改 height:auto 后底边落到 237,share-board margin-top 由 12 改 36 → 立即播放顶 273 = 237+36 */ ].join(', '); /* v132.60:面板可见性签名缓存 —— WeakMap 存 el → {sig, vis}, sig 取 className + 行内 style。播放中这两样绝大多数时刻根本没变, 签名一致就沿用上次结果,一次 getComputedStyle / getBoundingClientRect 都不做(这俩会强制样式重算 + 强制布局,是本函数唯一昂贵的地方)。 只在交互后的活跃窗口里才真算。 */ let panelZCache = null; try { panelZCache = new WeakMap(); } catch (e) { panelZCache = null; } function syncPlayerPanelZ() { if (!isVideoPage() && !isBangumiPage() && !isListPage()) return; if (!document.body || !document.querySelectorAll) return; let open = false; let host = null; const list = document.querySelectorAll(PLAYER_PANEL_SEL); for (let i = 0; i < list.length; i++) { const el = list[i]; let vis = false; try { const sig = (el.className || '') + '|' + (el.getAttribute && el.getAttribute('style') || ''); const c = panelZCache && panelZCache.get ? panelZCache.get(el) : null; if (c && c.sig === sig) { vis = c.vis; // v132.60:签名未变 → 零成本沿用 } else { const cs = window.getComputedStyle(el); const r = el.getBoundingClientRect(); // 收起态站内是 0x0 / display:none,只看真实可见的 vis = cs.display !== 'none' && cs.visibility !== 'hidden' && parseFloat(cs.opacity || '1') > 0.01 && r.width > 0 && r.height > 0; if (panelZCache && panelZCache.set) panelZCache.set(el, { sig: sig, vis: vis }); } } catch (e) { vis = false; } if (!vis) continue; open = true; if (!host && el.closest) { host = el.closest('.bpx-player-ctrl-btn') || el.closest('.bpx-player-dm-setting'); } } document.body.classList.toggle('bili-player-panel-open', open); // 先清旧标记,再给当前 host 打上 const marks = document.querySelectorAll('.bili-panel-host'); for (let i = 0; i < marks.length; i++) marks[i].classList.remove('bili-panel-host'); if (open && host) host.classList.add('bili-panel-host'); } function bindPlayerPanelZ() { if (bindPlayerPanelZ._done) return; bindPlayerPanelZ._done = true; // v132.60:250ms 定频轮询 → 自适应间隔。 // 静默期(没人碰播放器)拉到 1500ms;刚点过才是跟手的 200ms。 let lastActive = 0; let timer = null; const schedule = function (ms) { if (timer) clearTimeout(timer); timer = setTimeout(function () { timer = null; tick(); }, ms); }; const tick = function () { if (bgHeld()) { schedule(400); return; } // 抽屉动画期间完全让路 syncPlayerPanelZ(); schedule(Date.now() - lastActive < 2000 ? 200 : 1500); }; // 点按键后立刻补两拍,不等轮询,避免"刚点开那一瞬间还是被压住" document.addEventListener('click', function () { lastActive = Date.now(); if (bgHeld()) return; // 抽屉动画期间不抢 setTimeout(function () { syncPlayerPanelZ(); }, 0); setTimeout(function () { syncPlayerPanelZ(); }, 120); schedule(400); }, true); document.addEventListener('touchstart', function () { lastActive = Date.now(); if (bgHeld() || timer) return; schedule(0); }, { passive: true }); tick(); } /* ============================================================ ---- v132.30:稍后再看按键 · JS 兜底 ---- 上面那条泛化 CSS 选择器 [class*="watchlater"] 有个隐患: 万一站内某个**页面级容器**的类名也含这个词,整页都会被藏掉。 CSS 里已用 :not() 排掉了 body/html/#app/main/section/article/aside, 但仍有漏网可能;而且动态页等页面的稍后再看可能是别的类名, 探针里没抓到。 所以这里再做一层 JS 兜底,判据是「关键词 + 小尺寸」: ① class / aria-label / title 含 watch-later、watchlater、稍后再看 ② 且渲染宽高都不超过 120px(真正的按键就这么大; 页面容器、列表容器都远大于它) 两条同时满足才打 .bili-hide-watch-later。 这样即使类名花样再多也不会误伤 —— 宁可漏掉一个,也不能整页消失。 ---- */ const WATCH_LATER_KEYS = ['watch-later', 'watchlater', '稍后再看']; /* v132.61:这个函数是全脚本最贵的一段 —— querySelectorAll('body *') 把整页节点全收一遍(B站视频页含弹幕常有几千个),命中者还要 getBoundingClientRect(强制布局)。它挂在 run() 里,而 run() 又被 那个观察整棵 body 子树的 MutationObserver 每 200ms 叫一次: 播放中弹幕不停插节点 → run() 不停跑 → 整页反复被扫 → 卡。 暂停后弹幕不动、没有 mutation,run() 也就停了 —— 这就是 "暂停就不卡"的完整解释。 加两道闸:① 3 秒节流;② 节点总数没变(页面没长新东西)直接返回。 */ let watchLaterAt = 0; let watchLaterStamp = -1; function hideWatchLaterButtons(force) { if (!document.querySelectorAll) return; if (!force && typeof bgHeld === 'function' && bgHeld()) return; // 抽屉动画期绝不扫 const now = Date.now(); const total = document.getElementsByTagName('*').length; if (!force) { if (now - watchLaterAt < 3000) return; // ① 节流 if (total === watchLaterStamp) { // ② 没长新节点 → 零成本 watchLaterAt = now; return; } } watchLaterAt = now; watchLaterStamp = total; const all = document.querySelectorAll('body *'); for (let i = 0; i < all.length; i++) { const el = all[i]; if (!el.classList) continue; if (el.classList.contains('bili-hide-watch-later')) continue; /* v132.61:按"从便宜到昂贵"排序,命中即停 —— ① 只看 className(已经在内存里,最便宜); ② 未命中才读 aria-label / title(属性访问); ③ 仍未命中才读 textContent(会遍历子树,最贵)。 原来不管三七二十一先读两个属性再拼字符串,几千个节点 白白付了两遍属性访问。 */ let hit = false; const cls = el.className || ''; const clsStr = typeof cls === 'string' ? cls : String(cls || ''); const low = clsStr.toLowerCase(); for (let k = 0; k < WATCH_LATER_KEYS.length; k++) { if (low.indexOf(WATCH_LATER_KEYS[k]) !== -1) { hit = true; break; } } if (!hit && el.getAttribute) { let aria = ''; try { aria = ((el.getAttribute('aria-label') || '') + ' ' + (el.getAttribute('title') || '')).toLowerCase(); } catch (e) { aria = ''; } if (aria) { for (let k = 0; k < WATCH_LATER_KEYS.length; k++) { if (aria.indexOf(WATCH_LATER_KEYS[k]) !== -1) { hit = true; break; } } } } if (!hit) { // 文本恰好是「稍后再看」的小元素(如图标上的 tip) const txt = ((el.textContent || '') + '').trim(); if (txt !== '稍后再看') continue; } // 尺寸把关:只藏小按键,绝不碰容器 let w = 0, h = 0; try { const r = el.getBoundingClientRect(); w = r.width; h = r.height; } catch (e) { continue; } if (w > 120 || h > 120) continue; if (w === 0 && h === 0) continue; // 未渲染的不管,避免误判 el.classList.add('bili-hide-watch-later'); } } function fixSpaceNavTabs() { if (!isSpacePage()) return; const allowedTabs = ['主页', '动态', '投稿', '合集和系列']; document.querySelectorAll('body.bili-space-page .nav-tab > a.nav-tab__item').forEach(el => { const text = (el.textContent || '').trim(); el.style.removeProperty('display'); el.style.removeProperty('visibility'); const isAllowed = allowedTabs.some(t => text.indexOf(t) !== -1); if (!isAllowed) { el.style.setProperty('display', 'none', 'important'); el.style.setProperty('visibility', 'hidden', 'important'); } }); } /* ============================================================ ---- v132.11:搜索页骨架屏两列网格 + 分页回顶 ---- 【骨架屏】两列网格的 CSS 只认 .video-list(.row) 等固定类名, 而骨架屏阶段站内渲染的列表容器不在这个名单里(类名无从预知), 于是走原生布局:flex + 居中 + PC 定宽 → 看起来"窄而且居中", 真实卡片到位后才被 grid 成两列 → 宽度突变、骨架屏"放大"。 容器类名猜不到,只能按结构认人: 从骨架屏往上找,**第一个"直接子元素里含 ≥2 个骨架屏"的层** 就是列表层(item 层只含 1 个,再往上的大容器虽然也含多个, 但我们从下往上找、命中就停,所以拿到的正是列表层)。 给它打上 .bili-skel-grid,剩下的交给样式表。 【分页回顶】搜索页换页是站内 SPA 局部更新,浏览器保持滚动位置, 于是点完「下一页」还停在页面最底部。CSS 管不了滚动, 这里在捕获阶段监听分页区域的点击,触发后多拍把滚动条归零 (内容异步渲染,单次 scrollTo 会被随后的 DOM 变动顶回去)。 ---- */ const SKEL_GRID_CLS = 'bili-skel-grid'; const SKEL_SEL = '.bili-video-card__skeleton, .bili-live-card__skeleton'; function skelHas(el) { if (!el) return false; try { if (el.matches && el.matches(SKEL_SEL)) return true; } catch (e) {} try { return !!el.querySelector(SKEL_SEL); } catch (e) { return false; } } function markSkeletonGrid() { if (!isSearchPage()) return false; const list = document.querySelectorAll(SKEL_SEL); if (!list.length) return false; Array.prototype.forEach.call(list, function (sk) { let p = sk.parentElement; // 最多往上 6 层,别一路摸到 #app(把它 grid 化会整页乱套) for (let i = 0; p && i < 6; i++) { if (p === document.body || p === document.documentElement) break; let cnt = 0; for (let c = 0; c < p.children.length; c++) { if (skelHas(p.children[c])) cnt++; } if (cnt >= 2) { // v132.12:若这一层已经被 grid 化(如 .video-list.row // 本就在两列网格名单里),就不再叠标记,避免两套 // grid 规则打架。真正要补的是**父级被定宽**的情况。 let already = false; try { already = window.getComputedStyle(p).display.indexOf('grid') === 0; } catch (e) {} if (!already && p.classList && !p.classList.contains(SKEL_GRID_CLS)) { p.classList.add(SKEL_GRID_CLS); } break; // 命中即停:拿到的是最深的列表层 } p = p.parentElement; } }); return true; } /* v132.13:换页回顶只在**很短的窗口**内生效,且用户一滑动就立刻交还控制权。 上一版的问题是窗口开到了 1600ms:站内换页是异步的,中间还会 「先回到页1顶部 → 再跳到页2顶部」,于是这几拍归零会一直持续到 用户已经开始浏览之后 —— 表现为"滑下去几秒又被拽回顶部"。 修法两条: ① 窗口从 1600ms 收到 700ms(够覆盖站内换页渲染,又不会 追着用户跑); ② 一旦检测到滚动位置离开顶部超过阈值(用户自己在滑), 立即作废这一轮所有未执行的归零、并摘掉监听, 把页面完完整整交还给用户。 */ let scrollTopToken = 0; function scrollPageToTop() { const token = ++scrollTopToken; const timers = []; let onScroll = null; const cleanup = function () { timers.forEach(function (id) { clearTimeout(id); }); if (onScroll) { window.removeEventListener('scroll', onScroll, true); onScroll = null; } }; const go = function () { if (token !== scrollTopToken) return; // 已被新一次点击接管 try { window.scrollTo(0, 0); } catch (e) {} if (document.documentElement) document.documentElement.scrollTop = 0; if (document.body) document.body.scrollTop = 0; }; go(); [60, 160, 320, 500, 700].forEach(function (ms) { timers.push(setTimeout(function () { go(); if (ms >= 700) cleanup(); }, ms)); }); // 用户自己往下滑动 → 判定他已经接管,立刻停止后续归零 onScroll = function () { if (token !== scrollTopToken) { cleanup(); return; } const y = window.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || 0; if (y > 40) { scrollTopToken++; // 作废这一轮还没跑的 go() cleanup(); } }; window.addEventListener('scroll', onScroll, true); } const SEARCH_PAGING_SEL = [ '.vui_pagenation', '.vui_pagenation--btns', '[class*="pagenation"]', '[class*="pagination"]', '.pagination', '.page-wrap' ].join(', '); function bindSearchPagingScrollTop() { if (!isSearchPage() || bindSearchPagingScrollTop._done) return; bindSearchPagingScrollTop._done = true; document.addEventListener('click', function (e) { const t = e.target; if (!t || !t.closest) return; let hit = false; try { hit = !!t.closest(SEARCH_PAGING_SEL); } catch (e) { return; } if (!hit) return; scrollPageToTop(); }, true); } function run() { if (!document.body) return; // 兜底:直播页 / 用户页偶发首屏拿不到自建搜索栏,这里补建并同步菜单状态 createSearchBar(); syncSearchMenu(); // v132.30:稍后再看按键(懒加载卡片会出现在任意页面,每次 run 都扫一遍) hideWatchLaterButtons(); if (isTBiliDynamic()) { // 动态页 } else if (isOpusPage()) { // opus 详情页 } else if (isHome()) { // 首页 } else if (isSearchPage()) { // 搜索页:按 -排除词 过滤卡片(结果懒加载,run() 会被 MutationObserver 反复触发) applyKeywordFilter(); markSkeletonGrid(); // v132.11:骨架屏两列网格(容器类名未知,按结构认) bindSearchPagingScrollTop(); // v132.11:点分页后回到页首 } else if (isSpacePage()) { fixSpaceNavTabs(); } else if (isVideoPage() || isListPage()) { fixVideoControls(); bindVideoSlideGesture(); bindFullscreenFix(); syncDrawerState(); // v132.62:抽屉残留状态自愈 } else if (isBangumiPage()) { fixVideoControls(); bindVideoSlideGesture(); syncDrawerState(); // v132.62:抽屉残留状态自愈 bangumiHideSponsor(); // v132.63:隐藏「承包榜」· JS 兜底 } else if (isTopicPage()) { topicDescAutoExpand(); // v132.64:简介默认展开 + 隐藏展开/收起键 · JS 兜底 } else if (isLiveRoomPage()) { liveDropWebFullscreenClass(); // 站内补的网页全屏,每次 run 顺手清一次 syncLiveFullscreen(); applyLivePlayerSize(); bindLivePlayerSizeWatch(); bindLiveDblclickFullscreen(); bindLiveWebFullscreenGuard(); bindLiveBackExitFullscreen(); liveTrimController(); // 控制栏只留时间 + 清晰度 applyLiveControllerGeom(); // v132.05:按键行偏移补偿 + 自建遮罩开关 liveMarkContextMenu(); // 给长按调试面板打标记(隐藏在 CSS) bindLiveControllerWatch(); bindLiveLongPressGuard(); bindLiveChatScroll(); // 聊天区滚动(v131.98) bindLiveControllerCompLoop(); // v132.05:按键行偏移补偿 + 遮罩开关轮询 markSkeletonGrid(); // v132.11:骨架屏一出现就定成两列,不等下一拍 bindPlayerPanelZ(); // v132.29:播放页面板展开标记(压住控制栏按键) hideWatchLaterButtons(); // v132.30:稍后再看按键 · JS 兜底(按关键词 + 小尺寸判定) } } /* ===== 直播间:全屏状态检测(v131.90 重写)===== CSS 自己认不出"当前是不是全屏",只能由 JS 判断后给 body 打 bili-live-fs, 再由 CSS 里那组更高特异性的 .bili-live-fs 规则撤掉全部尺寸约束。 ⚠⚠ 旧判定的致命坑(v131.88 / 131.89 都没修好,现象"没变化"就出在这): 旧版第 4 条「尺寸启发式:播放器贴满视口四边就算全屏」。 而退出原生全屏的那一刻,站内播放器会自己给 #fullscreen-container 写上一套 position:fixed + 铺满视口的**行内样式**(所谓"网页全屏", 仍带浏览器地址栏)。于是: 尺寸启发式命中 -> 判成"全屏" -> applyLivePlayerSize 走全屏分支, 把整条链写成 100%/100% -> 它真的铺满视口 -> 用户看到的就是"带地址栏的全屏"。 下一拍双击:不在原生全屏 -> 又 requestFullscreen 进原生全屏。 ⇒ 两种全屏来回跳,永远退不回普通态。返回键同理: 浏览器用第一次返回退出原生全屏,站内立刻补上假全屏, "看起来就是退到了带地址栏的全屏",再按一次才真的离开页面。 新判定:只认**原生**全屏,其余一切(class、行内尺寸、fixed 定位)都不算。 1) document.fullscreenElement / webkit 前缀 2) #fullscreen-container 命中 :fullscreen / :-webkit-full-screen(等价兜底) 站内自造的假全屏一律判"非全屏",由普通态那套 16:9 排版接管, 并立刻被 applyLivePlayerSize 写回正常尺寸 —— 即"双击 = 退出全屏"。 唯一例外:iOS Safari 不支持元素级 requestFullscreen,liveEnterFullscreen 只能退回点站内全屏键(那种确实是假全屏),此时靠 liveFsViaButton 放开, 仍按全屏铺满。Android 走不到这条分支,不会重新引入来回跳。 */ function isLiveFullscreen() { if (liveIsNativeFs()) return true; if (liveFsViaButton) return true; const wrap = document.querySelector('#fullscreen-container'); if (wrap) { try { if (wrap.matches(':fullscreen') || wrap.matches(':-webkit-full-screen')) return true; } catch (e) {} } return false; } function syncLiveFullscreen() { if (!isLiveRoomPage() || !document.body) return false; const on = isLiveFullscreen(); document.body.classList.toggle('bili-live-fs', on); return on; } /* ===== 直播间:站内「假全屏」class 的摘除(辅助,非主力)===== 主播间的"网页全屏"在不同版本里挂过 body.player-full-win。 它不是根因(根因是 isLiveFullscreen 的尺寸启发式,已在 v131.90 修正), 但留着无害:class 一出现就摘掉,配合样式表第 6 段把它按普通态渲染。 只有"摘 class"这件事 CSS 做不到,所以保留这一小段 JS。 */ function liveDropWebFullscreenClass() { if (!document.body) return false; // 原生全屏生效时留着无害,但没必要动,免得和站内状态机互相抖 if (liveIsNativeFs()) return false; if (!document.body.classList.contains('player-full-win')) return false; document.body.classList.remove('player-full-win'); return true; } /* 假全屏不只会挂 class,还会给容器链写行内 position:fixed / inset / z-index (行内 !important,样式表压不过,只能清行内)。非全屏时清一次, 播放器和评论区才会老老实实回到普通流里。 */ const LIVE_RESET_SEL = [ '.live-room-app', 'main.app-content', '.app-body', 'section.player-and-aside-area', '#player-ctnr', '.fullscreen-container-paddingbox' ]; const LIVE_RESET_PROPS = [ 'position', 'top', 'left', 'right', 'bottom', 'inset', 'width', 'height', 'max-width', 'min-height', 'max-height', 'transform', 'z-index', 'overflow' ]; function resetLiveFakeFullscreenInline() { let changed = false; const roots = [document.documentElement, document.body]; roots.forEach(function (el) { if (!el || !el.style) return; LIVE_RESET_PROPS.forEach(function (p) { if (el.style.getPropertyValue(p)) { el.style.removeProperty(p); changed = true; } }); }); LIVE_RESET_SEL.forEach(function (s) { const el = document.querySelector(s); if (!el || !el.style) return; LIVE_RESET_PROPS.forEach(function (p) { if (el.style.getPropertyValue(p)) { el.style.removeProperty(p); changed = true; } }); }); return changed; } function bindLiveWebFullscreenGuard() { if (!isLiveRoomPage() || bindLiveWebFullscreenGuard._done) return; const start = function () { if (!document.body) { setTimeout(start, 50); return; } if (bindLiveWebFullscreenGuard._done) return; bindLiveWebFullscreenGuard._done = true; const obs = new MutationObserver(function () { // 摘掉后自己也会触发一次(class 没了 -> 直接返回 false),不会自激 const a = liveDropWebFullscreenClass(); const b = isLiveFullscreen() ? false : resetLiveFakeFullscreenInline(); if (a || b) { syncLiveFullscreen(); applyLivePlayerSize(); } }); obs.observe(document.body, { attributes: true, attributeFilter: ['class', 'style'] }); liveDropWebFullscreenClass(); }; start(); } /* ===== 直播间:返回键 ===== 实测行为:浏览器自己会把「全屏中的第一次返回」用来退出全屏(不发生导航), 第二次返回才真的后退。所以只要把退出全屏后站内补上的"假全屏"及时压回普通态 (见 isLiveFullscreen / applyLivePlayerSize),返回键的行为就自动正确了: 第一次返回 = 退出全屏,仍停在直播间 第二次返回 = 正常离开直播间页面 v131.89 曾经用 pushState 哨兵条目去"吃"这次返回,实测无效(浏览器压根不派 popstate,哨兵反而多留一条历史记录),这里删掉,只保留一条兜底: 万一有浏览器在返回时既退全屏又派 popstate,这里补一次 exitFullscreen, 保证不会带着全屏态去后退。 */ function bindLiveBackExitFullscreen() { if (!isLiveRoomPage() || bindLiveBackExitFullscreen._done) return; bindLiveBackExitFullscreen._done = true; window.addEventListener('popstate', function () { if (!isLiveRoomPage()) return; if (liveIsNativeFs()) liveExitFullscreen(); }, true); } function bindLiveFullscreenWatch() { if (!isLiveRoomPage()) return; const sync = function () { syncLiveFullscreen(); }; ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange'].forEach(function (ev) { document.addEventListener(ev, function () { // 进来过就是成功,别再被 liveEnterFullscreen 的复核当成失败去重试 const on = isLiveFullscreen(); if (on) { liveFsSawFs = true; } else { // 退出原生全屏的瞬间,站内会补一套"假全屏"(fixed 铺满视口)。 // 连清几拍:class、行内定位、尺寸全部压回普通态。 [0, 60, 150, 300, 600, 1000].forEach(function (ms) { setTimeout(function () { const a = liveDropWebFullscreenClass(); const b = resetLiveFakeFullscreenInline(); if (a || b) { syncLiveFullscreen(); applyLivePlayerSize(); } }, ms); }); } // 站内切换全屏后还要重排一帧,延后一拍才量得到真实尺寸 setTimeout(sync, 0); setTimeout(sync, 120); setTimeout(sync, 400); setTimeout(applyLivePlayerSize, 0); setTimeout(applyLivePlayerSize, 120); setTimeout(applyLivePlayerSize, 400); }); }); window.addEventListener('resize', function () { sync(); applyLivePlayerSize(); // 假全屏常在 resize 时补写行内尺寸,一并压回去 }); window.addEventListener('orientationchange', function () { [0, 300, 800].forEach(function (ms) { setTimeout(function () { sync(); applyLivePlayerSize(); }, ms); }); }); } /* ================================================================ 直播间:控制栏只留「时间」+「清晰度」 ================================================================ 单击播放器弹出的那条控制栏,探针实测: .control-area > .left-area(含「47:43」)+ .right-area(含「自动」+ 一排图标) 窄屏(360)下右侧那排图标挤成一团,显示不全、点了也没反应(音量/弹幕/网页全屏 这些在手机上都用不了),全部隐藏;只留: · 时间(形如 12:34 / 1:02:03) · 清晰度(自动 / 流畅 / 高清 / 蓝光 / 原画 / 1080P …) ⚠ 为什么需要一点点 JS:CSS 选择器无法按「元素里的文字」匹配 (:contains() 从未实装),认不出哪个是时间、哪个是清晰度。 所以 JS 只做一件事:给要留的按键打 data-bili-live-keep="1"; 隐藏、居中、间距全部由样式表第 9 段的 CSS 完成。 ---- */ const LIVE_TIME_RE = /^\d{1,3}:\d{2}(:\d{2})?$/; const LIVE_QUALITY_RE = /^(自动|流畅|清晰|标清|高清|超清|蓝光|原画|真彩|4K|8K|\d{3,4}[pP](30|50|60)?)$/; const LIVE_QUALITY_KEY = /画质|清晰度|quality|definition/i; function liveIsKeepCtrl(node) { if (!node || node.nodeType !== 1) return false; const txt = ((node.textContent || '').trim()); if (LIVE_TIME_RE.test(txt)) return true; if (LIVE_QUALITY_RE.test(txt)) return true; const meta = (typeof node.className === 'string' ? node.className : '') + ' ' + (node.getAttribute('aria-label') || '') + ' ' + (node.getAttribute('title') || ''); return LIVE_QUALITY_KEY.test(meta); } // 文字有时在嵌套的子 div 里(探针:时间文字在 .tip-wrap 内的 div 上), // 所以自己不匹配时再往下看一层;太宽的直接不认,避免整条区域被保留 function liveCtrlKeep(node) { if (!node || node.nodeType !== 1) return false; const r = node.getBoundingClientRect(); if (r.width > 200 || r.height > 80) return false; if (liveIsKeepCtrl(node)) return true; const kids = node.children || []; if (kids.length > 8) return false; for (let i = 0; i < kids.length; i++) { if (liveIsKeepCtrl(kids[i])) return true; } return false; } function liveTrimController() { if (!isLiveRoomPage()) return false; const wrap = document.querySelector('#fullscreen-container') || document.body; if (!wrap) return false; const ctrl = wrap.querySelector('#web-player-controller-wrap-el') || wrap.querySelector('.web-player-controller-wrap') || wrap.querySelector('.live-web-player-controller') || wrap.querySelector('[class*="web-player-controller"]'); if (!ctrl) { // 控制栏节点被整块换掉了:放开重绑开关,让 watch 重新挂到新节点上 bindLiveControllerWatch._done = false; return false; } const area = ctrl.querySelector('.control-area') || ctrl.querySelector('[class*="control-area"]') || ctrl; const areas = []; ['.left-area', '.right-area'].forEach(function (sel) { Array.prototype.forEach.call(area.querySelectorAll(sel), function (a) { // 只取直接子级,避免把嵌套区域也算进来 if (a.parentElement === area && areas.indexOf(a) === -1) areas.push(a); }); }); if (!areas.length) areas.push(area); let changed = false; areas.forEach(function (a) { Array.prototype.forEach.call(a.children, function (c) { if (!c || c.nodeType !== 1) return; const keep = liveCtrlKeep(c); const has = c.getAttribute('data-bili-live-keep') === '1'; if (keep && !has) { c.setAttribute('data-bili-live-keep', '1'); changed = true; } else if (!keep && has) { c.removeAttribute('data-bili-live-keep'); changed = true; } }); }); return changed; } function bindLiveControllerWatch() { if (!isLiveRoomPage() || bindLiveControllerWatch._done) return; const start = function () { const wrap = document.querySelector('#fullscreen-container'); const ctrl = wrap && (wrap.querySelector('#web-player-controller-wrap-el') || wrap.querySelector('.live-web-player-controller') || wrap.querySelector('.control-area')); if (!ctrl) { setTimeout(start, 400); return; } if (bindLiveControllerWatch._done) return; bindLiveControllerWatch._done = true; const obs = new MutationObserver(function () { if (obs._t) clearTimeout(obs._t); obs._t = setTimeout(function () { liveTrimController(); applyLiveControllerGeom(); // v132.05:控制栏重渲染后重新测偏移 }, 80); }); // 控制栏是 svelte 组件,会整体重渲染;class 变化也一起盯 obs.observe(ctrl, { childList: true, subtree: true, attributes: true, attributeFilter: ['class'] }); liveTrimController(); applyLiveControllerGeom(); }; start(); } /* ================================================================ 直播间:屏蔽播放器「长按」 ================================================================ 双击(尤其是退出全屏那一下)会带出两个东西: A) 浏览器的「选中文字 / 复制分享」浮层 —— 已由 CSS 的 -webkit-touch-callout:none + user-select:none 掐掉。 B) 播放器自带的调试面板(#live-player > ul,z=99, 「画面异常?切换解码策略试试 / 播放器日志信息 / 视频统计信息 / Room Player」) —— 挡画面,连点还会误触面板里的按键。 CSS 已经把 #live-player > ul 整块隐藏;这里再补两件 CSS 做不到的事: 1) contextmenu 在捕获阶段拦掉(浏览器长按浮层 + 站内面板都靠它触发), stopImmediatePropagation 保证站内挂在元素上的监听也收不到; 2) 按文本识别面板,打 .bili-live-ctx-menu —— 万一站内换了容器 (不再挂在 #live-player 下面),CSS 那组选择器照样能隐藏它。 */ function liveMarkContextMenu() { if (!isLiveRoomPage() || !document.body) return false; let changed = false; // 面板可能被挂到播放器外(portal),所以整棵文档都扫; // 识别串("画面异常 / 切换解码 / 播放器日志 / Room Player")是播放器独有的,不会误伤 const nodes = document.querySelectorAll('ul, [class*="menu"], [class*="context"]'); Array.prototype.forEach.call(nodes, function (el) { const txt = (el.textContent || '') + ' ' + ((typeof el.className === 'string' ? el.className : '') || ''); if (!/画面异常|播放器日志|视频统计信息|Room Player|切换解码/.test(txt)) return; if (el.classList.contains('bili-live-ctx-menu')) return; el.classList.add('bili-live-ctx-menu'); changed = true; }); return changed; } /* ================================================================ v132.57:隐藏直播播放器里的「点击取消静音」提示 ---------------------------------------------------------------- 这层的类名是 CSS Module 哈希(探针里是 ntMzdJGK5xCLVyRrSZKp), 站内每次构建都会换,CSS 写不死 → 只能按文本识别后补一个稳定的 .bili-live-mute-toast 类,交给样式表隐藏(与 .bili-live-ctx-menu 同一套路)。 扫描范围只限 #live-player 的直接子级,并且先过一道「结构」廉价前置过滤: · 只看 DIV、且恰好 2 个子节点、且同时含 img 与 p (提示层的结构就是「喇叭图标 + 一行文字」) · 文本命中「取消静音」才动手 这样即使弹幕不断插入(统一守卫每 120ms 批量触发一次), 一轮也只是十几次 O(1) 判断,不会去读 .web-player-danmaku 那棵装着成百上千条弹幕的子树,开销可忽略。 命中后打 data-bili-mute-hidden,下一轮直接跳过,不重复写样式。 ================================================================ */ function liveMarkMuteToast() { const host = document.querySelector('#live-player'); if (!host) return false; let changed = false; const kids = host.children; for (let i = 0; i < kids.length; i++) { const el = kids[i]; if (el.tagName !== 'DIV') continue; if (el.getAttribute('data-bili-mute-hidden')) continue; if (el.classList && el.classList.contains('bili-live-mute-toast')) continue; // 廉价前置过滤:必须是「img + p」两件套 if (el.children.length !== 2) continue; if (!el.querySelector('img') || !el.querySelector('p')) continue; let txt = ''; try { txt = el.textContent || ''; } catch (e) { continue; } if (txt.indexOf('取消静音') < 0) continue; el.setAttribute('data-bili-mute-hidden', '1'); if (el.classList) el.classList.add('bili-live-mute-toast'); changed = true; } return changed; } function bindLiveMuteToastGuard() { if (bindLiveMuteToastGuard._done) return; bindLiveMuteToastGuard._done = true; // 播放器是异步挂上去的,首屏多扫几拍 [0, 150, 400, 900, 1800].forEach(function (ms) { setTimeout(liveMarkMuteToast, ms); }); liveMarkMuteToast(); // 之后交给统一守卫(页面上没有 #live-player 时 test 直接返回 false) registerDomGuard( function () { return !!document.querySelector('#live-player'); }, liveMarkMuteToast ); } function bindLiveLongPressGuard() { if (!isLiveRoomPage() || bindLiveLongPressGuard._done) return; bindLiveLongPressGuard._done = true; // 捕获阶段:window 是最外层,先于站内任何元素级监听,能彻底掐断 window.addEventListener('contextmenu', function (e) { if (!isLiveRoomPage()) return; const t = e.target; if (!t || !t.closest) return; if (!t.closest('#fullscreen-container, #live-player')) return; e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation(); // 面板万一已经被触发,立刻补一次标记,让 CSS 收掉 setTimeout(liveMarkContextMenu, 0); }, true); // 切换全屏后的一小段时间里,长按 / 连点最容易误触,多扫几次 const sweep = function () { if (liveMarkContextMenu()) liveTrimController(); }; [0, 120, 400, 900].forEach(function (ms) { setTimeout(sweep, ms); }); liveMarkContextMenu(); } /* ===== 直播间:播放器尺寸接管 ===== 站内有两种「行内 !important」写死的尺寸,样式表压不过,只能由 JS 补写行内: A) 正常态:站内 JS 按「容器高 - 礼物栏 84px」给 .player-section 写高度, 礼物栏被我们隐藏后这 84 仍被扣掉,视频被压成 106 高的扁条(应为 190)。 B) 网页全屏(body.player-full-win):站内把 #fullscreen-container 写成 行内 668px(PC 定宽),塞进 360 的视口 → 右侧溢出被裁、左侧一片空白。 两种都靠"后写覆盖先写"解决:同一元素的 inline style 是同一个声明块, 我们后写同一属性就会顶掉站内的值。 v131.93:内层链改用 height:100%(而非 calc),跟随外层内容盒高度, 只有一份取整,彻底消除左/底部约 1px 的次像素缝隙(旧版 calc 与外层 calc 分别取整会产生差值)。min-height 与 max-height 同步写 100%, 防止站内再改 height 时出现闪动。 外层仍用 calc 维持 16:9 比例(含 6px 边框补偿)。 */ /* v131.93:内层链统一用 100% 跟随父级,消除两份 calc 取整带来的次像素缝隙。 外层仍用 calc 维持 16:9 比例(含 3px 边框 x2 = 6px)。 */ const LIVE_PLAYER_H = '100%'; // 外层还要带上 3px 边框 x2 = 6px,与 CSS 里 #fullscreen-container 的取值一致 const LIVE_WRAP_H = 'calc((100vw - 22px) * 9 / 16 + 6px)'; const LIVE_PLAYER_SEL = ['.player-section', '#live-player-ctnr', '#live-player', '#live-player video']; // 只在值真的不一样时才写:observer 会盯着 style 属性,无脑重写会自激死循环 function setLiveStyle(el, prop, val) { if (!el || !el.style) return; if (el.style.getPropertyValue(prop) === val && el.style.getPropertyPriority(prop) === 'important') return; el.style.setProperty(prop, val, 'important'); } /* v131.94 兜底(只在 display:contents 不被支持的环境才会真正起作用): CSS 里已让 .player-section 走 display:contents,行内几何值自然失效。 万一某个 WebView 不支持 display:contents,站内那套「容器高 - 84px」 的行内 height / min-height / max-height / bottom 就会重新压扁画面, 而样式表压不过行内 !important —— 这里直接把它摘掉,交回样式表。 摘完后 setLiveStyle 会立刻补写 100%,两边不会打架。 */ function stripPlayerSectionInline(wrap) { const sec = wrap && wrap.querySelector('.player-section'); if (!sec || !sec.style) return; ['height', 'min-height', 'max-height', 'top', 'bottom'].forEach(function (p) { if (sec.style.getPropertyValue(p)) sec.style.removeProperty(p); }); } function applyLivePlayerSize() { const wrap = document.querySelector('#fullscreen-container'); if (!wrap) return; stripPlayerSectionInline(wrap); const chain = LIVE_PLAYER_SEL.map(function (s) { return wrap.querySelector(s); }); if (isLiveFullscreen()) { // 原生全屏:贴满视口,顶掉站内行内的 668px 定宽 setLiveStyle(wrap, 'position', 'fixed'); setLiveStyle(wrap, 'top', '0'); setLiveStyle(wrap, 'left', '0'); setLiveStyle(wrap, 'width', '100%'); setLiveStyle(wrap, 'max-width', 'none'); setLiveStyle(wrap, 'height', '100%'); setLiveStyle(wrap, 'max-height', 'none'); setLiveStyle(wrap, 'min-height', '0'); setLiveStyle(wrap, 'transform', 'none'); chain.forEach(function (el) { if (!el) return; setLiveStyle(el, 'width', '100%'); setLiveStyle(el, 'max-width', 'none'); setLiveStyle(el, 'height', '100%'); setLiveStyle(el, 'min-height', '0'); setLiveStyle(el, 'max-height', 'none'); }); applyLiveControllerGeom(); // v132.05:全屏态同样实测补偿 return; } // 正常态:恢复 16:9。外层必须一并写回来 —— // 上面全屏分支给它写过 height:100%,退出全屏后行内 !important 会压过 CSS, // 播放器就会一直顶着满屏高。两条分支的属性集要对齐,才能来回切换都收敛。 // // v131.90:这里额外把 position / inset / transform / z-index 一并写回。 // 站内退出原生全屏时会给 #fullscreen-container 写上一套行内 // 「position:fixed; top/left:0; 满视口」的假全屏,光改尺寸不够 —— // 它还 fixed 着,就会盖在整页上面,看着仍然像全屏(只是带地址栏)。 // // v131.93:内层链 height 改为 100%(不再用 calc),跟随外层内容盒, // 只有一份取整,彻底消除左/底部约 1px 的次像素缝隙。 setLiveStyle(wrap, 'position', 'relative'); setLiveStyle(wrap, 'top', 'auto'); setLiveStyle(wrap, 'right', 'auto'); setLiveStyle(wrap, 'bottom', 'auto'); setLiveStyle(wrap, 'left', 'auto'); setLiveStyle(wrap, 'transform', 'none'); setLiveStyle(wrap, 'z-index', 'auto'); setLiveStyle(wrap, 'width', '100%'); setLiveStyle(wrap, 'max-width', '100%'); setLiveStyle(wrap, 'height', LIVE_WRAP_H); setLiveStyle(wrap, 'min-height', '0'); setLiveStyle(wrap, 'max-height', LIVE_WRAP_H); chain.forEach(function (el) { if (!el) return; setLiveStyle(el, 'position', 'absolute'); setLiveStyle(el, 'top', '0'); setLiveStyle(el, 'left', '0'); setLiveStyle(el, 'transform', 'none'); setLiveStyle(el, 'width', '100%'); setLiveStyle(el, 'max-width', '100%'); setLiveStyle(el, 'height', LIVE_PLAYER_H); setLiveStyle(el, 'min-height', LIVE_PLAYER_H); setLiveStyle(el, 'max-height', LIVE_PLAYER_H); }); applyLiveControllerGeom(); // v132.05:同上,普通态也实测(通常 d=0) } /* ===== 直播间:控制栏偏移补偿 + 自建遮罩开关(v132.05)===== 样式表第 11 段负责两件 CSS 能做的事: ① 弃用站内遮罩层 .web-player-controller-bg(display:none) —— 它带着 bottom:84px 让位,唤出瞬间会先在 84px 处渲染, 补偿永远比它慢一拍,因此"闪一下"无法靠轮询根治,直接弃用; ② 自建遮罩 #live-player::after(常驻 opacity:0, .bili-live-ctrl-on 时淡入到 1),铺满播放器、底部渐隐向上。 这里补 CSS 做不到的一件事:**测出按键行真实的偏移像素数** d = 播放器底边 − 按键行底边 只写 margin(绝不写 height / top / bottom / transform / opacity —— 那是站内收起动画的状态通道,v132.01/02 碰了它才改坏自动隐藏)。 ⚠ 用哪条 margin 取决于站内用的是 top 还是 bottom 定位: bottom 定位 → margin-bottom: -d 下移 top 定位 / 静态 → margin-top: +d 下移 读了 computed style 再决定,两条都写会双重位移。 ⚠ 撤除规则: · d <= 2px(已贴底)→ 撤; · 元素零尺寸 / 整块跑到播放器外(站内已收起卸载)→ 撤; · 撤除必须写 0(行内 !important),光 removeProperty 会让 样式表那条 -84px 兜底立刻重新生效。 ⚠ 补偿会让测得底边跟着下移,所以每轮都要用当前生效的位移量 还原原始位置再算,避免自激振荡。 ---- */ // 需要补偿的目标:按键行(站内遮罩层已在第 11 段直接弃用) const LIVE_SHIFT_SEL = ['.control-area']; /* 读当前**实际生效**的位移量与定位方式。 返回 { v: 下移像素(正数=已下移), via: 'mb' | 'mt' } */ function liveShiftState(el) { let cs = null; try { cs = window.getComputedStyle(el); } catch (e) {} if (!cs) return { v: 0, via: 'mb' }; // bottom 定位时用 margin-bottom 负值下移;否则用 margin-top 正值 const via = (cs.bottom && cs.bottom !== 'auto') ? 'mb' : 'mt'; const raw = via === 'mb' ? cs.marginBottom : cs.marginTop; const n = parseInt(raw, 10); const v = isNaN(n) ? 0 : n; return { v: (via === 'mb' ? -v : v), via: via }; } /* 写入下移 d(另一条 margin 清零,避免残留造成双重位移) */ function liveSetShift(el, via, d) { const val = (via === 'mb' ? (-d) : d) + 'px'; setLiveStyle(el, via === 'mb' ? 'margin-top' : 'margin-bottom', '0px'); setLiveStyle(el, via === 'mb' ? 'margin-bottom' : 'margin-top', val); } function liveClearShift(el) { if (!el || !el.style) return; setLiveStyle(el, 'margin-bottom', '0px'); setLiveStyle(el, 'margin-top', '0px'); } function applyLiveControllerGeom() { if (!isLiveRoomPage()) return false; const player = document.querySelector('#live-player') || document.querySelector('#fullscreen-container'); if (!player) return false; const pr = player.getBoundingClientRect(); if (pr.height <= 0) return false; const pBottom = pr.bottom; let shown = false; LIVE_SHIFT_SEL.forEach(function (sel) { Array.prototype.forEach.call(document.querySelectorAll(sel), function (el) { if (!el || !el.style) return; const r = el.getBoundingClientRect(); // 零尺寸 / 整块跑到播放器外 —— 站内已收起或卸载,撤补偿 if (r.height <= 0 || r.width <= 0 || r.top >= pBottom - 2 || r.bottom <= 2) { liveClearShift(el); return; } if (sel === '.control-area') shown = true; const st = liveShiftState(el); // 还原未补偿时的原始底边 const rawBottom = r.bottom - st.v; const d = Math.round(pBottom - rawBottom); if (d <= 2) { liveClearShift(el); return; } if (d === st.v) return; // 已收敛,别反复写(防自激) liveSetShift(el, st.via, d); }); }); // 补底渐变的开关:控制栏显示时才亮,收起立刻灭 if (player.classList) player.classList.toggle('bili-live-ctrl-on', shown); return true; } function bindLiveControllerCompLoop() { if (!isLiveRoomPage() || bindLiveControllerCompLoop._done) return; bindLiveControllerCompLoop._done = true; // 站内滑入滑出有动画 + 会重新挂载,轮询跟上;250ms 一次,只在直播间页跑 setInterval(applyLiveControllerGeom, 250); // 控制栏一挂载就立刻量一次,免得等下一拍才贴底(首帧闪一下) const host = document.querySelector('#live-player') || document.body; if (host) { const mo = new MutationObserver(function () { if (mo._t) clearTimeout(mo._t); mo._t = setTimeout(applyLiveControllerGeom, 30); }); // 只听**直接子节点**:控制栏就是 #live-player 的子孙, // 用 subtree 会被弹幕的频繁插入带着一直跑 mo.observe(host, { childList: true }); } applyLiveControllerGeom(); } /* 站内会随时重写行内尺寸(切清晰度、切全屏、双击…), 光靠 run() 的那几个定时器抓不住,这里专盯着播放器链的 style/class 变化。 */ function bindLivePlayerSizeWatch() { if (!isLiveRoomPage() || bindLivePlayerSizeWatch._done) return; const wrap = document.querySelector('#fullscreen-container'); if (!wrap) { setTimeout(bindLivePlayerSizeWatch, 300); return; } bindLivePlayerSizeWatch._done = true; const obs = new MutationObserver(function () { if (obs._t) clearTimeout(obs._t); obs._t = setTimeout(applyLivePlayerSize, 60); }); obs.observe(wrap, { attributes: true, attributeFilter: ['style', 'class'] }); LIVE_PLAYER_SEL.forEach(function (s) { const el = wrap.querySelector(s); if (el) obs.observe(el, { attributes: true, attributeFilter: ['style', 'class'] }); }); if (document.body) obs.observe(document.body, { attributes: true, attributeFilter: ['class'] }); applyLivePlayerSize(); } /* ===== 直播间:聊天区滚动(v131.98 真·修好)===== 探针实录(bili_probe 含隐藏的探针)暴露了真正的根因: .chat-history-panel [344x516] ← 面板被限死在一屏内(v131.97 生效了) > #chat-history-list [344x1286] ← 列表却有 1286 高,跟内容一样长! 列表自己跟内容一样高 ⇒ scrollHeight === clientHeight ⇒ 可滚动量 max = 0 ⇒ **它压根不是滚动容器**: · 手指在聊天区滑 → 没有可滚的东西 → 纹丝不动 · 面板 overflow:hidden 把它裁掉 → 页面也不产生滚动 → 整页也滑不动 这正是「从一开始只要手指落在聊天区,滑动就完全无效」的原因, 也解释了 v131.97 的 JS 兜底为何没动静(它第一条就是 max<=2 直接 return)。 为什么 CSS 的 height:100% !important 没把它压回 516? 站内给列表写了**行内 !important** 的高度(与播放器那套 84px 同款手段), 样式表的 !important 压不过行内的 !important,只能由 JS 后写覆盖。 本版两步走: ① forceLiveChatSize —— JS 用行内 !important 把列表高度钉成面板高度 (优先取 panel.clientHeight 的 px 值,最稳;拿不到才退回 100%), 并补齐 overflow-y:auto / overscroll-behavior:contain / touch-action 等。 站内随时会重写(Vue 重渲染、新弹幕),用 MutationObserver 盯着补写。 ② 手势兜底 —— 监听挂在 document **捕获阶段**(比站内任何监听器都早, 否则站内在祖先上 stopPropagation 我们就永远收不到事件), 只在「原生确实没动」时接管:defaultPrevented 为真,或累计位移 > 12px 而 scrollTop 纹丝不动。接管后手动写 scrollTop, 用 passive:false 以便必要时 preventDefault 掐掉页面滚动。 */ /* v131.99:聊天区高度改为「实测视口 - 面板顶距 - 36px」 —— 为什么不用纯 CSS 的 100dvh?两份探针对比说明了问题: 刚进直播间:100dvh ≈ 524 → 面板 240,看着很矮 全屏再退出:100dvh ≈ 800 → 面板 516,明显变高 同一个页面视口差出 276px,是移动端浏览器地址栏/工具栏收放造成的, dvh 在某些 WebView 里还会滞后一拍,纯 CSS 算出来的高度忽高忽低。 这里直接读 visualViewport.height(拿不到退 innerHeight), 再用面板的**文档坐标**顶距(rect.top + scrollY,不随滚动变化)换算: 目标高 = 视口高 - 面板顶距 - 36 这样无论地址栏是收是放,聊天区底边恒距屏幕底 36px。 */ function forceLiveChatSize() { const panel = document.querySelector('.chat-history-panel'); const list = document.querySelector('#chat-history-list'); if (!list) return false; let h = '100%'; if (panel) { const vp = window.visualViewport; const vh = (vp && vp.height) || window.innerHeight || (document.documentElement && document.documentElement.clientHeight) || 0; const scrollY = window.scrollY || window.pageYOffset || 0; const docTop = panel.getBoundingClientRect().top + scrollY; // 文档坐标,稳定 if (vh > 320 && docTop > 0) { const target = Math.round(vh - docTop - 36); // 距屏幕底 36px if (target > 120) h = target + 'px'; } else if (panel.clientHeight > 40) { h = panel.clientHeight + 'px'; // 兜底:沿用面板高 } } setLiveStyle(list, 'height', h); setLiveStyle(list, 'min-height', '0'); setLiveStyle(list, 'max-height', h); setLiveStyle(list, 'overflow-x', 'hidden'); setLiveStyle(list, 'overflow-y', 'auto'); setLiveStyle(list, 'overscroll-behavior', 'contain'); setLiveStyle(list, 'touch-action', 'pan-y'); setLiveStyle(list, 'transform', 'none'); setLiveStyle(list, '-webkit-overflow-scrolling', 'touch'); if (panel) { // 面板跟着列表一起定高,两者始终等高(否则列表会被面板裁掉一截) if (h !== '100%') { setLiveStyle(panel, 'height', h); setLiveStyle(panel, 'min-height', '0'); setLiveStyle(panel, 'max-height', h); } setLiveStyle(panel, 'overflow', 'hidden'); setLiveStyle(panel, 'overscroll-behavior', 'contain'); setLiveStyle(panel, 'touch-action', 'pan-y'); } return true; } function bindLiveChatScroll() { if (!isLiveRoomPage() || bindLiveChatScroll._done) return; if (!forceLiveChatSize()) { setTimeout(bindLiveChatScroll, 400); return; } bindLiveChatScroll._done = true; // 站内随时重写行内尺寸 / 重渲染,盯着补写 const panel = document.querySelector('.chat-history-panel'); const list = document.querySelector('#chat-history-list'); const obs = new MutationObserver(function () { if (obs._t) clearTimeout(obs._t); obs._t = setTimeout(forceLiveChatSize, 60); }); if (list) obs.observe(list, { attributes: true, attributeFilter: ['style', 'class'] }); if (panel) obs.observe(panel, { attributes: true, attributeFilter: ['style', 'class'] }); window.addEventListener('resize', function () { setTimeout(forceLiveChatSize, 120); }); window.addEventListener('orientationchange', function () { setTimeout(forceLiveChatSize, 300); }); // v131.99:地址栏收放 / 键盘弹出只改 visualViewport,不触发 window.resize, // 这里单独盯住,视口一变立刻按新的可视高度重算「距底 36px」 if (window.visualViewport) { const vv = window.visualViewport; const onVV = function () { setTimeout(forceLiveChatSize, 80); }; vv.addEventListener('resize', onVV); vv.addEventListener('scroll', onVV); } // 首屏 dvh 常滞后一拍,多补几次让高度尽快收敛 [0, 150, 400, 900, 1800].forEach(function (ms) { setTimeout(forceLiveChatSize, ms); }); let active = false, hijack = false, lastY = 0, startTop = 0, moved = 0; const inChat = function (t) { return !!(t && t.closest && t.closest('.chat-history-panel, #aside-area-vm')); }; document.addEventListener('touchstart', function (e) { if (e.touches.length !== 1 || !inChat(e.target)) { active = false; return; } forceLiveChatSize(); // 每次手势前先确认列表被钉住 const l = document.querySelector('#chat-history-list'); if (!l) { active = false; return; } active = true; hijack = false; moved = 0; lastY = e.touches[0].clientY; startTop = l.scrollTop; }, { capture: true, passive: true }); document.addEventListener('touchmove', function (e) { if (!active || e.touches.length !== 1) return; const l = document.querySelector('#chat-history-list'); if (!l) return; const y = e.touches[0].clientY; const dy = lastY - y; // 手指上滑 dy>0 → scrollTop 变大 lastY = y; moved += Math.abs(dy); const max = l.scrollHeight - l.clientHeight; if (max <= 2) return; // 列表还是没被限高,滚不了 if (e.defaultPrevented) hijack = true; else if (!hijack && moved > 12 && Math.abs(l.scrollTop - startTop) < 2) hijack = true; if (hijack) { const next = Math.max(0, Math.min(max, l.scrollTop + dy)); if (next !== l.scrollTop) l.scrollTop = next; if (e.cancelable) e.preventDefault(); // 掐掉页面滚动,避免两头一起动 } }, { capture: true, passive: false }); const reset = function () { active = false; hijack = false; }; document.addEventListener('touchend', reset, { capture: true, passive: true }); document.addEventListener('touchcancel', reset, { capture: true, passive: true }); } /* 控制栏最右侧那个全屏键 —— 只在兜底时才用(见 liveEnterFullscreen 说明) */ function liveFindFullscreenButton() { const wrap = document.querySelector('#fullscreen-container') || document.body; const ctrl = wrap.querySelector('#web-player-controller-wrap-el') || wrap.querySelector('.web-player-controller-wrap') || wrap.querySelector('[class*="web-player-controller"]'); if (!ctrl) return null; // 探针「按键」实测:控制栏 = .control-area > .left-area / .right-area, // 全屏键在 .right-area 最右端(right=777,与 .right-area 右边缘齐平) const area = ctrl.querySelector('.right-area') || ctrl.querySelector('[class*="right-area"]') || ctrl; let best = null, bestScore = -1, bestRight = -Infinity; const nodes = area.querySelectorAll( 'button, [role="button"], [class*="tip-wrap"], [class*="btn"], [class*="icon"], ' + '[class*="full"], [class*="screen"]' ); Array.prototype.forEach.call(nodes, function (el) { const tag = (el.tagName || '').toLowerCase(); if (tag === 'svg' || tag === 'path' || tag === 'g') return; // 图标内部节点,点了不触发 const r = el.getBoundingClientRect(); // 只认图标级小元素,排除整条 .right-area(597 宽) if (r.width < 8 || r.height < 8 || r.width > 80 || r.height > 80) return; const cls = (typeof el.className === 'string' ? el.className : '') || ''; const aria = (el.getAttribute && el.getAttribute('aria-label')) || ''; const key = cls + ' ' + aria + ' ' + (el.textContent || ''); let score = 0; if (/full|screen|全屏/i.test(key)) score += 4; // 明确是全屏键 if (tag === 'button' || el.getAttribute('role') === 'button') score += 2; if (/tip-wrap|btn|button/i.test(cls)) score += 1; // 按钮外层,比内部 span 稳 if (score > bestScore || (score === bestScore && r.right > bestRight)) { best = el; bestScore = score; bestRight = r.right; } }); return best; } /* ===== 直播间:双击 = 进出「原生全屏」 ===== 目标行为(v131.89 收紧为**严格二态**): · 未开全屏 → 双击进入**原生全屏**(没有浏览器地址栏) · 已开原生全屏 → 双击退出全屏,回到普通态(播放器 16:9 + 评论区在下) 为什么不再点站内那个键: 实测它切出来的是「网页全屏」body.player-full-win —— 带浏览器地址栏, 底部还留着给礼物栏的 84px 空档,而且会把播放器写死成 668px 定宽。 上一版正是"无条件点那个键",才出现 · 未开全屏双击没反应(控制栏未渲染时找不到键,退回的 API 调用也没生效) · 全屏中双击在"正常全屏 ↔ 带地址栏的全屏"之间来回跳 v131.90 的三条修复(v131.89 只做了第 2 条,判定没改,所以现象"没变化"): 1) ★根因★ isLiveFullscreen 只认原生全屏,删掉"尺寸启发式" —— 旧版一看播放器贴满视口就判"全屏",于是退出原生全屏后站内补的 假全屏被当成全屏、还被写成 100%/100%,真就铺满视口(带地址栏)。 现在假全屏一律判"非全屏",被强制压回 16:9,双击自然回到普通态。 2) 退出原生全屏的瞬间,站内有概率补 class / 写行内 fixed 定位, 由 liveExitFullscreen 连清几拍 + bindLiveWebFullscreenGuard 常驻清理, 行内 position/inset 也被写回 relative/auto(行内 !important 只能这样顶) 3) 返回键交给浏览器自身:它本来就会用第一次返回退出全屏, 假全屏被压回普通态后,"第一次=退全屏、第二次=离开页面"自动成立 进全屏仍带一条自愈链:依次试 #fullscreen-container / #live-player / .player-section,并对每个候选做结果复核 —— 有些浏览器是 promise 不 reject 但实际压根没进全屏,400ms 后复查一次,没进就换下一个候选; 三个都失败才退回点站内那个键(老浏览器没有 Fullscreen API 时的最后退路)。 —— 状态判断、事件拦截、触发点击,CSS 都做不到,这一处只能 JS。 ---- */ // 由 fullscreenchange 置位:用来区分"请求失败"和"进来又被人手动退出了" let liveFsSawFs = false; // 只有 iOS Safari 这类不支持元素级 requestFullscreen 的浏览器才会置位: // 表示"当前的全屏其实是站内自造的假全屏",此时仍要按全屏铺满(否则双击等于没反应) let liveFsViaButton = false; function liveIsNativeFs() { return !!(document.fullscreenElement || document.webkitFullscreenElement); } function liveExitNativeFullscreen() { const exit = document.exitFullscreen || document.webkitExitFullscreen || document.webkitCancelFullScreen || document.msExitFullscreen; if (!exit) return false; try { const p = exit.call(document); if (p && p.catch) p.catch(function () {}); } catch (e) { return false; } return true; } /* 退出全屏:API 退出 + 把站内随后补上的假全屏清干净。 站内补发的时机不定(实测在下一帧到几百毫秒之间),所以连清几拍: · class(player-full-win) · 容器链的行内 fixed / inset / z-index(行内 !important,只能清行内) · 播放器链的行内尺寸(applyLivePlayerSize 写回 16:9) 每次清理后都要 syncLiveFullscreen(),好让 bili-live-fs 立刻摘掉。 */ function liveExitFullscreen() { liveFsViaButton = false; const clean = function () { const a = liveDropWebFullscreenClass(); const b = !liveIsNativeFs() ? resetLiveFakeFullscreenInline() : false; syncLiveFullscreen(); applyLivePlayerSize(); return a || b; }; clean(); const ok = liveExitNativeFullscreen(); [0, 60, 150, 300, 600, 1000, 1600].forEach(function (ms) { setTimeout(clean, ms); }); return ok; } function liveClickFsButton() { const btn = liveFindFullscreenButton(); if (!btn) return false; liveFsViaButton = true; // 这一路切出来的是站内假全屏,只能认它 btn.click(); return true; } function liveEnterFullscreen() { const cands = ['#fullscreen-container', '#live-player', '.player-section']; liveFsSawFs = false; let done = false; const tryOne = function (idx) { if (done) return; if (idx >= cands.length) { done = true; liveClickFsButton(); return; } const el = document.querySelector(cands[idx]); const req = el && (el.requestFullscreen || el.webkitRequestFullscreen || el.webkitRequestFullScreen || el.msRequestFullscreen); if (!req) { tryOne(idx + 1); return; } let p = null; try { p = req.call(el); } catch (e) { tryOne(idx + 1); return; } if (p && p.then) { p.then(function () { done = true; }, function () { if (!liveIsNativeFs()) tryOne(idx + 1); }); } // 复核:没收到过 fullscreenchange、现在也没在全屏,才算这次尝试失败 setTimeout(function () { if (done || liveFsSawFs || liveIsNativeFs()) return; tryOne(idx + 1); }, 400); }; tryOne(0); return true; } /* 严格二态:全屏中 -> 退出;否则 -> 进原生全屏。 中间的"站内假全屏"不是一个合法状态 —— 进之前先把它清掉, 免得 requestFullscreen 被站内的状态机带偏。 */ function liveToggleFullscreen() { if (liveIsNativeFs()) return liveExitFullscreen(); // 只有 iOS 兜底那条路会是真的假全屏,点站内键退出它 if (liveFsViaButton) { liveFsViaButton = false; const hit = liveClickFsButton(); setTimeout(function () { liveFsViaButton = false; syncLiveFullscreen(); applyLivePlayerSize(); }, 300); return hit; } liveDropWebFullscreenClass(); resetLiveFakeFullscreenInline(); return liveEnterFullscreen(); } // 切换后按站内重排的节奏复核几次:状态类 + 尺寸都要跟上 function liveAfterToggle() { [60, 200, 400, 900].forEach(function (ms) { setTimeout(function () { syncLiveFullscreen(); applyLivePlayerSize(); applyLiveControllerGeom(); // v132.05:切全屏后重新测偏移 }, ms); }); } function bindLiveDblclickFullscreen() { if (!isLiveRoomPage() || bindLiveDblclickFullscreen._done) return; bindLiveDblclickFullscreen._done = true; let lastTapT = 0, lastTapX = 0, lastTapY = 0, lastHandledT = 0, lastHandledSrc = ''; // 只认落在播放器画面上的操作:控制栏自己身上的双击不抢 const inPlayer = function (t) { if (!t || !t.closest) return false; // v132.02:探针(探测脚本 v3.4)在全屏时会被搬进 #fullscreen-container, // 它虽在播放器容器内却不是画面,双击它不该切换全屏 if (t.closest('#__probe_btn, #__probe_panel')) return false; if (!t.closest('#fullscreen-container, #live-player')) return false; if (t.closest('#web-player-controller-wrap-el, .web-player-controller-wrap, .control-area')) return false; return true; }; window.addEventListener('dblclick', function (e) { if (!inPlayer(e.target)) return; const now = Date.now(); // 手机上手势已经被 touchstart 那条处理过,浏览器紧跟着合成的 dblclick // 会让同一次双击被切换两次(等于没切 / 或切到另一个全屏态),这里挡掉 if (now - lastHandledT < 700 && lastHandledSrc === 'touch') return; e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation(); lastHandledT = now; lastHandledSrc = 'dbl'; liveToggleFullscreen(); liveAfterToggle(); }, true); // 站内若走 touch 而非 dblclick,这里兜住;lastHandledT 防与 dblclick 重复触发 window.addEventListener('touchstart', function (e) { if (!e.touches || e.touches.length !== 1) return; if (!inPlayer(e.target)) return; const t = e.touches[0]; const now = Date.now(); if (now - lastHandledT < 700) return; if (now - lastTapT < 300 && Math.abs(t.clientX - lastTapX) < 40 && Math.abs(t.clientY - lastTapY) < 40) { lastTapT = 0; lastHandledT = now; lastHandledSrc = 'touch'; e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation(); liveToggleFullscreen(); liveAfterToggle(); return; } lastTapT = now; lastTapX = t.clientX; lastTapY = t.clientY; }, true); } /* ============================================================ ---- 首屏零闪(JS 兜底):元素一插进 DOM 就立刻藏 ---- 样式表第 12 段已经用不依赖 body class 的选择器把这些元素 display:none 了,理论上样式表在 document-start 注入、元素一解析 出来就是隐藏的。但实测仍有"先显示一下再隐藏",说明在部分 移动端脚本环境里脚本并不是真的在文档开始时执行 —— 页面已经画过一帧,CSS 再补就必然有闪。 CSS 管不到"脚本来晚了"这种情况,只能靠 JS 在**能跑的第一刻** 就动手,并且盯着 DOM:节点刚被插进来(哪怕还没画到屏幕) 就立刻写行内 display:none,把可见时间压到最短。 行内 !important 只用于"藏",不碰任何几何/动画属性, 不影响站内其它逻辑。 ---- */ const LIVE_CHROME_SEL = [ '#room-ssr-vm', '.link-navbar-ctnr', '.link-navbar-wrap', 'nav.link-navbar', '.link-navbar', '#head-info-vm', '#gift-control-vm', '#rank-list-vm', '#rank-list-ctnr-box', '#sections-vm', '#sidebar-vm', '#room-background-vm', '#gift-screen-animation-vm', '#guard-popup', 'footer.link-footer' ].join(','); function hideChromeEl(el) { if (!el || !el.style) return; const st = el.style; if (st.getPropertyValue('display') !== 'none') { st.setProperty('display', 'none', 'important'); } if (st.getPropertyValue('visibility') !== 'hidden') { st.setProperty('visibility', 'hidden', 'important'); } } /* main.app-content 的 padding-top:74px 是顶部栏留的位置, 顶部栏一藏它就是无来由的一块空白,顺手按当前页面状态归零。 */ function fixChromePadding() { const m = document.querySelector && document.querySelector('main.app-content'); if (!m || !m.style) return; let pt = 0; try { pt = parseFloat(window.getComputedStyle(m).paddingTop) || 0; } catch (e) {} if (pt > 40) m.style.setProperty('padding-top', '0px', 'important'); } function sweepLiveChrome() { if (!isLiveRoomPage()) return; if (!document.querySelectorAll) return; const list = document.querySelectorAll(LIVE_CHROME_SEL); for (let i = 0; i < list.length; i++) hideChromeEl(list[i]); fixChromePadding(); } function bindEarlyChromeHide() { if (!isLiveRoomPage() || bindEarlyChromeHide._done) return; bindEarlyChromeHide._done = true; let pending = false; const raf = window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function (fn) { return setTimeout(fn, 16); }; const schedule = function () { if (pending) return; pending = true; raf(function () { pending = false; sweepLiveChrome(); }); }; const attach = function () { const target = document.documentElement; if (!target) { setTimeout(attach, 0); return; } sweepLiveChrome(); // 盯着整个文档:节点一被插入(含 SSR 解析、JS 后插)就立刻藏, // 不等下一帧布局,把可见窗口压到最短 const mo = new MutationObserver(schedule); mo.observe(target, { childList: true, subtree: true }); document.addEventListener('DOMContentLoaded', sweepLiveChrome, true); window.addEventListener('load', sweepLiveChrome, true); // 只是为了压住"首屏那一下"。稳定期交给样式表(CSS 一直在), // 15 秒后停掉监听,省得跟着弹幕插入一直空跑。 setTimeout(function () { try { mo.disconnect(); } catch (e) {} }, 15000); }; attach(); } /* ===== v132.44:视频页 / 列表页 / 番剧页 —— 站内「网页全屏」JS 兜底 ===== 上面那条 CSS 只有在站内的定位**不是行内 !important** 时才压得过。 一旦站内直接往 element.style 里写 left/top/margin(播放器这类状态 几何值非常爱用行内样式),CSS 再 !important 也没辙 —— 所以补这一层。 判定口径(三个条件同时满足才动手): 1) 容器 computed position === 'fixed'(网页全屏态的标志) 2) 没有原生 fullscreenElement、body 没有 video-fullscreen —— 真·原生全屏要铺满视口,不能拽回播放器里 3) body 没有 player-full-win —— 那是用户**主动**点的网页全屏,尊重用户 站内"自动"切进来时不挂 body class(探针里 body 只有 bili-video-page win), 正好用第 3 条把自动与手动区分开。 若实测手动全屏也被误钉回来,把第 3 条那行 return 删掉即可。 */ function videoPinContainer(el) { const s = el.style; s.setProperty('position', 'absolute', 'important'); s.setProperty('top', '0px', 'important'); s.setProperty('left', '0px', 'important'); s.setProperty('right', 'auto', 'important'); s.setProperty('bottom', 'auto', 'important'); s.setProperty('margin', '0px', 'important'); s.setProperty('transform', 'none', 'important'); s.setProperty('width', '100%', 'important'); s.setProperty('height', '100%', 'important'); s.setProperty('max-width', '100%', 'important'); s.setProperty('max-height', '100%', 'important'); s.setProperty('z-index', 'auto', 'important'); } function videoWebFsSweep() { try { if (!(isVideoPage() || isListPage() || isBangumiPage())) return; if (document.fullscreenElement || document.webkitFullscreenElement) return; const bd = document.body; if (!bd) return; if (bd.classList.contains('video-fullscreen')) return; if (bd.classList.contains('player-full-win')) return; const el = document.querySelector('#playerWrap .bpx-player-container') || document.querySelector('#bilibili-player-wrap .bpx-player-container'); if (!el) return; if (getComputedStyle(el).position !== 'fixed') return; videoPinContainer(el); } catch (e) {} } function bindVideoWebFullscreenGuard() { if (!(isVideoPage() || isListPage() || isBangumiPage())) return; videoWebFsSweep(); [300, 800, 1500, 3000, 5000, 8000, 12000].forEach(function (t) { setTimeout(videoWebFsSweep, t); }); try { let timer = null; const mo = new MutationObserver(function () { if (timer) clearTimeout(timer); timer = setTimeout(videoWebFsSweep, 50); }); mo.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); // 只盯前 15 秒,压住"进页面那一下"就够了,之后别跟着弹幕一直空跑 setTimeout(function () { try { mo.disconnect(); } catch (e) {} }, 15000); } catch (e) {} ['fullscreenchange', 'webkitfullscreenchange'].forEach(function (ev) { document.addEventListener(ev, function () { setTimeout(videoWebFsSweep, 100); }); }); } /* ============================================================ v132.45:迷你播放器(小窗)遮罩层兜底 CSS 已整层 display:none,但站内可能把样式写成行内 !important, 也可能在播放器初始化完成后才把这一层塞进 DOM —— 这里补一道: 扫到"还可见的" mini-warp 时,先点一次右上角的 × 让站内自己退出 小窗状态(顺带把它自己后续的行为也一并收拾干净),再逐项 setProperty important 钉死隐藏,防止下一帧又被写回来。 只在视频页 / 列表页 / 番剧页跑,别处不空转。 ============================================================ */ function miniOverlaySweep() { var wraps = document.querySelectorAll('.bpx-player-mini-warp'); for (var i = 0; i < wraps.length; i++) { var w = wraps[i]; var cs = getComputedStyle(w); if (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0') continue; // 先让站内自己退出小窗:点一次右上角的关闭键 try { var closeBtn = w.querySelector('.bpx-player-mini-close'); if (closeBtn) { closeBtn.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true })); closeBtn.click(); } } catch (e) {} // 再无条件下钉死,防它被写回 var st = w.style; ['display', 'visibility', 'pointer-events', 'opacity'].forEach(function (k, idx) { var v = ['none', 'hidden', 'none', '0'][idx]; st.setProperty(k, v, 'important'); }); var kids = w.querySelectorAll('*'); for (var j = 0; j < kids.length; j++) { kids[j].style.setProperty('display', 'none', 'important'); kids[j].style.setProperty('visibility', 'hidden', 'important'); } } } /* ------------------------------------------------------------ v132.75:视频页推荐列表里的「推广卡」—— 隐藏 CSS 那条按 .video-page-special-card-small 类名已能收掉常规情形, 这里只补一件 CSS 做不到的事:站内若换掉 special 类名(这类推广位 的类名历史上改过多次),类名规则就整体失效,而推广位的**文案特征** 是稳定的 —— 标题末尾带「>>」(探针实测「直击2026诺贝尔奖>>」), 正常推荐视频的标题不会出现这个符号。 做法:扫推荐列表的直接子级卡片,命中就打上稳定的 .bili-video-special-ad, 由样式表负责隐藏(与直播间「点击取消静音」提示同一套路)。 降载:命中即打 data 标记跳过;只看 .rec-list / 推荐列表的直接子级, 不递归整棵推荐树(列表可能有几十张卡,但子级只有几十个 O(1) 判断)。 ------------------------------------------------------------ */ function videoSpecialAdMark() { const lists = document.querySelectorAll('.rec-list, .recommend-list-v1, .recommend-list-v1 > *'); for (let i = 0; i < lists.length; i++) { const kids = lists[i].children; for (let k = 0; k < kids.length; k++) { const el = kids[k]; if (el.nodeType !== 1) continue; if (el.dataset.biliSpecialAdScan === '1') continue; const cls = el.className && typeof el.className === 'string' ? el.className : ''; let hit = false; if (cls && /special|ad-|advert|_ad\b|promo/i.test(cls)) hit = true; if (!hit) { // 文案特征:标题末尾的「>>」(> 可能被转义成 >,取 textContent 已是原文) const title = el.querySelector('.title, .desc, a'); const txt = title ? (title.textContent || '') : (el.textContent || ''); if (txt && txt.indexOf('>>') !== -1) hit = true; } if (hit) { el.classList.add('bili-video-special-ad'); el.dataset.biliSpecialAdScan = '1'; } else { el.dataset.biliSpecialAdScan = '1'; } } } } function bindVideoSpecialAdGuard() { if (!isVideoPage() && !isListPage() && !isBangumiPage()) return; [0, 150, 400, 900, 1800, 3000].forEach(function (t) { setTimeout(videoSpecialAdMark, t); }); registerDomGuard( // 页面上没有推荐列表时一次 querySelector 就返回,几乎零成本 function () { return !!document.querySelector('.rec-list, .recommend-list-v1'); }, videoSpecialAdMark ); } /* ------------------------------------------------------------ v132.76:个性装扮页 —— 隐藏右侧扫码区 + 手机屏适配(JS 兜底) CSS 那组用 [class*="_rightPanel_"] 这类前缀匹配已经能收掉常规情形, 这里只补两件 CSS 做不到 / 可能压不住的事: ① 类名是 CSS Module 哈希(_mainPage_zfm7p_1),将来若改名或去掉语义名, 前缀匹配整体失效;而 #mall-iframe 这个 id 是稳定的, 故按它判定页面并给 body 打标记 bili-garb-page(样式表有对应兜底规则); ② main 是 PC 定宽 715,若被站内写成「行内 !important」则样式表压不过 —— 探测到仍然横向溢出时用 setProperty important 反写。 只按几何量与稳定 id 判断,无事发生时零写入。 ------------------------------------------------------------ */ function garbPageFit() { const iframe = document.getElementById('mall-iframe'); const main = document.querySelector('main[class*="_mainPage_"]'); if (!iframe && !main) return; // 非装扮页:两次廉价查询即返回 if (iframe || main) document.body.classList.add('bili-garb-page'); // ① 右侧扫码区:类名前缀没命中就按"主栏的第二个 section"兜底 if (main) { const secs = main.children; for (let i = 0; i < secs.length; i++) { const s = secs[i]; if (s === main.querySelector('section[class*="_leftPanel_"]')) continue; const cls = (s.className && typeof s.className === 'string') ? s.className : ''; if (/rightPanel/i.test(cls) || (s.querySelector('canvas') && !s.querySelector('iframe'))) { const st = s.style; ['display', 'visibility', 'pointer-events'].forEach(function (k, idx) { st.setProperty(k, ['none', 'hidden', 'none'][idx], 'important'); }); ['width', 'min-width', 'max-width', 'height', 'min-height', 'max-height'].forEach(function (k) { st.setProperty(k, '0', 'important'); }); st.setProperty('margin', '0', 'important'); st.setProperty('padding', '0', 'important'); st.setProperty('border', 'none', 'important'); st.setProperty('overflow', 'hidden', 'important'); } } // ② main 仍横向溢出 → 行内反写 const vw = window.innerWidth || 360; if (main.scrollWidth - vw > 2 || main.getBoundingClientRect().width - vw > 2) { const st = main.style; st.setProperty('width', '100%', 'important'); st.setProperty('max-width', '100%', 'important'); st.setProperty('min-width', '0', 'important'); st.setProperty('display', 'block', 'important'); st.setProperty('overflow-x', 'hidden', 'important'); st.setProperty('box-sizing', 'border-box', 'important'); } // ③ 预览 iframe 拉满(防站内行内写死 375) const frame = iframe || main.querySelector('iframe'); if (frame) { const fr = frame.getBoundingClientRect(); if (fr.width - vw > 2 || fr.left < -2) { const fs = frame.style; fs.setProperty('position', 'absolute', 'important'); fs.setProperty('top', '0', 'important'); fs.setProperty('left', '0', 'important'); fs.setProperty('width', '100%', 'important'); fs.setProperty('max-width', '100%', 'important'); fs.setProperty('height', '100%', 'important'); fs.setProperty('border', 'none', 'important'); fs.setProperty('box-sizing', 'border-box', 'important'); } } } } function bindGarbPageFit() { [0, 150, 400, 900, 1800].forEach(function (t) { setTimeout(garbPageFit, t); }); registerDomGuard( // 装扮页外一次 getElementById + querySelector 就返回,几乎零成本 function () { return !!document.getElementById('mall-iframe') || !!document.querySelector('main[class*="_mainPage_"]'); }, garbPageFit ); } /* ------------------------------------------------------------ v132.77:空间页「投稿 → 视频」底部分页条上下间距钉为 16px(JS 兜底) CSS 那组已把间距收归 .video-footer 的 padding:16px 0, 这里只防"站内把 padding 写成行内 !important 让样式表压不过"。 按实测计算值判断(padding-top / bottom 不等于 16 才写), 已经正确的零写入;页面没有 .video-footer 时一次查询即返回。 ------------------------------------------------------------ */ function spaceVideoFooterPad() { const footers = document.querySelectorAll('.video-footer'); if (!footers.length) return; for (let i = 0; i < footers.length; i++) { const f = footers[i]; let pt = -1, pb = -1; try { const cs = getComputedStyle(f); pt = parseFloat(cs.paddingTop) || 0; pb = parseFloat(cs.paddingBottom) || 0; } catch (e) { continue; } if (Math.abs(pt - 16) < 0.5 && Math.abs(pb - 16) < 0.5) continue; const st = f.style; st.setProperty('padding-top', '16px', 'important'); st.setProperty('padding-bottom', '16px', 'important'); st.setProperty('padding-left', '0', 'important'); st.setProperty('padding-right', '0', 'important'); st.setProperty('margin', '0', 'important'); st.setProperty('box-sizing', 'border-box', 'important'); /* 父容器底部的 16 交给 footer,避免下间距被叠成 32。 v132.86 补充:空间内搜索页(route_search)多出的那 16 不在 div.video 上,而在**再往上一层的 .search-content**(脚本 v131.20 自己加的 padding:0 8px 16px 8px)。只清父级不够, 故改成沿祖先链向上找,命中这几个已知宿主就归零 padding-bottom (最多往上 4 层,够用且不做全树遍历)。 */ const host = f.parentElement; // div.video if (host) host.style.setProperty('padding-bottom', '0', 'important'); let up = host ? host.parentElement : null; for (let k = 0; up && k < 4; k++) { const cn = (up.className && typeof up.className === 'string') ? up.className : ''; if (cn.indexOf('search-content') >= 0 || cn.indexOf('upload-content') >= 0 || cn.indexOf('space-list-details') >= 0) { let hpb = 0; try { hpb = parseFloat(getComputedStyle(up).paddingBottom) || 0; } catch (e) {} if (hpb > 0.5) up.style.setProperty('padding-bottom', '0', 'important'); break; } up = up.parentElement; } } } function bindSpaceVideoFooterPad() { [0, 150, 400, 900, 1800].forEach(function (t) { setTimeout(spaceVideoFooterPad, t); }); registerDomGuard( function () { return !!document.querySelector('.video-footer'); }, spaceVideoFooterPad ); } /* ------------------------------------------------------------ v132.80:关注页 —— 隐藏「新建分组」/ 站内搜索框 + aside 钉 36px(JS 兜底) CSS 那组已能处理常规情形,这里只防两件 CSS 压不过的事: ① 站内把 aside 高度 / 显示写成**行内 !important**(作者样式里的最高 优先级,样式表压不过)→ 用 setProperty important 反写; ② 「新建分组」或搜索框被站内异步重新插入 → 补一次隐藏。 全部按实测计算值判断(高度不等于 36、或元素仍可见才写), 已经正确的零写入;页面没有 .relation-aside 时一次查询即返回。 ------------------------------------------------------------ */ function followAsideFit() { const aside = document.querySelector('.relation-aside'); if (!aside) return; // ① 「新建分组」按键 const create = aside.querySelector('.relation-collapse-create'); if (create) { const cs = getComputedStyle(create); if (cs.display !== 'none' && cs.visibility !== 'hidden') { const st = create.style; ['display', 'visibility', 'pointer-events'].forEach(function (k, idx) { st.setProperty(k, ['none', 'hidden', 'none'][idx], 'important'); }); ['width', 'min-width', 'height', 'min-height', 'max-height'].forEach(function (k) { st.setProperty(k, '0', 'important'); }); st.setProperty('margin', '0', 'important'); st.setProperty('padding', '0', 'important'); st.setProperty('border', 'none', 'important'); st.setProperty('overflow', 'hidden', 'important'); } } // ② 站内搜索框整行(.relation-content-header 是 aside 的兄弟,不在它内部) const heads = document.querySelectorAll( '.relation-content-header, .fav-list-header-filter__search' ); for (let i = 0; i < heads.length; i++) { const h = heads[i]; const cs = getComputedStyle(h); if (cs.display === 'none' || cs.visibility === 'hidden') continue; const st = h.style; ['display', 'visibility', 'pointer-events'].forEach(function (k, idx) { st.setProperty(k, ['none', 'hidden', 'none'][idx], 'important'); }); ['width', 'min-width', 'height', 'min-height', 'max-height'].forEach(function (k) { st.setProperty(k, '0', 'important'); }); st.setProperty('margin', '0', 'important'); st.setProperty('padding', '0', 'important'); st.setProperty('border', 'none', 'important'); st.setProperty('overflow', 'hidden', 'important'); } // ③ aside 钉 36px 高、内容居中(目标间距就是这一个数) let h = 0; try { h = parseFloat(getComputedStyle(aside).height) || 0; } catch (e) {} if (Math.abs(h - 36) > 0.5) { const st = aside.style; st.setProperty('display', 'flex', 'important'); st.setProperty('flex-direction', 'row', 'important'); st.setProperty('justify-content', 'center', 'important'); st.setProperty('align-items', 'center', 'important'); st.setProperty('height', '36px', 'important'); st.setProperty('min-height', '36px', 'important'); st.setProperty('max-height', '36px', 'important'); st.setProperty('padding', '0', 'important'); st.setProperty('margin', '0', 'important'); st.setProperty('overflow', 'hidden', 'important'); st.setProperty('box-sizing', 'border-box', 'important'); } } function bindFollowAsideFit() { [0, 150, 400, 900, 1800].forEach(function (t) { setTimeout(followAsideFit, t); }); registerDomGuard( function () { return !!document.querySelector('.relation-aside'); }, followAsideFit ); } /* ------------------------------------------------------------ v132.81:关注页 —— 隐藏分组胶囊 / UP 主名片浮层(JS 兜底) CSS 那组已能处理常规情形,这里只补三件 CSS 做不到或压不过的事: ① 胶囊容器 .vui_collapse 若被站内异步重新插入 → 补一次隐藏; ② bili-user-profile 是**动态插入**的浮层(点头像才建), 且 z 实测 1004 说明站内的定位很可能是行内 !important, 样式表的 display:none 也可能被同样的方式顶掉 → 用 setProperty important 反写(作者样式里的最高优先级); ③ aside 保持 36px —— 只藏内容、不动容器,若发现 aside 被 站内一起收掉(高度不等于 36)就补回来。 全部按实测计算值判断,已经正确的零写入;不在关注页时一次查询即返回。 ------------------------------------------------------------ */ function followAsideCollapseHide() { const aside = document.querySelector('.relation-aside'); if (!aside) return; // ① 胶囊容器 const cols = aside.querySelectorAll('.vui_collapse'); for (let i = 0; i < cols.length; i++) { const cs = getComputedStyle(cols[i]); if (cs.display === 'none' || cs.visibility === 'hidden') continue; const st = cols[i].style; ['display', 'visibility', 'pointer-events'].forEach(function (k, idx) { st.setProperty(k, ['none', 'hidden', 'none'][idx], 'important'); }); ['width', 'min-width', 'height', 'min-height', 'max-height'].forEach(function (k) { st.setProperty(k, '0', 'important'); }); st.setProperty('margin', '0', 'important'); st.setProperty('padding', '0', 'important'); st.setProperty('border', 'none', 'important'); st.setProperty('overflow', 'hidden', 'important'); } // ② aside 保持 36px(只藏内容、不动容器 —— 这 36 就是卡片距顶的间距) let h = 0; try { h = parseFloat(getComputedStyle(aside).height) || 0; } catch (e) {} if (h > 0 && Math.abs(h - 36) > 0.5) { const st = aside.style; st.setProperty('display', 'flex', 'important'); st.setProperty('flex-direction', 'row', 'important'); st.setProperty('justify-content', 'center', 'important'); st.setProperty('align-items', 'center', 'important'); st.setProperty('height', '36px', 'important'); st.setProperty('min-height', '36px', 'important'); st.setProperty('max-height', '36px', 'important'); st.setProperty('padding', '0', 'important'); st.setProperty('margin', '0', 'important'); st.setProperty('overflow', 'hidden', 'important'); st.setProperty('box-sizing', 'border-box', 'important'); } // ③ UP 主名片浮层(动态插入,挂在 body 直下) const profs = document.querySelectorAll('bili-user-profile'); for (let i = 0; i < profs.length; i++) { const cs = getComputedStyle(profs[i]); if (cs.display === 'none' || cs.visibility === 'hidden') continue; const st = profs[i].style; ['display', 'visibility', 'pointer-events'].forEach(function (k, idx) { st.setProperty(k, ['none', 'hidden', 'none'][idx], 'important'); }); ['width', 'min-width', 'height', 'min-height', 'max-height'].forEach(function (k) { st.setProperty(k, '0', 'important'); }); st.setProperty('margin', '0', 'important'); st.setProperty('padding', '0', 'important'); st.setProperty('border', 'none', 'important'); st.setProperty('overflow', 'hidden', 'important'); } } /* ------------------------------------------------------------ v132.82:关注 / 粉丝页 —— 隐藏卡片右侧「⋮ 竖三点」按键(JS 兜底) CSS 那组已能处理常规情形,这里只补两件 CSS 做不到的事: ① 列表是异步渲染 / 无限滚动的,新卡片插入后补一次隐藏; ② 站内若把尺寸写成行内 !important → setProperty important 反写。 识别方式(不用位置,避免与第一个「关注按钮」混淆): 扫 .relation-card-info-option 的**直接子级**,命中的条件是 · 自身类名含 more_vertical,或 · 内部有 [class*="more_vertical"] 的后代(图标在它里面) 第 1 个 menu-popover 里装的是 .follow-btn__trigger,不含该类名, 所以不会误伤「已关注」按钮。 已隐藏的打 data 标记跳过;不在关注 / 粉丝页时一次查询即返回。 ------------------------------------------------------------ */ function relationCardMoreHide() { const opts = document.querySelectorAll('.relation-card-info-option'); if (!opts.length) return; for (let i = 0; i < opts.length; i++) { const kids = opts[i].children; for (let k = 0; k < kids.length; k++) { const el = kids[k]; if (el.nodeType !== 1) continue; if (el.dataset.biliMoreHidden === '1') continue; const cls = (el.className && typeof el.className === 'string') ? el.className : ''; let hit = /more_vertical/i.test(cls); if (!hit && el.querySelector) { const ic = el.querySelector('[class*="more_vertical"]'); if (ic) hit = true; } if (!hit) continue; el.dataset.biliMoreHidden = '1'; const cs = getComputedStyle(el); if (cs.display === 'none' || cs.visibility === 'hidden') continue; const st = el.style; ['display', 'visibility', 'pointer-events'].forEach(function (kk, idx) { st.setProperty(kk, ['none', 'hidden', 'none'][idx], 'important'); }); ['width', 'min-width', 'max-width', 'height', 'min-height', 'max-height'].forEach(function (kk) { st.setProperty(kk, '0', 'important'); }); st.setProperty('margin', '0', 'important'); st.setProperty('padding', '0', 'important'); st.setProperty('border', 'none', 'important'); st.setProperty('overflow', 'hidden', 'important'); // 图标本身也收掉,防父容器被改结构后留空白 const inner = el.querySelectorAll('[class*="more_vertical"]'); for (let m = 0; m < inner.length; m++) { const ist = inner[m].style; ist.setProperty('display', 'none', 'important'); ist.setProperty('visibility', 'hidden', 'important'); ist.setProperty('width', '0', 'important'); ist.setProperty('height', '0', 'important'); ist.setProperty('margin', '0', 'important'); ist.setProperty('padding', '0', 'important'); ist.setProperty('pointer-events', 'none', 'important'); } } } } function bindFollowCollapseHide() { // 只在关注 / 粉丝页运行 —— 别处的名片是有用功能,不能碰 const onFollow = function () { return !!document.querySelector( 'main.space-main.route_relationFollow, main.space-main.route_relationFans' ); }; if (!onFollow()) return; // v132.82:三个兜底合成一轮 —— 列表是异步渲染 + 无限滚动的, // 分开注册会多出一堆回调,合并后一轮只多几次 querySelector。 const sweep = function () { followAsideCollapseHide(); relationCardMoreHide(); }; [0, 150, 400, 900, 1800].forEach(function (t) { setTimeout(sweep, t); }); registerDomGuard(onFollow, sweep); // 名片是"点头像才建"的,点击后多补几拍 document.addEventListener('click', function () { if (!onFollow()) return; [0, 120, 400].forEach(function (t) { setTimeout(sweep, t); }); }, { passive: true }); // 无限滚动:新卡片插入后补一次(三点按钮随卡片一起生成) window.addEventListener('scroll', function () { if (!onFollow()) return; relationCardMoreHide(); }, { passive: true }); } /* ------------------------------------------------------------ v132.83:首页直播卡(如「直播中 左右开弓」)—— 隐藏三点 + 高度对齐 172(JS 兜底) CSS 那组已能处理常规情形,这里只补两件 CSS 做不到 / 压不过的事: ① __wrap 若被站内写成**行内 !important** 的 absolute,样式表压不过 → 用 setProperty important 反写 position:static(这是"撑开父级"的关键); ② 首页是无限滚动,新卡片插入后补一次隐藏 + 高度校正。 高度判断:以同页**视频卡**的实际高度为基准(不写死 172 —— 万一将来 视频卡高度改了,直播卡会跟着走),量不到视频卡时才退回 172。 已处理过的卡片打 data 标记跳过;页面没有直播卡时一次查询即返回。 ------------------------------------------------------------ */ function liveCardFit() { const cards = document.querySelectorAll('.bili-live-card'); if (!cards.length) return; // 基准高度:同页视频卡的实际高度(量不到再退回 172) let base = 0; const vid = document.querySelector('.bili-video-card'); if (vid) { const r = vid.getBoundingClientRect(); if (r.height > 20) base = Math.round(r.height); } if (!base) base = 172; for (let i = 0; i < cards.length; i++) { const card = cards[i]; // ① 三点按键 const more = card.querySelector('.bili-live-card__info--no-interest'); if (more && more.dataset.biliMoreHidden !== '1') { more.dataset.biliMoreHidden = '1'; const ms = more.style; ['display', 'visibility', 'pointer-events'].forEach(function (k, idx) { ms.setProperty(k, ['none', 'hidden', 'none'][idx], 'important'); }); ['width', 'min-width', 'max-width', 'height', 'min-height', 'max-height'].forEach(function (k) { ms.setProperty(k, '0', 'important'); }); ms.setProperty('margin', '0', 'important'); ms.setProperty('padding', '0', 'important'); ms.setProperty('border', 'none', 'important'); ms.setProperty('overflow', 'hidden', 'important'); } // ② wrap 改回 static —— 不撑开父级是"高度只有 139"的真因 const wrap = card.querySelector('.bili-live-card__wrap'); if (wrap) { let pos = ''; try { pos = getComputedStyle(wrap).position; } catch (e) {} if (pos === 'absolute' || pos === 'fixed') { const ws = wrap.style; ws.setProperty('position', 'static', 'important'); ws.setProperty('top', 'auto', 'important'); ws.setProperty('left', 'auto', 'important'); ws.setProperty('right', 'auto', 'important'); ws.setProperty('bottom', 'auto', 'important'); ws.setProperty('transform', 'none', 'important'); ws.setProperty('z-index', 'auto', 'important'); } /* v132.84:几何量复核 —— 卡片里还藏着一个无类名 div 排在 wrap 前 (探针只输出带类名的节点,查不到它;实测它高 139 = 旧卡片高度)。 把 wrap 改回 static 后它被顶下去 139px,卡片只剩底部 33px 可见。 这里两步兜底(不依赖 CSS 那条"隐藏非 wrap 兄弟"是否生效): a) 卡片内除 wrap 外的 div 兄弟全部隐藏(尺寸/边距/定位归零); b) 若 wrap 顶部仍比卡片顶部低 2px 以上 —— 说明还有别的东西 在占位 —— 直接把 wrap 钉成 absolute 贴回卡片左上角, 并给卡片补显式高度(absolute 不撑高父级,必须显式给)。 判断全部用 rect 差值,不做任何猜测。 */ let dy = 0; try { dy = wrap.getBoundingClientRect().top - card.getBoundingClientRect().top; } catch (e) {} if (dy > 2) { // a) 清掉占位兄弟 const sibs = card.children; for (let k = 0; k < sibs.length; k++) { const sb = sibs[k]; if (sb.nodeType !== 1 || sb === wrap) continue; if ((sb.className && typeof sb.className === 'string' && sb.className.indexOf('bili-live-card__wrap') >= 0)) continue; const ss = sb.style; ['display', 'visibility', 'pointer-events'].forEach(function (kk, idx) { ss.setProperty(kk, ['none', 'hidden', 'none'][idx], 'important'); }); ['width', 'min-width', 'max-width', 'height', 'min-height', 'max-height'].forEach(function (kk) { ss.setProperty(kk, '0', 'important'); }); ss.setProperty('margin', '0', 'important'); ss.setProperty('padding', '0', 'important'); ss.setProperty('border', 'none', 'important'); ss.setProperty('overflow', 'hidden', 'important'); ss.setProperty('position', 'absolute', 'important'); ss.setProperty('float', 'none', 'important'); } // b) 仍被压 → 钉回左上角 let dy2 = 0; try { dy2 = wrap.getBoundingClientRect().top - card.getBoundingClientRect().top; } catch (e) {} if (dy2 > 2) { const ws = wrap.style; ws.setProperty('position', 'absolute', 'important'); ws.setProperty('top', '0', 'important'); ws.setProperty('left', '0', 'important'); ws.setProperty('right', '0', 'important'); ws.setProperty('bottom', 'auto', 'important'); ws.setProperty('width', '100%', 'important'); ws.setProperty('height', '100%', 'important'); ws.setProperty('transform', 'none', 'important'); ws.setProperty('margin', '0', 'important'); // absolute 不撑高父级 → 卡片高度必须显式给 card.style.setProperty('height', base + 'px', 'important'); card.style.setProperty('min-height', base + 'px', 'important'); } } } // ③ 高度对齐:量到了再写,差 2px 以内不动(避免无谓写入) const cr = card.getBoundingClientRect(); if (cr.height > 20 && Math.abs(cr.height - base) > 2) { const info = card.querySelector('.bili-live-card__info'); if (info) { // 封面固定 95,剩下的全给 info const coverEl = card.querySelector('.bili-live-card__image--link'); const coverH = coverEl ? coverEl.getBoundingClientRect().height : 95; const infoH = Math.max(40, Math.round(base - coverH)); const is = info.style; is.setProperty('height', infoH + 'px', 'important'); is.setProperty('min-height', infoH + 'px', 'important'); is.setProperty('max-height', infoH + 'px', 'important'); /* v132.85:上内边距维持 8(与视频卡一致)—— v132.83 曾收到 6 来凑 6+63+8=77,代价是 UP 主行整体上移 2px、比视频卡高。那 2px 改由文字区底部回收: text 定 61(36 标题 + 4 间距 + 21 UP主行), uname 改 block 消掉 inline 基线下方留白。 实测 uname 底 525 = text 顶 464 + 61,正好贴底,不裁。 */ is.setProperty('padding-top', '8px', 'important'); is.setProperty('padding-bottom', '8px', 'important'); const txt = card.querySelector('.bili-live-card__info--text'); if (txt) { const ts = txt.style; ts.setProperty('height', '61px', 'important'); ts.setProperty('min-height', '61px', 'important'); ts.setProperty('max-height', '61px', 'important'); ts.setProperty('margin', '0', 'important'); ts.setProperty('padding', '0', 'important'); ts.setProperty('overflow', 'hidden', 'important'); ts.setProperty('box-sizing', 'border-box', 'important'); } const un = card.querySelector('.bili-live-card__info--uname'); if (un) { const us = un.style; us.setProperty('display', 'block', 'important'); us.setProperty('height', '21px', 'important'); us.setProperty('line-height', '21px', 'important'); us.setProperty('margin-top', '4px', 'important'); us.setProperty('margin-bottom', '0', 'important'); us.setProperty('padding', '0', 'important'); us.setProperty('white-space', 'nowrap', 'important'); us.setProperty('box-sizing', 'border-box', 'important'); } is.setProperty('overflow', 'hidden', 'important'); is.setProperty('box-sizing', 'border-box', 'important'); } // 还差就直接钉卡片本体(站内若给卡片写了固定高度) const cs2 = card.style; cs2.setProperty('height', base + 'px', 'important'); cs2.setProperty('min-height', base + 'px', 'important'); } /* ④ v132.85:UP 主行的**位置**对齐(不只是卡片总高) 基准取同页视频卡的 UP 主行(.bili-video-card__info--bottom) 相对自己卡片顶部的偏移,实测 143(= 95 封面 + 8 + 36 + 4)。 直播卡 .bili-live-card__info--uname 实测 141,高 2px。 差 1px 以上就按几何量反推上内边距并回收文字区高度, 不写死数字 —— 视频卡将来改了,直播卡会跟着走。 */ let vOff = 0; const vc = document.querySelector('.bili-video-card'); if (vc) { const vb = vc.querySelector('.bili-video-card__info--bottom'); if (vb) { try { const d = vb.getBoundingClientRect().top - vc.getBoundingClientRect().top; if (d > 20) vOff = Math.round(d); } catch (e) {} } } if (vOff) { const un = card.querySelector('.bili-live-card__info--uname'); if (un) { let d = 0; let ctop = 0; try { ctop = card.getBoundingClientRect().top; // 重新取,③ 可能已改过布局 d = un.getBoundingClientRect().top - ctop; } catch (e) {} const diff = Math.round(d - vOff); if (Math.abs(diff) >= 1) { const info = card.querySelector('.bili-live-card__info'); if (info) { let pt = 8; try { pt = parseFloat(getComputedStyle(info).paddingTop) || 8; } catch (e) {} const npt = Math.max(0, pt - diff); // 高了就加大上边距 const is = info.style; is.setProperty('padding-top', npt + 'px', 'important'); // 上边多出来的从文字区底部回收,卡片总高不变 const txt = card.querySelector('.bili-live-card__info--text'); if (txt) { const th = Math.max(40, 61 - (npt - 8)); const ts = txt.style; ts.setProperty('height', th + 'px', 'important'); ts.setProperty('min-height', th + 'px', 'important'); ts.setProperty('max-height', th + 'px', 'important'); ts.setProperty('overflow', 'hidden', 'important'); } } } } } } } /* ============================================================ v132.87:搜索页「下一页预渲染占位卡」隐藏兜底 站内 SSR 一次吐 42 个 .video-list-item:前 24 个是本页,后 18 个是多带 to_hide_* 类的「下一页预渲染占位」,由站内响应式规则隐藏为 0x0。 本脚本「列表项两列」那条规则此前把 .to_hide_xs / _md / _xl 也一并 display:block,等于替站内把这 18 个占位卡放了出来 —— 每页变 42 张, 翻到第 2 页自然与前页重复 18 张。CSS 已还原为站内本意(见样式表 v132.87 段),这里只补 CSS 做不到的一件事:换页 / 无限滚动时若站内用 行内 !important 重新插入或改写,用 setProperty important 反写钉死。 判断按 computed display 是否为 none,已隐藏的零写入; 页面上没有 to_hide 元素时一次 querySelector 即返回。 v132.88:选择器从 .video-list 扩到用户页 / 番剧页的 .media-list (用户垂搜页官方 24 个 col_6.col_md_4 + 12 个 to_hide_md.col_6.col_md_4, 容器是 .media-list.row.mt_x40,'video-list' 匹配不到 → 此前完全漏掉)。 用 [class*="-list"] 一次覆盖 media-list / video-list / card-list, 以后站内再换列表容器类名也不至于再次漏掉。 ============================================================ */ const TO_HIDE_SEL = '[class*="-list"] [class*="to_hide"]'; function searchToHideSweep() { if (!document.body) return; if ((document.body.className || '').indexOf('bili-search-page') < 0) return; let items; try { items = document.querySelectorAll(TO_HIDE_SEL); } catch (e) { return; } if (!items.length) return; const PROPS = { display: 'none', visibility: 'hidden', width: '0px', height: '0px', minWidth: '0px', minHeight: '0px', maxWidth: '0px', maxHeight: 'none', margin: '0px', padding: '0px', border: '0px', flex: '0 0 0', float: 'none', opacity: '0', pointerEvents: 'none', position: 'absolute', left: '-99999px', top: '0px', zIndex: '-1' }; for (let i = 0; i < items.length; i++) { const el = items[i]; if (!el || !el.style) continue; let cs; try { cs = getComputedStyle(el); } catch (e) { continue; } if (cs && cs.display === 'none') continue; // 已经是正确的,零写入 for (const k in PROPS) { try { el.style.setProperty( k.replace(/[A-Z]/g, function (m) { return '-' + m.toLowerCase(); }), PROPS[k], 'important'); } catch (e) {} } } } /* v132.88 附带:占位卡隐藏后,「最后一个卡片 margin-bottom → 0」那条 CSS 里的 :last-child 落到了隐藏项上(DOM 里 to_hide 排在末尾),真正可见的 最后一张卡没被归零 → 底部凭空多 16px(用户页 32 vs 8 的老问题复现)。 CSS 表达不了「最后一个不含某类的子元素」(:has 兼容面不够), 这里按「最后一个 visible 子项」用 JS 补一次,只在列表存在时跑。 */ function searchListTailPad() { let lists; try { lists = document.querySelectorAll('.media-list.row, .media-list[class*="mt_x40"]'); } catch (e) { return; } for (let i = 0; i < lists.length; i++) { const kids = lists[i].children; let last = null; for (let j = kids.length - 1; j >= 0; j--) { const k = kids[j]; if ((k.className || '').indexOf('to_hide') >= 0) continue; let cs; try { cs = getComputedStyle(k); } catch (e) { continue; } if (cs && cs.display === 'none') continue; last = k; break; } if (last && last.style) { try { last.style.setProperty('margin-bottom', '0px', 'important'); } catch (e) {} } } } function bindSearchToHideGuard() { [0, 150, 400, 900, 1800].forEach(function (t) { setTimeout(function () { searchToHideSweep(); searchListTailPad(); }, t); }); registerDomGuard( function () { return !!document.querySelector(TO_HIDE_SEL); }, searchToHideSweep ); registerDomGuard( function () { return !!document.querySelector('.media-list.row, .media-list[class*="mt_x40"]'); }, searchListTailPad ); } function bindLiveCardFit() { [0, 150, 400, 900, 1800].forEach(function (t) { setTimeout(liveCardFit, t); }); registerDomGuard( function () { return !!document.querySelector('.bili-live-card'); }, liveCardFit ); // 首页无限滚动:新卡片插入后补一次 window.addEventListener('scroll', function () { liveCardFit(); }, { passive: true }); } function bindMiniOverlayGuard() { if (!isVideoPage() && !isListPage() && !isBangumiPage()) return; [0, 100, 300, 800, 1500, 3000, 6000, 10000].forEach(function (t) { setTimeout(miniOverlaySweep, t); }); try { var mo = new MutationObserver(function () { miniOverlaySweep(); }); mo.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); // 只盯前 15 秒,压住"进页面那一下"就够了 setTimeout(function () { try { mo.disconnect(); } catch (e) {} }, 15000); } catch (e) {} ['fullscreenchange', 'webkitfullscreenchange'].forEach(function (ev) { document.addEventListener(ev, function () { setTimeout(miniOverlaySweep, 100); }); }); } /* ============================================================ v132.47 起:登录弹层适配兜底(v132.48 同补三处) CSS 已按视口宽度重排,但面板是站内 JS 动态插进 body 的, 尺寸可能直接写在行内(样式表的 !important 压不过行内 !important)。 这里补一道:扫到 .bili-mini-content-wp 时逐项 setProperty important 钉死, 并把扫码区、竖分割线一并隐藏。没有弹层时立刻返回,不做无谓开销。 v132.48 三处:① 面板改 display:block + margin:auto(协议区不再被挤成 0 宽、 内容超高时不裁顶);② 每个 .form__item 改 flex + nowrap,input 改 flex:1 1 0, 其余三个零件 flex:0 0 auto + nowrap —— 修掉「密码」「忘记密码?」换行; ③ 顶部 .bili-mini-customer-title(820 定宽 / absolute)改回文档流居中。 ============================================================ */ function loginPanelSweep() { const wp = document.querySelector('.bili-mini-content-wp'); if (!wp) return; const vw = document.documentElement.clientWidth || window.innerWidth || 360; const w = Math.max(240, Math.min(vw - 32, 340)); const st = wp.style; st.setProperty('width', w + 'px', 'important'); st.setProperty('max-width', w + 'px', 'important'); st.setProperty('min-width', '0', 'important'); st.setProperty('left', '0', 'important'); st.setProperty('right', '0', 'important'); st.setProperty('top', 'auto', 'important'); st.setProperty('margin', 'auto', 'important'); st.setProperty('transform', 'none', 'important'); st.setProperty('box-sizing', 'border-box', 'important'); st.setProperty('display', 'block', 'important'); st.setProperty('flex-direction', 'column', 'important'); st.setProperty('max-height', 'calc(100vh - 32px)', 'important'); st.setProperty('overflow-x', 'hidden', 'important'); st.setProperty('overflow-y', 'auto', 'important'); st.setProperty('height', 'auto', 'important'); // v132.48①:表单行 flex 化,救回「密码」与「忘记密码?」 const items = wp.querySelectorAll('.form__item'); for (let i = 0; i < items.length; i++) { const ist = items[i].style; ist.setProperty('display', 'flex', 'important'); ist.setProperty('flex-direction', 'row', 'important'); ist.setProperty('flex-wrap', 'nowrap', 'important'); ist.setProperty('align-items', 'center', 'important'); ist.setProperty('width', '100%', 'important'); ist.setProperty('max-width', '100%', 'important'); ist.setProperty('box-sizing', 'border-box', 'important'); const inp = items[i].querySelector('input'); if (inp) { inp.style.setProperty('flex', '1 1 0', 'important'); inp.style.setProperty('width', 'auto', 'important'); inp.style.setProperty('min-width', '0', 'important'); } const fixed = items[i].querySelectorAll('.form_info, .eye-btn, .clickable'); for (let k = 0; k < fixed.length; k++) { fixed[k].style.setProperty('flex', '0 0 auto', 'important'); fixed[k].style.setProperty('white-space', 'nowrap', 'important'); fixed[k].style.setProperty('max-width', 'none', 'important'); } } // v132.48③:顶部标题(820 定宽 / absolute)回文档流居中 const ctitle = wp.querySelector('.bili-mini-customer-title'); if (ctitle) { const tst = ctitle.style; tst.setProperty('position', 'static', 'important'); tst.setProperty('width', '100%', 'important'); tst.setProperty('max-width', '100%', 'important'); tst.setProperty('left', 'auto', 'important'); tst.setProperty('top', 'auto', 'important'); tst.setProperty('transform', 'none', 'important'); tst.setProperty('text-align', 'center', 'important'); tst.setProperty('margin', '0 0 12px', 'important'); } st.setProperty('padding-top', '52px', 'important'); st.setProperty('padding-right', '16px', 'important'); st.setProperty('padding-bottom', '20px', 'important'); st.setProperty('padding-left', '16px', 'important'); // 扫码区 + 中间竖线:手机用不到 const hide = wp.querySelectorAll('.login-scan-wp, .login-client-qr-code, .bili-mini-line'); for (let i = 0; i < hide.length; i++) { hide[i].style.setProperty('display', 'none', 'important'); hide[i].style.setProperty('visibility', 'hidden', 'important'); } // 关闭键钉回面板右上角 const close = wp.querySelector('.bili-mini-close-icon'); if (close) { const cst = close.style; cst.setProperty('position', 'absolute', 'important'); cst.setProperty('top', '12px', 'important'); cst.setProperty('right', '12px', 'important'); cst.setProperty('left', 'auto', 'important'); } // 协议说明回到文档流 const agree = wp.querySelector('.login-agreement-wp'); if (agree) { const ast = agree.style; ast.setProperty('position', 'static', 'important'); ast.setProperty('width', '100%', 'important'); ast.setProperty('max-width', '100%', 'important'); ast.setProperty('min-width', '0', 'important'); ast.setProperty('left', 'auto', 'important'); ast.setProperty('transform', 'none', 'important'); ast.setProperty('text-align', 'center', 'important'); ast.setProperty('margin', '16px 0 0', 'important'); ast.setProperty('font-size', '12px', 'important'); ast.setProperty('line-height', '18px', 'important'); } } /* ============================================================ 统一 DOM 守卫分发器(单例 MutationObserver) 之前每个兜底各自起一个常驻 MO,再加一条 1.5s 的永久 setInterval, 在弹幕密集的页面里会叠加成一堆回调。这里收敛成**一个** observer: · 只维护一个 debounce 定时器(120ms); · 到点后依次问每个 guard 的 test()(一个廉价的 querySelector), 页面上根本没有目标元素就直接跳过,不做重活; → 视频页既没有登录弹层也没有图片查看器时,一轮的开销 就是两次 querySelector,可以忽略。 ============================================================ */ const DOM_GUARDS = []; let domGuardTimer = null; let domGuardDirty = false; let domGuardInstalled = false; function registerDomGuard(test, run) { DOM_GUARDS.push({ test: test, run: run }); } /* v132.60:播放器"噪音"白名单 —— 播放中这些区域每帧都在改行内 style、 插弹幕节点,DOM 守卫若被它们唤醒,就会在抽屉滑入的那几百毫秒里 反复打断主线程。它们与任何 guard 的目标(登录弹层 / 图片查看器 / PhotoSwipe)都无关,直接判为噪音跳过。 */ const PLAYER_NOISE_SEL = [ '#bilibili-player', '.bpx-player-container', '.bpx-player-video-area', '.bpx-player-control-wrap', '.bpx-player-control-top', '.bpx-player-control-bottom', '.bpx-player-danmaku', '.bili-danmaku-x', '[class*="danmaku"]', '.bilibili-player-video-wrap', '.bpx-player-progress', '.bpx-player-ctrl-time', '.bpx-player-loading', '.screenshot', '.bpx-player-video-info', '.bpx-player-video-perch' ].join(', '); let noiseYes = null, noiseNo = null; try { noiseYes = new WeakSet(); noiseNo = new WeakSet(); } catch (e) { noiseYes = noiseNo = null; } function isPlayerNoise(node) { if (!node || node.nodeType !== 1) return false; // 播放中每条弹幕都会问一次,缓存住结果,别反复走 closest if (noiseYes && noiseYes.has(node)) return true; if (noiseNo && noiseNo.has(node)) return false; let r = false; try { if (typeof node.closest === 'function' && node.closest(PLAYER_NOISE_SEL)) r = true; } catch (e) {} if (noiseYes) (r ? noiseYes : noiseNo).add(node); return r; } function runDomGuards() { for (let i = 0; i < DOM_GUARDS.length; i++) { const g = DOM_GUARDS[i]; try { if (g.test()) g.run(); } catch (e) {} } } function installDomGuardObserver() { if (domGuardInstalled || !DOM_GUARDS.length) return; domGuardInstalled = true; try { const mo = new MutationObserver(function (records) { // v132.60:批量变更(换页 / 整块重绘)不过滤,其余先判是否全是播放器噪音 const n = records && records.length; if (n && n <= 60) { let noise = true; for (let i = 0; i < n; i++) { if (!isPlayerNoise(records[i].target)) { noise = false; break; } } if (noise) return; // 整批都是播放器自己的动静 → 不唤醒守卫 } domGuardDirty = true; if (domGuardTimer) return; domGuardTimer = setTimeout(function () { domGuardTimer = null; if (!domGuardDirty) return; domGuardDirty = false; // v132.60:抽屉动画期间先让路,动画跑完再补一轮 if (bgHeld()) { setTimeout(runDomGuards, 260); return; } runDomGuards(); }, 120); }); mo.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'style'] }); } catch (e) {} } function bindLoginPanelFit() { loginPanelSweep(); // 登录弹层不在页面上时,test() 一次 querySelector 就返回,几乎零成本 registerDomGuard( function () { return !!document.querySelector('.bili-mini-content-wp'); }, loginPanelSweep ); } /* ============================================================ 图片查看器(.bili-album__watch)三件套 v132.49 高亮框跟随 → v132.50 大图翻页箭头 → v132.51 轨道可视区与翻页 ============================================================ */ /* 读元素当前的 translateX(站内用 transform 平移缩略图列表) */ function readTranslateX(el) { try { const tf = getComputedStyle(el).transform; if (!tf || tf === 'none') return 0; if (typeof DOMMatrixReadOnly === 'function') { return new DOMMatrixReadOnly(tf).m41; } const m = tf.match(/matrix\(([^)]+)\)/); if (m) { const parts = m[1].split(','); return parseFloat(parts[parts.length - 2]) || 0; } const m3 = tf.match(/matrix3d\(([^)]+)\)/); if (m3) { const parts = m3[1].split(','); return parseFloat(parts[12]) || 0; } } catch (e) {} return 0; } function albumTrackFindActive(items) { for (let i = 0; i < items.length; i++) { if (items[i].classList && items[i].classList.contains('active')) { return { el: items[i], idx: i }; } } return null; } /* ------------------------------------------------------------ v132.51①:轨道可视窗口对齐到「整数张」缩略图 轨道宽 296(opus)/ 310(动态页),缩略图 54x54、步进 58, 一行放得下 5 张整的,第 6 张必然露半截(315~369 超出 335)。 这里按实测步进把 slider 收到 n*step - gap,让第 6 张不露半张。 只改裁剪窗口、不动 item 尺寸,站内的 translate 步进不受影响。 ------------------------------------------------------------ */ function albumTrackFitWindow(track, items, tr) { const slider = track.querySelector('.bili-album__watch__track__slider'); if (!slider || items.length < 2) return tr.width; const r0 = items[0].getBoundingClientRect(); const r1 = items[1].getBoundingClientRect(); let step = r1.left - r0.left; if (!step || step <= 0) step = r0.width + 4; const gap = Math.max(0, step - r0.width); const n = Math.max(1, Math.floor((tr.width + gap) / step)); const visibleW = Math.max(step, Math.min(tr.width, n * step - gap)); const curW = slider.getBoundingClientRect().width; if (Math.abs(curW - visibleW) > 1) { slider.style.setProperty('width', visibleW + 'px', 'important'); slider.style.setProperty('max-width', visibleW + 'px', 'important'); slider.style.setProperty('overflow', 'hidden', 'important'); slider.style.setProperty('box-sizing', 'border-box', 'important'); } return visibleW; } /* ------------------------------------------------------------ v132.51②:把当前那张滚进可视区 —— 这才是「框住第六张却看不到」的真因 探针:opus 正常,list 是 translateX(-290)(已翻页); 动态页异常,list 是 translateX(0),站内压根没平移, 于是第 6 张仍停在 left=315,落在裁剪区外,高亮框框住的是空位。 这里不猜站内的翻页算法,只做几何判断: active 项不在窗口内 → 按差额平移 list(沿用站内已有的 translateX)。 ------------------------------------------------------------ */ function albumTrackScrollActiveIn(track, items, tr, visibleW, winLeft) { const list = track.querySelector('.bili-album__watch__track__list'); if (!list) return; const hit = albumTrackFindActive(items); if (!hit) return; /* v132.67:原点取「可视窗口的左缘」而不是轨道左缘 —— slider 已被 margin:auto 居中,两者相差半个余量(动态页 12px)。 若仍按 tr.left 判断,边缘那张会被算成"已在窗口内"而不平移, 于是最左 / 最右一张只能露出一部分。 */ const origin = (typeof winLeft === 'number') ? winLeft : tr.left; const sr = { left: origin, right: origin + visibleW }; const ar = hit.el.getBoundingClientRect(); if (!ar.width) return; let delta = 0; if (ar.left < sr.left - 1) delta = sr.left - ar.left; // 在左边,往右推 else if (ar.right > sr.right + 1) delta = sr.right - ar.right; // 在右边,往左拉 else return; // 已在窗口内,不动 const cur = readTranslateX(list); list.style.setProperty('transform', 'translateX(' + (cur + delta) + 'px)', 'important'); } /* ------------------------------------------------------------ v132.51③:两个翻页箭头(track__prev / track__next)钉在轨道内两端 站内给的是 absolute + z=10,位置由它自己算;窄屏下可能落到裁剪区。 这里只在「它本身有尺寸」时校位置 —— 0x0 代表站内主动隐藏 (例如已经是最后一张、没有下一张),不能强行显示,否则点了没反应。 ------------------------------------------------------------ */ function albumTrackPinArrows(track, tr, visibleW, winLeft) { const prev = track.querySelector('.bili-album__watch__track__prev'); const next = track.querySelector('.bili-album__watch__track__next'); /* v132.67:箭头是 track 的 absolute 子元素(包含块 = track), 而窗口(slider)已居中,故要在「相对 track 的坐标」上先补上 窗口相对轨道的偏移 —— 否则箭头会停在轨道两端,与缩略图那一行 错开半个余量(动态页 12px),看起来像"箭头和缩略图对不齐"。 */ const origin = (typeof winLeft === 'number') ? winLeft : tr.left; const off = Math.max(0, origin - tr.left); const pair = [[prev, 'left'], [next, 'right']]; for (let i = 0; i < pair.length; i++) { const el = pair[i][0]; if (!el) continue; const r = el.getBoundingClientRect(); if (!r.width || !r.height) continue; // 站内隐藏中,别动它 const w = r.width; const st = el.style; st.setProperty('position', 'absolute', 'important'); st.setProperty('top', '0', 'important'); st.setProperty('bottom', 'auto', 'important'); st.setProperty('height', r.height + 'px', 'important'); st.setProperty('z-index', '20', 'important'); if (pair[i][1] === 'left') { st.setProperty('left', off + 'px', 'important'); st.setProperty('right', 'auto', 'important'); } else { // 贴可视窗口右缘(而不是轨道右缘),免得落在裁剪后的空白里 st.setProperty('left', Math.max(0, off + visibleW - w) + 'px', 'important'); st.setProperty('right', 'auto', 'important'); } } } /* 高亮框:几何量重算 left = 当前项 rect.left − 轨道 rect.left */ function albumTrackSyncBorder(track, items, tr) { const border = track.querySelector('.bili-album__watch__track__activeitemborder'); if (!border) return; const hit = albumTrackFindActive(items) || { el: items[0] }; const ir = hit.el.getBoundingClientRect(); if (!ir.width || !ir.height) return; const st = border.style; st.setProperty('position', 'absolute', 'important'); st.setProperty('left', (ir.left - tr.left) + 'px', 'important'); st.setProperty('top', (ir.top - tr.top) + 'px', 'important'); st.setProperty('width', (ir.width + 4) + 'px', 'important'); st.setProperty('height', (ir.height + 4) + 'px', 'important'); st.setProperty('transform', 'translate(-2px, -2px)', 'important'); st.setProperty('margin', '0', 'important'); st.setProperty('max-width', 'none', 'important'); } /* 一条轨道的完整处理:窗口 → 滚入 → 箭头 → 高亮框(顺序不能反) */ function albumTrackSyncOne(track) { const tr = track.getBoundingClientRect(); if (!tr.width) return; // 未展开的查看器整条链是 0x0 const items = track.querySelectorAll('.bili-album__watch__track__item'); if (!items.length) return; const visibleW = albumTrackFitWindow(track, items, tr); /* v132.67:窗口(slider)被 margin:auto 居中了,它的左缘才是缩略图那一行的 真正起点。albumTrackFitWindow 刚写完 width,这里 getBoundingClientRect 会触发一次同步布局,拿到的就是居中后的位置。 取不到就退回轨道左缘(等价于旧行为,不会比现在更差)。 */ let winLeft = tr.left; const sld = track.querySelector('.bili-album__watch__track__slider'); if (sld) { const sr = sld.getBoundingClientRect(); if (sr.width) winLeft = sr.left; } albumTrackScrollActiveIn(track, items, tr, visibleW, winLeft); albumTrackPinArrows(track, tr, visibleW, winLeft); albumTrackSyncBorder(track, items, tr); } function albumTrackSync() { const tracks = document.querySelectorAll('.bili-album__watch__track'); for (let i = 0; i < tracks.length; i++) albumTrackSyncOne(tracks[i]); } /* 逐帧跟一段:active 切换与列表横滑都有动画,只跑一次会落后一帧 */ function albumTrackSyncFor(ms) { const nowFn = (window.performance && performance.now) ? function () { return performance.now(); } : function () { return Date.now(); }; const t0 = nowFn(); const step = function () { albumTrackSync(); if (nowFn() - t0 < ms) requestAnimationFrame(step); }; requestAnimationFrame(step); } /* ------------------------------------------------------------ v132.50:大图两侧的翻页箭头(content__prev / __next) 站内这两个是空壳,箭形靠伪元素 / 背景图;窄屏下可能压根没画出来。 不硬画(怕和站内已有的 ::before 重出两个),先探测再按需注入。 ------------------------------------------------------------ */ function albumArrowFixOnce() { const btns = document.querySelectorAll( '.bili-album__watch__content__prev, .bili-album__watch__content__next' ); for (let i = 0; i < btns.length; i++) { const btn = btns[i]; const br = btn.getBoundingClientRect(); if (!br.width || !br.height) continue; if (btn.querySelector('.bili-album-arrow-fix')) continue; let hasOwn = false; try { const kids = btn.querySelectorAll('svg, img, i, span'); for (let k = 0; k < kids.length; k++) { if (kids[k].getBoundingClientRect().width > 0) { hasOwn = true; break; } } if (!hasOwn) { const csB = getComputedStyle(btn, '::before'); const csA = getComputedStyle(btn, '::after'); const cB = csB && csB.content ? csB.content : ''; const cA = csA && csA.content ? csA.content : ''; const bg = (csB && csB.backgroundImage) || (csA && csA.backgroundImage) || ''; if ((cB && cB !== 'none' && cB !== '""' && cB !== "''") || (cA && cA !== 'none' && cA !== '""' && cA !== "''") || (bg && bg !== 'none')) { hasOwn = true; } } } catch (e) {} if (hasOwn) continue; const arrow = document.createElement('div'); arrow.className = 'bili-album-arrow-fix'; btn.appendChild(arrow); } } /* ------------------------------------------------------------ v132.71:相册图片下方多出的空档 —— JS 兜底 CSS 那一组(height:auto)已经能处理常规情形;这里只为防站内把盒高 写成「行内 !important」(内联优先于样式表,CSS 压不过)。 做法:实测盒子与图片的渲染高度,差超过 2px 才把盒高写回图片高度; 没空档就一个字节都不写 —— 正常情况下本函数应当是纯观察、零写入。 只按几何量判断,不猜站内的尺寸算法,也不碰宽度与裁切比例。 ------------------------------------------------------------ */ function albumPreviewTrimGap() { const boxes = document.querySelectorAll('.bili-album__preview.single > .bili-album__preview__picture'); for (let i = 0; i < boxes.length; i++) { const box = boxes[i]; const pic = box.querySelector('picture.b-img__inner') || box.querySelector('img'); if (!pic) continue; const br = box.getBoundingClientRect(); if (!br.height) continue; // 未展开 / 未渲染 const pr = pic.getBoundingClientRect(); if (!pr.height) continue; // 图还没加载出高度,等下一次 if (br.height - pr.height <= 2) continue; // 没有空档,别动它 const h = Math.round(pr.height); const st = box.style; st.setProperty('height', h + 'px', 'important'); st.setProperty('max-height', h + 'px', 'important'); st.setProperty('min-height', '0', 'important'); const wrap = box.querySelector('.bili-album__preview__picture__img'); if (wrap) { wrap.style.setProperty('height', h + 'px', 'important'); wrap.style.setProperty('max-height', h + 'px', 'important'); wrap.style.setProperty('min-height', '0', 'important'); } } } /* ------------------------------------------------------------ v132.72:动态页轮播图平铺 —— JS 兜底 CSS 那一组(overflow:visible + 换行 flex + 3 列等分)已能处理常规情形, 这里只补两件 CSS 压不过的事: ① 站内把 transform / 高度写成「行内 !important」——内联特异性最高, 样式表压不过,只能由 JS 用 setProperty important 反写; ② 懒加载时 img 还没拿到尺寸(探针里 img 是 [201x0]), 盒高塌成 0 → 整行错位。补 aspect-ratio 让盒子先按原始比例占位。 全部按实测几何量判断,不猜站内的尺寸算法;无事发生时零写入。 ------------------------------------------------------------ */ let dynGalleryFlatAt = 0; // 模块级节流:扫描整条轨道不算便宜 function dynGalleryFlatten(force) { const tracks = document.querySelectorAll('.bili-dyn-gallery__track'); if (!tracks.length) return; // 非动态页:一次查询即返回 const now = Date.now(); if (!force && now - dynGalleryFlatAt < 1200) return; dynGalleryFlatAt = now; for (let i = 0; i < tracks.length; i++) { const track = tracks[i]; if (track.dataset.biliDynGalleryFlat === '1') continue; let flatFixed = false; const items = []; const kids = track.children; for (let k = 0; k < kids.length; k++) { const el = kids[k]; if (el.classList && el.classList.contains('bili-dyn-gallery__image')) items.push(el); } if (!items.length) continue; /* ⓪ v132.74:轨道没撑满宿主内容宽 → 整块右偏、左侧留白 探针实测(新的动态页探针10030903.txt):__window [310x425] left=25, 而 track 只有 [222x425] left=113 —— 222 正是 3×71 + 2×4 的内容宽, 右边贴齐 335、左边空出 310 − 222 = 88px,肉眼就是"左侧一大片空白"。 v132.73 只加特异性更高的 width:100% 无效 —— 说明真因不是"宽度声明 被压过",而是**宿主的包含块本身就只有 222**(padding-left 88, 或 grid 的 auto 列宽按内容定型)。故这里改为先修宿主、再修轨道, 并按"宿主内容宽"判断 —— 无事发生时零写入。 */ const host = track.parentElement; if (host) { const hr = host.getBoundingClientRect(); const tr0 = track.getBoundingClientRect(); // 宿主的真实内容宽:减去左右内边距(padding-left 88 就是这种情况) let padL = 0, padR = 0; try { const cs = getComputedStyle(host); padL = parseFloat(cs.paddingLeft) || 0; padR = parseFloat(cs.paddingRight) || 0; } catch (e) {} const inner = hr.width - padL - padR; if (hr.width > 0 && tr0.width > 0 && inner > 0 && (inner - tr0.width > 2 || tr0.left - (hr.left + padL) > 2)) { flatFixed = true; // 本轮动过宽,先别打标记 —— 下轮再复核 // ① 先清宿主:padding / 对齐 / grid 列 —— 这三条才是根因 const hs = host.style; hs.setProperty('padding', '0', 'important'); hs.setProperty('padding-left', '0', 'important'); hs.setProperty('padding-right', '0', 'important'); hs.setProperty('padding-inline', '0', 'important'); hs.setProperty('margin', '0', 'important'); hs.setProperty('display', 'block', 'important'); hs.setProperty('grid-template-columns', 'none', 'important'); hs.setProperty('justify-content', 'flex-start', 'important'); hs.setProperty('justify-items', 'stretch', 'important'); hs.setProperty('align-items', 'stretch', 'important'); // ② 轨道拉满:百分比 + 具体 px 双写(px 兜住"百分比参照系被 // 父级 padding / auto 列宽带偏"这类情况) const st0 = track.style; st0.setProperty('width', '100%', 'important'); st0.setProperty('min-width', '100%', 'important'); st0.setProperty('max-width', '100%', 'important'); st0.setProperty('flex', '1 1 100%', 'important'); st0.setProperty('align-self', 'stretch', 'important'); st0.setProperty('margin', '0', 'important'); st0.setProperty('margin-left', '0', 'important'); st0.setProperty('margin-right', '0', 'important'); st0.setProperty('position', 'static', 'important'); st0.setProperty('left', 'auto', 'important'); st0.setProperty('right', 'auto', 'important'); st0.setProperty('transform', 'none', 'important'); st0.setProperty('box-sizing', 'border-box', 'important'); st0.setProperty('width', Math.round(hr.width) + 'px', 'important'); // ③ 图片也按宿主宽给一次具体 px(3 列 / 4px 间隙), // 万一轨道的百分比仍被算错,图片也不至于再缩回 71 const each = Math.floor((hr.width - 8) / 3); if (each > 20) { for (let k = 0; k < items.length; k++) { const s = items[k].style; s.setProperty('flex', '0 1 ' + each + 'px', 'important'); s.setProperty('width', each + 'px', 'important'); s.setProperty('max-width', each + 'px', 'important'); } } } } // ① 行内 !important 的 transform —— 样式表压不过,只能反写 try { const tf = getComputedStyle(track).transform; if (tf && tf !== 'none' && tf !== 'matrix(1, 0, 0, 1, 0, 0)') { track.style.setProperty('transform', 'none', 'important'); } } catch (e) {} // ② 几何自检:首张仍明显跑到轨道左缘外面 → 位移来自别处,一并清 const tr = track.getBoundingClientRect(); const ir = items[0].getBoundingClientRect(); if (tr.width && ir.width && ir.left < tr.left - 8) { const st = track.style; st.setProperty('transform', 'none', 'important'); st.setProperty('position', 'static', 'important'); st.setProperty('margin-left', '0', 'important'); st.setProperty('left', 'auto', 'important'); for (let k = 0; k < items.length; k++) { const s = items[k].style; s.setProperty('transform', 'none', 'important'); s.setProperty('position', 'relative', 'important'); s.setProperty('left', 'auto', 'important'); s.setProperty('margin', '0', 'important'); } } // ③ 行内 !important 的高度限制 —— 会压过 CSS 的 height:auto,逐个解除 for (let k = 0; k < items.length; k++) { const el = items[k]; const st = el.style; ['height', 'max-height', 'min-height'].forEach(function (prop) { if (!st.getPropertyValue(prop)) return; if (st.getPropertyPriority(prop) !== 'important') return; st.setProperty(prop, prop === 'min-height' ? '0' : (prop === 'height' ? 'auto' : 'none'), 'important'); }); // ④ 懒加载占位:图还没高度时按原始比例先撑住,防整行塌成 0 const img = el.querySelector('img'); if (!img || img.dataset.biliDynFlatAr === '1') continue; const nw = img.naturalWidth, nh = img.naturalHeight; if (!nw || !nh) continue; const r = img.getBoundingClientRect(); if (r.height > 2) continue; img.style.setProperty('aspect-ratio', nw + ' / ' + nh, 'important'); img.style.setProperty('height', 'auto', 'important'); img.dataset.biliDynFlatAr = '1'; } if (!flatFixed) track.dataset.biliDynGalleryFlat = '1'; } } function bindDynGalleryFlat() { dynGalleryFlatten(); // 轮播是异步渲染的,首屏多补几拍,别只靠守卫的一次触发 [0, 150, 400, 900, 1800, 3000].forEach(function (d) { setTimeout(function () { dynGalleryFlatten(true); }, d); }); registerDomGuard( // 页面上没有轮播轨道时一次 querySelector 就返回,几乎零成本 function () { return !!document.querySelector('.bili-dyn-gallery__track'); }, dynGalleryFlatten ); // 图片 load 不冒泡,但捕获阶段能收到 —— 加载完高度才最终确定 document.addEventListener('load', function (e) { const t = e.target; if (t && typeof t.closest === 'function' && t.closest('.bili-dyn-gallery__track')) { dynGalleryFlatten(); } }, true); // 视口 / 容器宽变了 → 3 列的等分宽要重算,标记作废 window.addEventListener('resize', function () { const ts = document.querySelectorAll('.bili-dyn-gallery__track'); for (let i = 0; i < ts.length; i++) ts[i].dataset.biliDynGalleryFlat = ''; dynGalleryFlatten(true); }, { passive: true }); } /* 一次跑完查看器的全部修补 */ function albumGuardsRun() { albumTrackSync(); albumArrowFixOnce(); } function bindAlbumGuards() { albumGuardsRun(); albumPreviewTrimGap(); // 图片 load 不冒泡,但捕获阶段能收到 —— 图加载完高度才最终确定 document.addEventListener('load', function (e) { const t = e.target; if (t && typeof t.closest === 'function' && t.closest('.bili-album__preview')) { albumPreviewTrimGap(); } }, true); registerDomGuard( // 只盯单图预览(多图格子由 CSS 的 height:auto 兜住), // 页面上没有相册时一次 querySelector 就返回,几乎零成本 function () { return !!document.querySelector('.bili-album__preview.single'); }, albumPreviewTrimGap ); registerDomGuard( function () { return !!document.querySelector('.bili-album__watch__track'); }, albumGuardsRun ); // 点缩略图 / 点大图 / 点控件后逐帧跟一段,覆盖动画全程 document.addEventListener('click', function (e) { const t = e.target; if (!t || typeof t.closest !== 'function') return; if (t.closest('.bili-album__preview, .bili-album__watch, .bili-album__watch__track__item')) { albumTrackSyncFor(700); [0, 60, 150, 300, 600].forEach(function (d) { setTimeout(albumArrowFixOnce, d); }); } }, true); window.addEventListener('scroll', function () { if (bgHeld()) return; // v132.60:抽屉动画 / 滑入收尾期不凑热闹 albumTrackSync(); }, { passive: true, capture: true }); } /* ============================================================ v132.52 / v132.53:全屏看图器 PhotoSwipe(.pswp)—— 大图尺寸被写成 0 站内算对了贴合尺寸与居中偏移(zoom-wrap 的 transform 就是居中偏移), 却把 width / height 写成了 0。这里只补尺寸,绝不碰 transform。 v132.53 修正:v132.52 只在 naturalWidth 就绪时才动手,而多图相册里 .pswp__img 常常还没加载(naturalWidth = 0),于是整组图片继续 0x0。 新增第二条取尺寸的通路 —— 从 zoom-wrap 的居中偏移反推: tx = (vw - w) / 2 → w = vw - 2 * tx ty = (vh - h) / 2 → h = vh - 2 * ty 探针验证(异常图片位置2.txt,4 图相册,视口 360x800): zoom-wrap tf = matrix(1,0,0,1,0,282) → w = 360 - 0 = 360,h = 800 - 564 = 236 (v132.52 那张长图:tf = matrix(1,0,0,1,80,0) → 200x800,同样对得上) 即这个偏移本身就是站内按「贴合视口后的尺寸」算出来的, 与 naturalWidth 推导的结果完全一致,但不依赖图片是否加载。 只在出现信箱边(tx 或 ty 有留白)时采信, 避免两个偏移都是 0 时反推出 360x800 这种错误满屏尺寸。 ============================================================ */ /* 读元素的 translate(tx, ty) */ function readTranslateXY(el) { try { const tf = getComputedStyle(el).transform; if (!tf || tf === 'none') return null; if (typeof DOMMatrixReadOnly === 'function') { const m = new DOMMatrixReadOnly(tf); return { x: m.m41, y: m.m42 }; } const m2 = tf.match(/matrix\(([^)]+)\)/); if (m2) { const q = m2[1].split(','); return { x: parseFloat(q[4]) || 0, y: parseFloat(q[5]) || 0 }; } const m3 = tf.match(/matrix3d\(([^)]+)\)/); if (m3) { const q = m3[1].split(','); return { x: parseFloat(q[12]) || 0, y: parseFloat(q[13]) || 0 }; } } catch (e) {} return null; } function pswpImageFit() { const root = document.querySelector('.pswp.pswp--open'); if (!root) return; const vw = root.clientWidth || window.innerWidth || 360; const vh = root.clientHeight || window.innerHeight || 800; if (!vw || !vh) return; // ① 先"替用户碰一下屏幕":PhotoSwipe 自己就监听 window resize 并重算尺寸, // 这正是「触碰一下就正常」的等价触发,且没有任何交互副作用 // (不像派发 tap:灯箱带 pswp--click-to-zoom,点了会缩放甚至关掉)。 // 每个灯箱实例只派发一次,避免反复触发。 if (!root.__biliPswpNudged) { root.__biliPswpNudged = true; try { window.dispatchEvent(new Event('resize')); } catch (e) {} } const imgs = root.querySelectorAll('.pswp__img'); for (let i = 0; i < imgs.length; i++) { const img = imgs[i]; const r = img.getBoundingClientRect(); if (r.width > 0 && r.height > 0) continue; // 站内自己算好了,别多事 const nw = img.naturalWidth; const nh = img.naturalHeight; const zw = img.parentElement; let w = 0, h = 0; if (nw && nh) { // 通路一:按原图比例贴合视口(与 zoom-wrap 的算法一致) const scale = Math.min(vw / nw, vh / nh); w = Math.max(1, Math.round(nw * scale)); h = Math.max(1, Math.round(nh * scale)); } else { const m = zw ? readTranslateXY(zw) : null; if (m && (m.x > 0.5 || m.y > 0.5)) { // 通路二:从 zoom-wrap 的居中偏移反推站内已算好的尺寸 const cw = vw - 2 * m.x; const ch = vh - 2 * m.y; if (cw > 0 && ch > 0) { w = Math.round(cw); h = Math.round(ch); } } else { // 通路三:偏移为 (0,0) 且图未加载 —— 站内的正解就是视口满屏。 // 探针实证(显示不出来.txt vs 触碰后正常.txt,同一张「长图」, // 两份的 zoom-wrap transform 都是 matrix(1,0,0,1,0,0)): // 未触碰 → 0x0 已触碰 → 360x800(正是 vw x vh) // 配合 CSS 的 object-fit:contain,即使比例不同也只是留白、绝不拉伸变形。 w = vw; h = vh; } } if (!w || !h) continue; // 拿不到就跳过等下一次 const st = img.style; st.setProperty('width', w + 'px', 'important'); st.setProperty('height', h + 'px', 'important'); st.setProperty('max-width', 'none', 'important'); st.setProperty('max-height', 'none', 'important'); // zoom-wrap 同步补上(它的 transform 是站内算好的居中偏移,保持不动) if (zw && zw.classList && zw.classList.contains('pswp__zoom-wrap')) { zw.style.setProperty('width', w + 'px', 'important'); zw.style.setProperty('height', h + 'px', 'important'); } } } function pswpImageFitFor(ms) { const nowFn = (window.performance && performance.now) ? function () { return performance.now(); } : function () { return Date.now(); }; const t0 = nowFn(); const step = function () { pswpImageFit(); if (nowFn() - t0 < ms) requestAnimationFrame(step); }; requestAnimationFrame(step); } function bindPswpImageFit() { pswpImageFit(); // 图片 load 不冒泡,但捕获阶段能收到 document.addEventListener('load', function (e) { const t = e.target; if (t && t.classList && t.classList.contains('pswp__img')) { pswpImageFitFor(400); } }, true); // 点「查看大图」以及左右切换后跟一段 document.addEventListener('click', function (e) { const t = e.target; if (!t || typeof t.closest !== 'function') return; if (t.closest('.bili-album__watch__control__option.full-screen') || t.closest('.pswp')) { pswpImageFitFor(900); } }, true); registerDomGuard( // 灯箱 DOM 常驻 body,只看 .pswp__img 会一直命中 → 收紧到打开态 function () { return !!document.querySelector('.pswp.pswp--open .pswp__img'); }, pswpImageFit ); } function start() { installShadowRootPatch(); bindEarlyChromeHide(); // v132.07:能跑的第一刻就藏顶部栏等(先于样式表) bindVideoWebFullscreenGuard(); // v132.44:视频页「自动网页全屏 + 左上偏移」兜底 bindVideoSpecialAdGuard(); // v132.75:隐藏推荐列表里的推广卡(如「直击2026诺贝尔奖>>」) bindGarbPageFit(); // v132.76:个性装扮页隐藏右侧扫码区 + 手机屏适配 bindSpaceVideoFooterPad(); // v132.77:空间页投稿→视频底部分页条上下间距 16px bindSearchToHideGuard(); // v132.87:搜索页隐藏「下一页预渲染占位卡」,每页回到官方 24 张 bindFollowAsideFit(); // v132.80:关注页隐藏「新建分组」/ 站内搜索框 + 卡片距顶 36px bindFollowCollapseHide(); // v132.81:关注页隐藏 5 个分组胶囊 + UP 主名片浮层 bindLiveCardFit(); // v132.83:直播卡隐藏右侧三点 + 高度对齐视频卡 172px bindMiniOverlayGuard(); // v132.45:移除迷你播放器(小窗)占位遮罩层 bindLoginPanelFit(); // v132.47:登录弹层手机屏适配 + 隐藏扫码区 bindAlbumGuards(); // v132.49/50/51:图片查看器(轨道 / 箭头 / 高亮框) bindDynGalleryFlat(); // v132.72:动态页轮播图改为屏内完整平铺 bindPswpImageFit(); // v132.52:全屏看图器 PhotoSwipe 大图尺寸兜底 installDomGuardObserver(); // 统一装载单例 observer(所有 guard 注册完之后) injectStyle(); applyPageClassEarly(); fixViewport(); createSearchBar(); bindLiveFullscreenWatch(); bindLivePlayerSizeWatch(); bindLiveDblclickFullscreen(); bindLiveWebFullscreenGuard(); bindLiveMuteToastGuard(); // v132.57:隐藏直播间「点击取消静音」提示 bindLiveBackExitFullscreen(); bindLiveControllerWatch(); bindLiveLongPressGuard(); bindLiveChatScroll(); bindLiveControllerCompLoop(); const runAll = () => { run(); setTimeout(run, 300); setTimeout(run, 800); setTimeout(run, 1500); setTimeout(run, 3000); setTimeout(run, 5000); setTimeout(run, 8000); setTimeout(run, 12000); }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', runAll); } else { runAll(); } window.addEventListener('load', runAll); /* v132.61:这条 observer 观察整棵 body 的 childList。 视频播放时每秒都有弹幕节点被插入/移除,它就会被反复唤醒, 每次唤醒 → 200ms 后 run() → 全页重扫(见 hideWatchLaterButtons 注释),播放中等于一直在做整页遍历 + 强制布局。 三道闸: ① 播放器噪音过滤 —— 一批变更全落在播放器 / 弹幕 / 控制栏里 就直接不唤醒(超过 60 条视为批量变更,不过滤); ② debounce 200ms → 500ms,并限制两次 run 至少间隔 800ms; ③ 抽屉动画 / 滑动的静默窗口内不跑,延后再补。 */ let lastRunAt = 0; const RUN_GAP = 800; const observer = new MutationObserver(function (records) { const n = records && records.length; if (n && n <= 60) { let noise = true; for (let i = 0; i < n; i++) { if (!isPlayerNoise(records[i].target)) { noise = false; break; } } if (noise) return; // ① 全是播放器自己的动静 } if (observer._t) clearTimeout(observer._t); observer._t = setTimeout(function () { observer._t = null; const gap = Date.now() - lastRunAt; if (bgHeld() || gap < RUN_GAP) { // ②③ 让路 / 限频 observer._t = setTimeout(function () { observer._t = null; if (bgHeld()) return; lastRunAt = Date.now(); run(); }, Math.max(500, RUN_GAP - gap)); return; } lastRunAt = Date.now(); run(); }, 500); }); const observeStart = () => { if (!document.body) { setTimeout(observeStart, 50); return; } observer.observe(document.body, { childList: true, subtree: true }); }; observeStart(); } start(); })();