文章

懒人的代码片段

懒人的代码片段

整理了一下常用的代码片段,需要快速定位问题时,我就会用上这些代码片段。

可以先看一下右侧的目录,再跳转自己需要的部分,并不是开发者工具不能解决问题,这些都是经验的积累,可以方便快速的定位问题,仅供参考。

寻找页面滚动元素

开发了一个横向展示的banner,鼠标移动到上面时,会自动展开当前卡片,折叠其他卡片。自测没问题,提交代码。

但发布测试环境发现banner出现了横向滚动条,于是拉取代码后,发现本地也必现了。但滚动条宽度非常细微。很难定位,需要出现找到滚动条出现的元素。

滚动条出现的原因是内部容器超出外部容器宽度,外部容器设置了overflow: auto/scroll;x-overflow: auto/scroll;

1
2
3
4
5
6
7
8
// 把下边的代码粘贴到浏览器Console中敲回车,然后滚动界面,它会输出滚动的元素
function findScroller(element) {
    element.onscroll = function() { console.log(element)}

    Array.from(element.children).forEach(findScroller);
}

findScroller(document.body);

只要稍微操作一下,就能找到滚动的元素

假如滚动元素太多,操作不方便,还可以直接用代码找出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function findScrollElements(selector?: string) {
  const root = selector ? document.querySelector(selector) : document.body;
  if (!root) return [];

  const results: { element: Element; horizontal: boolean; vertical: boolean }[] = [];

  function checkAndTraverse(el: Element) {
    const horizontal = el.scrollWidth > el.clientWidth;
    const vertical = el.scrollHeight > el.clientHeight;

    if (horizontal || vertical) {
      results.push({ element: el, horizontal, vertical });
    }

    // 递归遍历子元素
    el.children && Array.from(el.children).forEach(child => checkAndTraverse(child));
  }

  checkAndTraverse(root);
  return results;
}

// 使用方法
const scrollElements = findScrollElements(); // 不传选择器默认从 body 开始
scrollElements.forEach(({ element, horizontal, vertical }, index) => {
  console.log(`元素 #${index + 1}:`, element);
  console.log('横向滚动条:', horizontal);
  console.log('纵向滚动条:', vertical);
});

翻译成js版本Typescript 演练场

当然也可以用浏览器开发者工具的的Layers和Rendering选项卡,在页面上操作后,也能快速定位滚动区域。

适用场景:当开发者工具不能够定位到影响元素出现滚动的原因时。</p

高亮所有超出容器的元素(横向或纵向)

1
2
3
4
5
6
7
8
9
10
11
12
function highlightOverflow() {
  const all = document.querySelectorAll('*');
  all.forEach(el => {
    if (
      el.scrollWidth > el.clientWidth ||
      el.scrollHeight > el.clientHeight
    ) {
      el.style.outline = '2px dashed orange';
    }
  });
}
highlightOverflow();

适用场景:找出谁撑爆了容器、谁引发了滚动条。</p

从对象、JSON中查找属性、值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
type Obj = Record | any[];

interface FindResult {
  path: string;
  value: any;
  isFunction: boolean;
}

/**
 * 从对象中查找指定属性名或方法名,支持精确和模糊匹配,返回路径和值
 * @param obj 目标对象
 * @param targetKeys 属性或方法名字符串或字符串数组
 * @param fuzzy 是否模糊匹配,默认false(精确匹配)
 * @param currentPath 当前递归路径,外部调用时不传
 */
