整理了一下常用的代码片段,需要快速定位问题时,我就会用上这些代码片段。
可以先看一下右侧的目录,再跳转自己需要的部分,并不是开发者工具不能解决问题,这些都是经验的积累,可以方便快速的定位问题,仅供参考。
寻找页面滚动元素
开发了一个横向展示的banner,鼠标移动到上面时,会自动展开当前卡片,折叠其他卡片。自测没问题,提交代码。
但发布测试环境发现banner出现了横向滚动条,于是拉取代码后,发现本地也必现了。但滚动条宽度非常细微。很难定位,需要出现找到滚动条出现的元素。
滚动条出现的原因是内部容器超出外部容器宽度,外部容器设置了overflow: auto/scroll;
、x-overflow: auto/scroll;
js
// 把下边的代码粘贴到浏览器Console中敲回车,然后滚动界面,它会输出滚动的元素
function findScroller(element) {
element.onscroll = function() { console.log(element)}
Array.from(element.children).forEach(findScroller);
}
findScroller(document.body);
只要稍微操作一下,就能找到滚动的元素
假如滚动元素太多,操作不方便,还可以直接用代码找出来
ts
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选项卡,在页面上操作后,也能快速定位滚动区域。

适用场景:当开发者工具不能够定位到影响元素出现滚动的原因时。
高亮所有超出容器的元素(横向或纵向)
ini
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();
适用场景:找出谁撑爆了容器、谁引发了滚动条。
从对象、JSON中查找属性、值
ts
type Obj = Record<string, any> | 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 < obj.length; i++) {
const value = obj[i];
const path = currentPath ? `${currentPath}[${i}]` : `[${i}]`;
if (typeof value === 'object' && value !== null) {
results = results.concat(findPathsWithValues(value, targetKeys, fuzzy, path));
}
}
} else {
for (const key of Object.keys(obj)) {
const value = obj[key];
const path = currentPath ? `${currentPath}.${key}` : key;
if (matchesKey(key)) {
results.push({
path,
value,
isFunction: typeof value === 'function',
});
}
if (typeof value === 'object' && value !== null) {
results = results.concat(findPathsWithValues(value, targetKeys, fuzzy, path));
}
}
}
return results;
}
翻译成js版本Typescript 演练场
使用方法
js
const obj = {
fooBar: 1,
barBaz() { return 'baz'; },
nested: {
fooBaz: 2,
bazQux() { return 'qux'; },
},
};
const exact = findPathsWithValues(obj, ['fooBar', 'barBaz']);
console.log('精确匹配:', exact);
const fuzzy = findPathsWithValues(obj, ['foo', 'baz'], true);
console.log('模糊匹配:', fuzzy);
这个依然有很多好用的工具,比如FeHelper、JSONHero,但这段代码可以在代码中调用,如果找到了值,可以添加添加debugger,可以搭配开发者工具的条件断点来调试。
还可以扩展一下,支持搜索value。
适用场景:需要在代码中调试对象中某个有某个key的情况。
利用 MutationObserver 实时监听 DOM 变化
页面有些动态插入/修改的元素,手动刷新查找不方便,用 MutationObserver 监听DOM树变化,自动输出新元素,定位问题或做调试。
ini
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) { // 元素节点
console.log('新元素插入:', node);
}
});
});
});
observer.observe(document.body, { childList: true, subtree: true });
// 需要停止时:observer.disconnect();
适用场景:动态渲染页面,调试元素何时何地被插入。
快速打印当前组件或DOM树层级结构
页面结构复杂时,想快速看当前元素的层级结构:
javascript
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结构,快速定位元素层级关系。
捕获并记录所有未处理的 Promise 异常
异步代码调试头疼时,用这个捕获全局未处理的 Promise 拒绝,方便快速定位报错位置。
javascript
window.addEventListener('unhandledrejection', event => {
console.error('未处理的Promise拒绝:', event.reason);
});
适用场景:调试异步请求失败,避免静默错误。
利用 Performance API 监测函数执行耗时
定位性能瓶颈,可以用 Performance API 计时,替代 console.time,更灵活。
javascript
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('计算密集型任务', () => {
// ...耗时操作
});
适用场景:精准测量任意函数执行时间,找性能瓶颈。
一键复制对象 JSON,方便快速分享调试数据
console 里打印的对象展开后复制不方便,用这个快速复制对象序列化 JSON。
javascript
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}});
适用场景:快速导出复杂数据结构给同事,便于复现和分析。
通过 CSS Outline 给任意元素添加醒目边框定位
调试页面样式错乱,给页面任意元素临时加轮廓线,方便视觉定位。
ini
function highlight(selector, color = 'red') {
document.querySelectorAll(selector).forEach(el => {
el.style.outline = `2px solid ${color}`;
});
}
// 使用
highlight('.my-banner');
适用场景:定位复杂布局中某些元素,视觉快速排查。
明白了,你要的是真正能在前端开发中快速定位疑难杂症 的调试技巧,不是泛泛的优化建议。我这次只说硬核的、现场能救命的技巧,尤其适合以下场景:
- 页面莫名错位 / 滚动条溢出 / 点击无效 / 样式冲突 / 动画鬼畜等
- 不方便打断点、不知道从哪下手分析
- 需要用非常规方式追踪问题
下面这批调试技巧,工程化 + 浏览器能力 + 元编程技巧,都能让你眼前一亮。
1. 快速可视化所有元素边框
ini
[...document.querySelectorAll('*')].forEach(el => {
el.style.outline = '1px solid rgba(255,0,0,0.2)';
});
排查元素错位、尺寸不对、隐藏溢出、margin collapse 等视觉问题。
也可以把颜色调成rgba(0,255,0,0.2)
代表可见元素,便于观察嵌套结构。
找到元素被谁覆盖了(z-index 或 pointer-events 问题)
javascript
document.addEventListener('click', function (e) {
const topElement = document.elementFromPoint(e.clientX, e.clientY);
console.log('你点到的实际顶层元素是:', topElement);
});
适用场景:点击事件无效 / hover 无响应 / 模态层穿透。
配合 DevTools 中的
$0
(当前选中元素)可以比对是否被盖住。
快速追踪 layout/layout shift(跳动)
定位页面跳动、卡片抖动等问题,利用浏览器的 Layout Shift 调试工具。
或者用代码手动打点 layout:
javascript
new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
console.warn('Layout Shift:', entry);
});
}).observe({ type: 'layout-shift', buffered: true });
打印 CSS 样式变动历史
ini
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);
};
适用场景
Debug 动画:强制显示 transform/opacity 动画的触发点
ini
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 });
});
适用场景:动画触发异常、不触发、提前触发。
实时观察 DOM 结构变化(谁插入了什么?)
javascript
const mo = new MutationObserver(mutations => {
mutations.forEach(m => {
console.log('DOM变动:', m);
});
});
mo.observe(document.body, { childList: true, subtree: true });
适用场景,不知道页面什么位置被插入了元素。
打印任意元素所有监听器(Chrome 限定)
scss
getEventListeners($0) // $0 是 Chrome DevTools 当前选中的元素
适用场景:查看绑定元素事件。