QQ 浏览器侧边栏导致 resize 事件异常触发的排查与解决

问题背景

在 QQ 浏览器最新版(内核 Chromium 123.0.6312.124)中,监听 window resize 事件时,发现在没有手动拖拽窗口的情况下,切换页面路由或操作 DOM 时,resize 事件被意外触发。


复现条件

  • 浏览器:QQ 浏览器(21.1.8539.400 64位),Chromium 123 内核
  • 操作系统:Windows
  • 必要条件:开启 QQ 浏览器左侧边栏(关闭侧边栏则不复现)
  • 触发操作:切换路由或进行 DOM 增删
window.addEventListener('resize', function() {
  console.log(document.body.clientWidth);
});

排查过程

第一步:初步怀疑滚动条

resize 事件触发时,clientWidth 变化有时是 10px,接近滚动条宽度,初步怀疑是 DOM 切换导致滚动条出现/消失,挤压了页面宽度。

但有时变化只有 1px,且关闭侧边栏后问题完全消失,滚动条假说无法解释全部现象。

第二步:扩大观测范围

resize 回调中同时打印多个宽度指标:

window.addEventListener('resize', function() {
  console.log({
    innerWidth: window.innerWidth,
    clientWidth: document.documentElement.clientWidth,
    bodyClientWidth: document.body.clientWidth,
    bodyOffsetWidth: document.body.offsetWidth,
  });
});

观测结果:

{innerWidth: 1965, clientWidth: 1953, bodyClientWidth: 1953, bodyOffsetWidth: 1953} {innerWidth: 1964, clientWidth: 1952, bodyClientWidth: 1952, bodyOffsetWidth: 1952}

关键发现:innerWidth 也在变化。

innerWidth 是视口宽度,不受页面内容、滚动条影响。它发生变化意味着问题根源在浏览器 UI 层,而非页面本身。

第三步:排除 sub-pixel 取整

怀疑侧边栏导致视口宽度为非整数,布局取整产生 1px 误差。执行验证:

console.log(window.innerWidth, document.documentElement.getBoundingClientRect().width)
// 输出:1964  1952

两者均为整数,排除 sub-pixel 取整的可能。

第四步:确认抖动为来回变化

持续监听 innerWidth,触发一次 DOM 切换后,观察到变化序列为:

1964 → 1965 → 1964

视口宽度短暂变化后弹回原值,确认是抖动行为。


根本原因

QQ 浏览器左侧边栏在开启状态下,页面 DOM 发生变化时,侧边栏会重新进行布局计算,导致自身宽度出现短暂的 1px 或 10px 变化,从而挤压页面视口宽度,触发真实的 resize 事件。抖动结束后视口宽度弹回原值。

这是 QQ 浏览器自身 UI 层的 bug,与页面代码无关,无法从网页侧阻止事件触发。


解决方案

利用"抖动会弹回原值、且幅度极小"的特性,用防抖 + 阈值两层过滤屏蔽噪声:

let lastWidth = window.innerWidth;
let resizeTimer = null;

window.addEventListener('resize', function() {
  clearTimeout(resizeTimer);

  resizeTimer = setTimeout(function() {
    const currentWidth = window.innerWidth;

    // 两层过滤
    if (Math.abs(currentWidth - lastWidth) <= 5) return;

    lastWidth = currentWidth;
    // 真实的窗口大小变化,执行业务逻辑
  }, 150);
}, { passive: true });

两层过滤的分工:

过滤层作用
防抖 150ms等待抖动结束,过滤来回弹动(最终值未变)的噪声
阈值 5px过滤单方向小幅噪声(最终值虽变但幅度极小)

各场景表现:

场景innerWidth 变化最终变化幅度结果
侧边栏 1px 抖动1964 → 1965 → 19640px忽略 ✅
侧边栏 10px 抖动1964 → 1974 → 19640px忽略 ✅
用户拖拽窗口1964 → 1200764px响应 ✅

结论

遇到 resize 事件异常触发时,首先通过同时观测 innerWidthclientWidth 来判断问题根源:

  • 只有 clientWidth 变化 → 页面内部问题(滚动条、布局)
  • innerWidth 也变化 → 浏览器 UI 层问题,页面侧只能做防御性过滤

对于无法修复的浏览器兼容性问题,防抖 + 阈值是通用的防御手段。