function findPathsWithValues(
  obj: Obj,
  targetKeys: string | string[],
  fuzzy = false,
  currentPath = ''
): FindResult[] {
  if (obj == null || typeof obj !== 'object') return [];

  const keysToFind = Array.isArray(targetKeys) ? targetKeys : [targetKeys];

  let results: FindResult[] = [];

  const matchesKey = (key: string) => {
    if (fuzzy) {
      return keysToFind.some(tk => key.includes(tk));
    } else {
      return keysToFind.includes(key);
    }
  }

  if (Array.isArray(obj)) {
    for (let i = 0; i  {
  mutations.forEach(mutation => {
    mutation.addedNodes.forEach(node => {
      if (node.nodeType === 1) { // 元素节点
        console.log('新元素插入:', node);
      }
    });
  });
});

observer.observe(document.body, { childList: true, subtree: true });

// 需要停止时:observer.disconnect();

适用场景:动态渲染页面,调试元素何时何地被插入。</p

快速打印当前组件或DOM树层级结构

页面结构复杂时,想快速看当前元素的层级结构:

1
2
3
4
5
6
7
function printDomTree(el = document.body, indent = 0) {
  console.log(' '.repeat(indent * 2) + el.tagName.toLowerCase() + (el.id ? `#${el.id}` : '') + (el.className ? `.${el.className.split(' ').join('.')}` : ''));
  Array.from(el.children).forEach(child => printDomTree(child, indent + 1));
}

// 打印 body 下的DOM树
printDomTree();

适用场景:查看DOM结构,快速定位元素层级关系。</p

捕获并记录所有未处理的 Promise 异常

异步代码调试头疼时,用这个捕获全局未处理的 Promise 拒绝,方便快速定位报错位置。

1
2
3
window.addEventListener('unhandledrejection', event => {
  console.error('未处理的Promise拒绝:', event.reason);
});

适用场景:调试异步请求失败,避免静默错误。</p

利用 Performance API 监测函数执行耗时

定位性能瓶颈,可以用 Performance API 计时,替代 console.time,更灵活。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function profile(name, fn) {
  performance.mark(`${name}-start`);
  const result = fn();
  performance.mark(`${name}-end`);
  performance.measure(name, `${name}-start`, `${name}-end`);
  const measures = performance.getEntriesByName(name);
  console.log(`${name}耗时: ${measures[0].duration.toFixed(2)}ms`);
  performance.clearMarks();
  performance.clearMeasures();
  return result;
}

// 使用
profile('计算密集型任务', () => {
  // ...耗时操作
});

适用场景:精准测量任意函数执行时间,找性能瓶颈。</p

一键复制对象 JSON,方便快速分享调试数据

console 里打印的对象展开后复制不方便,用这个快速复制对象序列化 JSON。

1
2
3
4
5
6
7
8
9
10
11
function copyJson(obj) {
  const json = JSON.stringify(obj, null, 2);
  navigator.clipboard.writeText(json).then(() => {
    console.log('已复制JSON到剪贴板');
  }).catch(() => {
    console.error('复制失败');
  });
}

// 使用
copyJson({foo: 'bar', nested: {a: 1}});

适用场景:快速导出复杂数据结构给同事,便于复现和分析。</p

通过 CSS Outline 给任意元素添加醒目边框定位

调试页面样式错乱,给页面任意元素临时加轮廓线,方便视觉定位。

1
2
3
4
5
6
7
8
function highlight(selector, color = 'red') {
  document.querySelectorAll(selector).forEach(el => {
    el.style.outline = `2px solid ${color}`;
  });
}

// 使用
highlight('.my-banner');

适用场景:定位复杂布局中某些元素,视觉快速排查。</p 明白了,你要的是真正能在前端开发中快速定位疑难杂症的调试技巧,不是泛泛的优化建议。我这次只说硬核的、现场能救命的技巧,尤其适合以下场景:

  • 页面莫名错位 / 滚动条溢出 / 点击无效 / 样式冲突 / 动画鬼畜等- 不方便打断点、不知道从哪下手分析- 需要用非常规方式追踪问题 下面这批调试技巧,工程化 + 浏览器能力 + 元编程技巧,都能让你眼前一亮。

1. 快速可视化所有元素边框

1
2
3
[...document.querySelectorAll('*')].forEach(el => {
  el.style.outline = '1px solid rgba(255,0,0,0.2)';
});

排查元素错位、尺寸不对、隐藏溢出、margin collapse 等视觉问题。</p 也可以把颜色调成 rgba(0,255,0,0.2)</code 代表可见元素,便于观察嵌套结构。</p

找到元素被谁覆盖了(z-index 或 pointer-events 问题)

1
2
3
4
document.addEventListener('click', function (e) {
  const topElement = document.elementFromPoint(e.clientX, e.clientY);
  console.log('你点到的实际顶层元素是:', topElement);
});

适用场景:点击事件无效 / hover 无响应 / 模态层穿透。</p>> <p>配合 DevTools 中的 $0(当前选中元素)可以比对是否被盖住。</p

快速追踪 layout/layout shift(跳动)

定位页面跳动、卡片抖动等问题,利用浏览器的 Layout Shift 调试工具

或者用代码手动打点 layout:

1
2
3
4
5
new PerformanceObserver((list) => {
  list.getEntries().forEach(entry => {
    console.warn('Layout Shift:', entry);
  });
}).observe({ type: 'layout-shift', buffered: true });

打印 CSS 样式变动历史

1
2
3
4
5
6
7
8
9
const target = document.querySelector('#problematic-element');
const originalSetAttribute = target.setAttribute;

target.setAttribute = function (attr, value) {
  if (attr === 'style') {
    console.trace('style 被改动了:', value);
  }
  return originalSetAttribute.call(this, attr, value);
};

适用场景</p

Debug 动画:强制显示 transform/opacity 动画的触发点

1
2
3
4
5
6
7
8
9
10
11
const observer = new MutationObserver(mutations => {
  mutations.forEach(m => {
    if (m.attributeName === 'style') {
      console.log('样式变化:', m.target, m.target.style.cssText);
    }
  });
});

document.querySelectorAll('*').forEach(el => {
  observer.observe(el, { attributes: true });
});

适用场景:动画触发异常、不触发、提前触发。</p

实时观察 DOM 结构变化(谁插入了什么?)

1
2
3
4
5
6
const mo = new MutationObserver(mutations => {
  mutations.forEach(m => {
    console.log('DOM变动:', m);
  });
});
mo.observe(document.body, { childList: true, subtree: true });

适用场景,不知道页面什么位置被插入了元素。</p

打印任意元素所有监听器(Chrome 限定)

1
getEventListeners($0) // $0  Chrome DevTools 当前选中的元素

适用场景:查看绑定元素事件。

本文由作者按照 CC BY 4.0 进行授权