A powerful browser console script to analyze DOM complexity and identify optimization opportunities, particularly useful for Webflow sites experiencing performance issues.
// Quick DOM Analysis Script - Run this in your browser console
(function analyzeDOMStructure() {
console.clear();
console.log('%cπ DOM Analysis Starting...', 'font-size: 20px; color: #3498db; font-weight: bold;');
const allNodes = document.querySelectorAll('*');
const totalNodes = allNodes.length;
// Collect data about each node
const nodeData = Array.from(allNodes).map(node => {
const children = node.children.length;
const descendants = node.querySelectorAll('*').length;
const classes = typeof node.className === 'string'
? node.className.split(' ').filter(c => c)
: node.classList
? Array.from(node.classList)
: [];
return {
element: node,
tag: node.tagName,
id: node.id,
classes: classes,
children: children,
descendants: descendants,
depth: getDepth(node),
isWebflow: classes.some(c => c.startsWith('w-')),
isCollection: classes.some(c => c.includes('collection')),
isSlider: classes.some(c => c.includes('slider')),
isNav: classes.some(c => c.includes('nav')),
};
});
// Helper function to get element depth
function getDepth(element) {
let depth = 0;
let current = element;
while (current.parentElement) {
depth++;
current = current.parentElement;
}
return depth;
}
// Helper to create a selector path
function getPath(element) {
const path = [];
let current = element;
while (current && current !== document.body && path.length < 5) {
let selector = current.tagName.toLowerCase();
if (current.id) {
selector += '#' + current.id;
} else if (current.className) {
const className = typeof current.className === 'string'
? current.className
: (current.classList ? Array.from(current.classList).join(' ') : '');
const classes = className.split(' ').filter(c => c).slice(0, 2);
if (classes.length) selector += '.' + classes.join('.');
}
path.unshift(selector);
current = current.parentElement;
}
return path.join(' > ');
}
// Sort by total descendants (heaviest elements)
// Exclude structural elements that naturally contain everything
const structuralTags = ['HTML', 'BODY', 'MAIN', 'HEAD', 'HEADER', 'FOOTER', 'SECTION', 'ARTICLE', 'ASIDE'];
const heaviest = nodeData
.filter(node => !structuralTags.includes(node.tag))
.sort((a, b) => b.descendants - a.descendants)
.slice(0, 30);
// Find repeated patterns
const repeatedPatterns = {};
nodeData.forEach(node => {
if (node.children > 5) {
const firstChild = node.element.children[0];
if (firstChild) {
const firstChildClass = typeof firstChild.className === 'string'
? firstChild.className
: (firstChild.classList ? Array.from(firstChild.classList).join(' ') : '');
const similarChildren = Array.from(node.element.children)
.filter(child => {
const childClass = typeof child.className === 'string'
? child.className
: (child.classList ? Array.from(child.classList).join(' ') : '');
return childClass === firstChildClass;
}).length;
if (similarChildren / node.children > 0.8) {
const pattern = `${node.tag}.${node.classes.slice(0, 2).join('.')}`;
repeatedPatterns[pattern] = (repeatedPatterns[pattern] || 0) + 1;
}
}
}
});
// Count tags
const tagCounts = {};
nodeData.forEach(node => {
tagCounts[node.tag] = (tagCounts[node.tag] || 0) + 1;
});
// Webflow-specific analysis
const webflowComponents = nodeData.filter(n => n.isWebflow);
const collections = nodeData.filter(n => n.isCollection);
const sliders = nodeData.filter(n => n.isSlider);
const navs = nodeData.filter(n => n.isNav);
// Display results
console.log('\n%cπ Summary', 'font-size: 16px; color: #2c3e50; font-weight: bold;');
console.log(`Total DOM Nodes: ${totalNodes} ${totalNodes > 6000 ? 'β (Over Google limit!)' : totalNodes > 1500 ? 'β οΈ (High)' : 'β
'}`);
console.log(`Google Recommended: 1,500 (You're ${totalNodes - 1500} over)`);
console.log(`Google Upper Limit: 6,000 (You're ${totalNodes > 6000 ? (totalNodes - 6000) + ' over' : (6000 - totalNodes) + ' under'})`);
console.log('\n%cποΈ Top 20 Heaviest Elements (excluding structural tags)', 'font-size: 16px; color: #e74c3c; font-weight: bold;');
console.log('Note: Excluding HTML, BODY, MAIN, HEADER, FOOTER, SECTION, ARTICLE, ASIDE');
console.table(heaviest.slice(0, 20).map(node => ({
'Element': getPath(node.element),
'Tag': node.tag,
'Direct Children': node.children,
'Total Descendants': node.descendants,
'Depth': node.depth,
'% of Total': ((node.descendants / totalNodes) * 100).toFixed(1) + '%'
})));
console.log('\n%cπ Repeated Patterns (Potential for Optimization)', 'font-size: 16px; color: #f39c12; font-weight: bold;');
console.table(Object.entries(repeatedPatterns)
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.map(([pattern, count]) => ({
'Pattern': pattern,
'Instances': count,
'Potential Impact': 'High repetition - consider virtualization or pagination'
})));
console.log('\n%cπ·οΈ Tag Distribution', 'font-size: 16px; color: #9b59b6; font-weight: bold;');
const topTags = Object.entries(tagCounts)
.sort((a, b) => b[1] - a[1])
.slice(0, 10);
console.table(topTags.map(([tag, count]) => ({
'Tag': tag,
'Count': count,
'% of Total': ((count / totalNodes) * 100).toFixed(1) + '%'
})));
console.log('\n%cπ¨ Webflow Components Analysis', 'font-size: 16px; color: #3498db; font-weight: bold;');
console.log(`Webflow Components: ${webflowComponents.length}`);
console.log(`Collection Lists: ${collections.length}`);
console.log(`Sliders: ${sliders.length}`);
console.log(`Navigation Elements: ${navs.length}`);
// Highlight heavy elements
console.log('\n%cπ‘ Quick Actions', 'font-size: 16px; color: #27ae60; font-weight: bold;');
console.log('1. Run this to highlight the heaviest elements:');
console.log(`%chighlightHeavy()`, 'background: #2c3e50; color: white; padding: 4px 8px; border-radius: 4px;');
console.log('2. Highlight specific number of elements:');
console.log(`%chighlightHeavy(20)`, 'background: #2c3e50; color: white; padding: 4px 8px; border-radius: 4px;');
window.highlightHeavy = function(count = 10) {
// Remove previous highlights
document.querySelectorAll('[data-dom-highlight]').forEach(el => {
el.style.outline = '';
el.style.backgroundColor = '';
el.removeAttribute('data-dom-highlight');
});
// Highlight top N heaviest (excluding structural elements)
const elementsToHighlight = Math.min(count, heaviest.length);
heaviest.slice(0, elementsToHighlight).forEach((node, index) => {
node.element.style.outline = `3px solid hsl(${(index * 360/elementsToHighlight)}, 70%, 50%)`;
node.element.style.backgroundColor = `hsla(${(index * 360/elementsToHighlight)}, 70%, 50%, 0.1)`;
node.element.setAttribute('data-dom-highlight', `heavy-${index + 1}`);
});
console.log(`β
Top ${elementsToHighlight} heaviest elements highlighted (excluding html/body/main)! Scroll to see them.`);
return 'Highlighted';
};
// Recommendations
console.log('\n%cπ― Recommendations Based on Analysis', 'font-size: 16px; color: #16a085; font-weight: bold;');
const recommendations = [];
if (totalNodes > 6000) {
recommendations.push('π¨ CRITICAL: Reduce DOM nodes below 6,000 for SEO compliance');
}
if (collections.length > 5) {
recommendations.push('π Consider limiting collection list items or implementing pagination');
}
if (Object.keys(repeatedPatterns).length > 10) {
recommendations.push('β»οΈ Many repeated patterns detected - consider dynamic rendering or virtualization');
}
const deepestNode = Math.max(...nodeData.map(n => n.depth));
if (deepestNode > 20) {
recommendations.push(`ποΈ Very deep nesting detected (${deepestNode} levels) - flatten component structure`);
}
const divPercentage = ((tagCounts['DIV'] || 0) / totalNodes) * 100;
if (divPercentage > 50) {
recommendations.push(`π¦ ${divPercentage.toFixed(0)}% of elements are DIVs - use semantic HTML to reduce wrappers`);
}
if (sliders.length > 3) {
recommendations.push('π Multiple sliders detected - these are DOM-heavy, consider alternatives');
}
recommendations.forEach((rec, index) => {
console.log(`${index + 1}. ${rec}`);
});
console.log('\n%c⨠Analysis Complete!', 'font-size: 18px; color: #2ecc71; font-weight: bold;');
console.log('Tip: Click on any element in the tables above to inspect it in DevTools');
console.log('Results saved to: window.domAnalysisResults');
// Save results for later use
window.domAnalysisResults = {
total: totalNodes,
heaviest: heaviest.slice(0, 20),
patterns: repeatedPatterns,
recommendations: recommendations,
tagCounts: tagCounts,
webflowComponents: {
total: webflowComponents.length,
collections: collections.length,
sliders: sliders.length,
navs: navs.length
}
};
return window.domAnalysisResults;
})();
- Copy the entire script from the snippet below
- Open your website in Chrome/Firefox/Edge
- Open DevTools (F12 or right-click β Inspect)
- Go to the Console tab
- Paste the script and press Enter
- View the comprehensive analysis in your console
- Total Node Count: Compares against Google's recommendations (1,500) and upper limit (6,000)
- Node Distribution: Shows which elements contribute most to DOM bloat
- Nesting Depth: Identifies overly deep component structures
- Lists the top 20 heaviest elements (by total descendants)
- Excludes structural tags (HTML, BODY, MAIN, etc.) for actionable results
- Shows percentage of total DOM each element represents
- Provides exact element paths for easy identification
- Repeated Patterns: Identifies elements with many similar children (candidates for virtualization)
- Webflow Components: Counts collection lists, sliders, navigation elements
- Tag Distribution: Shows overuse of generic tags like DIVs
After running the analysis, use:
highlightHeavy()This will:
- Highlight the top 10 heaviest elements with different colors
- Add semi-transparent backgrounds for easy identification
- Exclude structural elements for actionable insights
The script outputs several formatted tables:
- Heaviest Elements - Shows path, children count, total descendants
- Repeated Patterns - Identifies optimization opportunities
- Tag Distribution - Reveals semantic HTML issues
- Webflow Components - Specific analysis for Webflow sites
Based on your specific DOM structure, the script provides:
- Critical alerts for SEO compliance
- Specific optimization strategies
- Performance improvement suggestions
- Component refactoring advice
- < 1,500: β Excellent (Google's recommendation)
- 1,500 - 3,000:
β οΈ Acceptable but could be optimized - 3,000 - 6,000:
β οΈ High - will impact performance - > 6,000: β Critical - exceeds Google's upper limit
Elements are ranked by total descendants (not just direct children). Focus on:
- Elements with 500+ descendants
- Repeated patterns with 10+ instances
- Components taking >5% of total DOM
Solution: Implement pagination or lazy loading
Solution: Flatten component structure, reduce wrapper divs
Solution: Use semantic HTML (article, section, nav, etc.)
Solution: Consider lighter alternatives or load on-demand
To save the analysis for later:
// After running the main script
copy(JSON.stringify(window.domAnalysisResults, null, 2))Modify the script to analyze different thresholds:
// Change line filtering repeated patterns
if (node.children > 5) { // Adjust this numberFilter results by Webflow components:
// After analysis, filter collection lists
window.domAnalysisResults.heaviest.filter(n => n.isCollection)This script handles SVG and other special elements correctly. If you encounter issues:
- Ensure you're copying the complete script
- Check for browser console errors before running
- Some elements might be hidden or off-screen
- Try scrolling after running
highlightHeavy() - Check if CSS animations are interfering
- Ensure the page is fully loaded before running
- Dynamic content might load after analysis
- Re-run after lazy-loaded content appears
- Run Multiple Times: If your site has dynamic content, run the analysis at different states
- Compare Pages: Run on different pages to identify template-level issues
- Before/After: Use to measure optimization impact
- Save Results: Keep benchmarks to track improvements
- Collection Lists: Each can add 100s of nodes
- Rich Text: Often creates deep nesting
- Interactions: Hidden elements still count
- Components: Reused components multiply DOM impact
Use this script to prove whether the issue is:
- Actually DOM-related (client-side rendering)
- Server processing time (TTFB issues)
- Component complexity in their editor
Found an issue or have an improvement? This script is designed to be modified for your specific needs. Common modifications:
- Add custom pattern detection
- Filter by specific classes
- Export to different formats
- Integrate with performance monitoring
Free to use and modify. Created to help developers optimize their DOM structure and push back on oversimplified performance advice.
Remember: A high DOM count is often a symptom, not the cause. Use this tool to identify what to optimize, but don't forget to address server-side performance issues too.