返回博客

xterm.js 6 的滚动条不是原生的,CSS 全部失效是正常的

在某个内嵌了 Web 终端的桌面应用里,我需要隐藏 xterm.js 的滚动条,然后自己画一根始终可见的条。按照常规思路,先试 CSS——结果花了整整一个小时在一个根本打不到目标的地方反复调。

现象

往终端区域扔了以下所有招数,一条都没用:

/* 尝试 1:webkit 专用伪元素 */
.xterm-viewport::-webkit-scrollbar { display: none; }
.xterm-viewport::-webkit-scrollbar-thumb { background: transparent; }
 
/* 尝试 2:标准属性 */
.xterm-viewport { scrollbar-width: none; }
 
/* 尝试 3:appearance */
.xterm-viewport::-webkit-scrollbar { -webkit-appearance: none; }
 
/* 尝试 4:overflow 截断 */
.xterm-viewport { overflow: hidden; }

在 macOS WKWebView 环境里,那根灰色滚动条依然在滚动时出现,停止滚动后自动淡出——活脱脱一副系统原生 overlay 滚动条(NSScroller)的样子。

根因

这里有一个反直觉的事实:xterm.js 6.0 完全重写了滚动条,改用自己的 DOM 节点来渲染,根本不是浏览器原生滚动条。

用 DevTools 展开终端节点,实际结构是这样的:

<div
  role="presentation"
  aria-hidden="true"
  class="invisible scrollbar vertical fade"
  style="position:absolute; width:14px; height:735px; right:0; top:0;"
>
  <div
    class="slider"
    style="position:absolute; top:431px; height:289px; width:14px;
           transform:translate3d(0,0,0); contain:strict;"
  ></div>
</div>
  • 外层是 div.scrollbar.vertical.fade,有一个 JS controller 负责在滚动时移除 invisible 类、停止后再加回去并触发 fade 动画——这就是「滚动时出现、停止后消失」的根源,和系统 overlay 表现一模一样,却完全是两码事。
  • 内层 div.slider 是滚动条拇指(thumb),通过 transform + contain:strict 定位。

因为它是普通 <div>,所有针对原生/WebKit 滚动条的 CSS 机制(::-webkit-scrollbarscrollbar-widthoverflow-webkit-appearance)完全看不见它。

坑:macOS flash-on-scroll 不一定是 NSScroller

看到「滚动时出现、停止后消失」的灰色条,第一直觉是 macOS overlay 滚动条,然后开始在 ::-webkit-scrollbar / overflow / scrollbar-width 这几个工具里反复试——但 xterm.js 6 的这根条跟系统完全无关,这些工具的作用域根本不覆盖它,花再多时间也是零效果。

正解

先用 DevTools 右键 → Inspect 确认是 div.scrollbar > div.slider,然后直接对着这两个类打:

/* 隐藏 xterm 6 自渲染的滚动条(限定容器,不影响其他地方) */
.term-container .scrollbar,
.term-container .slider {
  display: none !important;
}

如果还要防止非 macOS 平台出现原生滚动条:

/* 可选:抑制 viewport 上可能存在的原生滚动条 */
.term-container .xterm-viewport {
  overflow: hidden;
}

这不会破坏滚轮滚动——xterm 6 的滚轮事件走的是 onRequestScrollLines → scrollLines(buffer ydisp),和 DOM scrollTop 无关。

如果要换成自己的始终可见滚动条,思路是:监听 term.onScroll / term.onRender,从 term.buffer.active.baseYterm.buffer.active.viewportYterm.rows 三个值算出比例,然后用 term.scrollToLine(n) 响应拖拽。

正解:用 display:none 直接打 .scrollbar / .slider

不要碰 ::-webkit-scrollbar 系列。找到 div.scrollbar > div.slider 直接 display: none !important,隐藏立即生效,两行 CSS 解决。

一句话外卖

滚动条 CSS 全部失效时,先 Inspect Element——现代库(xterm 6、各类虚拟列表/Grid 库)越来越多地自己渲染 DOM 滚动条,原生 CSS 机制对它们毫无作用,一次检查胜过无数轮猜测。