Dom Inspector Tool Chrome Extension (2026)

Building a Chrome Extension DOM Inspector Tool

A DOM inspector lets you examine and manipulate the structure of any webpage in real time. While Chrome DevTools provides excellent built-in functionality, building your own extension gives you custom features, tighter workflow integration, and a deeper understanding of how browsers expose the DOM.

This guide walks through creating a functional Chrome extension that inspects DOM elements, displays their properties, and allows real-time attribute editing.

Project Structure

A Chrome extension requires a manifest file and at least one background or content script. For a DOM inspector, you’ll need:

dom-inspector/
 manifest.json
 popup.html
 popup.js
 content.js
 styles.css

The manifest defines permissions and entry points. The content script runs in the context of web pages, while the popup provides the extension’s UI.

Manifest Configuration

Your manifest.json defines the extension’s capabilities:

{
 "manifest_version": 3,
 "name": "Custom DOM Inspector",
 "version": "1.0",
 "description": "Inspect and edit DOM elements with custom tools",
 "permissions": ["activeTab", "scripting"],
 "host_permissions": ["<all_urls>"],
 "action": {
 "default_popup": "popup.html",
 "default_icon": "icon.png"
 },
 "content_scripts": [{
 "matches": ["<all_urls>"],
 "js": ["content.js"],
 "css": ["styles.css"]
 }]
}

The activeTab permission lets you interact with the currently open page. The scripting permission enables programmatic script injection when needed.

Content Script: Element Selection

The content script handles element selection on the page. It listens for messages from the popup and highlights selected elements:

// content.js
let currentElement = null;
let overlay = null;
// Create highlight overlay
function createOverlay() {
 overlay = document.createElement('div');
 overlay.id = 'dom-inspector-overlay';
 overlay.style.cssText = `
 position: absolute;
 border: 2px solid #0066ff;
 background: rgba(0, 102, 255, 0.1);
 pointer-events: none;
 z-index: 2147483647;
 display: none;
 `;
 document.body.appendChild(overlay);
}
function highlightElement(element) {
 if (!overlay) createOverlay();
 
 const rect = element.getBoundingClientRect();
 overlay.style.display = 'block';
 overlay.style.left = `${rect.left + window.scrollX}px`;
 overlay.style.top = `${rect.top + window.scrollY}px`;
 overlay.style.width = `${rect.width}px`;
 overlay.style.height = `${rect.height}px`;
 
 currentElement = element;
}
function clearHighlight() {
 if (overlay) {
 overlay.style.display = 'none';
 }
 currentElement = null;
}
// Handle messages from popup
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
 if (request.action === 'inspect') {
 document.addEventListener('mousemove', handleMouseMove);
 document.addEventListener('click', handleClick, { once: true });
 } else if (request.action === 'stopInspecting') {
 document.removeEventListener('mousemove', handleMouseMove);
 document.removeEventListener('click', handleClick);
 clearHighlight();
 } else if (request.action === 'getElementInfo') {
 if (currentElement) {
 sendResponse({
 tagName: currentElement.tagName,
 id: currentElement.id,
 className: currentElement.className,
 attributes: Array.from(currentElement.attributes).map(attr => ({
 name: attr.name,
 value: attr.value
 })),
 innerHTML: currentElement.innerHTML.substring(0, 200),
 textContent: currentElement.textContent.substring(0, 200)
 });
 }
 }
 return true;
});
function handleMouseMove(event) {
 event.target.style.cursor = 'crosshair';
}
function handleClick(event) {
 event.preventDefault();
 event.stopPropagation();
 
 highlightElement(event.target);
 document.removeEventListener('mousemove', handleMouseMove);
 
 // Notify popup
 chrome.runtime.sendMessage({ 
 action: 'elementSelected',
 element: event.target.tagName 
 });
}

The script creates a visual overlay that follows hovered elements during inspection mode. When you click an element, it captures that element and reports back to the popup.

The popup provides buttons to start inspection and displays element information:

<!-- popup.html -->
<!DOCTYPE html>
<html>
<head>
 <style>
 body { width: 320px; padding: 16px; font-family: system-ui, sans-serif; }
 button {
 width: 100%;
 padding: 10px;
 margin-bottom: 12px;
 background: #0066ff;
 color: white;
 border: none;
 border-radius: 4px;
 cursor: pointer;
 font-size: 14px;
 }
 button:hover { background: #0052cc; }
 button.active { background: #cc0000; }
 .info-section { margin-top: 12px; }
 .info-label { font-weight: bold; font-size: 12px; color: #666; }
 .info-value { font-size: 13px; word-break: break-all; margin-bottom: 8px; }
 .tag { color: #0066ff; font-weight: bold; }
 </style>
</head>
<body>
 <button id="inspectBtn">Start Inspection</button>
 
 <div id="elementInfo" style="display: none;">
 <div class="info-section">
 <div class="info-label">Tag</div>
 <div class="info-value" id="tagName"></div>
 </div>
 <div class="info-section">
 <div class="info-label">ID</div>
 <div class="info-value" id="elementId"></div>
 </div>
 <div class="info-section">
 <div class="info-label">Classes</div>
 <div class="info-value" id="elementClasses"></div>
 </div>
 <div class="info-section">
 <div class="info-label">Attributes</div>
 <div class="info-value" id="elementAttributes"></div>
 </div>
 </div>
 
 <script src="popup.js"></script>
</body>
</html>

The popup script manages the inspection workflow and displays collected element data:

// popup.js
let isInspecting = false;
document.getElementById('inspectBtn').addEventListener('click', async () => {
 const btn = document.getElementById('inspectBtn');
 
 if (!isInspecting) {
 // Start inspection
 isInspecting = true;
 btn.textContent = 'Stop Inspection';
 btn.classList.add('active');
 
 const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
 chrome.tabs.sendMessage(tab.id, { action: 'inspect' });
 } else {
 // Stop inspection
 isInspecting = false;
 btn.textContent = 'Start Inspection';
 btn.classList.remove('active');
 
 const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
 chrome.tabs.sendMessage(tab.id, { action: 'stopInspecting' });
 }
});
// Listen for element selection
chrome.runtime.onMessage.addListener((request) => {
 if (request.action === 'elementSelected') {
 displayElementInfo();
 }
});
async function displayElementInfo() {
 const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
 
 chrome.tabs.sendMessage(tab.id, { action: 'getElementInfo' }, (info) => {
 if (!info) return;
 
 document.getElementById('elementInfo').style.display = 'block';
 document.getElementById('tagName').textContent = `<${info.tagName}>`;
 document.getElementById('elementId').textContent = info.id || '(none)';
 document.getElementById('elementClasses').textContent = info.className || '(none)';
 document.getElementById('elementAttributes').textContent = 
 info.attributes.map(a => `${a.name}="${a.value}"`).join(', ') || '(none)';
 });
}

Loading Your Extension

To test the extension:

  1. Open Chrome and navigate to chrome://extensions/
  2. Enable “Developer mode” in the top right
  3. Click “Load unpacked” and select your extension folder
  4. Navigate to any webpage and click the extension icon
  5. Click “Start Inspection” and hover over elements
  6. Click an element to see its properties

Practical Applications

A custom DOM inspector becomes valuable in several scenarios:

Automated testing verification. Inspect elements to validate that your JavaScript frameworks render the expected structure. Capture element states during test failures.

Accessibility auditing. Quick inspection reveals missing ARIA attributes, improper heading hierarchies, or unlabeled interactive elements.

Debugging dynamic content. Single-page applications often modify the DOM extensively. A custom inspector helps track exactly what changes when interactions occur.

Design system verification. Confirm that components render with expected classes and data attributes across different pages.

Extending the Inspector

Once the base works, consider adding these features:

  • Attribute editing. Modify element attributes directly from the popup and see changes immediately
  • CSS inspection. Display computed styles for selected elements
  • DOM tree navigation. Show parent/child relationships in a collapsible tree view
  • Element search. Find elements by selector or text content
  • Export functionality. Copy element HTML or attributes to clipboard

The Chrome Extensions documentation provides comprehensive details on content scripts, message passing, and storage APIs. Start with the basics, then layer in features as your needs evolve.

Step-by-Step: Building the DOM Inspector

  1. Set up Manifest V3 with activeTab and scripting permissions.
  2. Inject the inspector panel: use chrome.scripting.executeScript to inject a fixed-position sidebar div when the user activates the extension.
  3. Implement element highlighting: on mouseover, draw a semi-transparent overlay around the hovered element with a tooltip showing tag, classes, and dimensions.
  4. Build the element detail panel: on click, display the element’s attribute list, computed styles, and position in the DOM tree.
  5. Add CSS path generation: compute the full CSS selector path to the clicked element with a one-click copy button.
  6. Export selected elements: let users export HTML, computed styles, or XPath to a JSON file for automated test use.

Computing a Unique CSS Selector

function getCSSSelector(element) {
 if (element.id) return '#' + element.id;
 const parts = [];
 let current = element;
 while (current && current !== document.documentElement) {
 let selector = current.tagName.toLowerCase();
 if (current.classList.length > 0) {
 selector += '.' + Array.from(current.classList).join('.');
 } else {
 const parent = current.parentElement;
 if (parent) {
 const index = Array.from(parent.children).indexOf(current) + 1;
 selector += ':nth-child(' + index + ')';
 }
 }
 parts.unshift(selector);
 current = current.parentElement;
 }
 return parts.join(' > ');
}

Comparison with Browser DevTools

Feature This extension Chrome DevTools Firefox Inspector
Always-visible overlay Yes No No
CSS selector copy One-click Manual Manual
Custom export formats Yes Limited Limited
Mobile Chrome Yes Via remote debug No

Advanced: Accessibility Audit Overlay

function auditAccessibility() {
 document.querySelectorAll('img:not([alt])').forEach(img => {
 img.style.outline = '3px solid red';
 });
 document.querySelectorAll('button').forEach(btn => {
 if (!btn.textContent.trim() && !btn.getAttribute('aria-label')) {
 btn.style.outline = '3px solid orange';
 }
 });
}

Troubleshooting

Inspector not injecting on chrome:// URLs: Chrome restricts injection on chrome:// and extension pages. Show a message in the popup explaining that the inspector only works on regular web pages.

Element highlighting flicker: Throttle the mouseover handler with requestAnimationFrame to update the overlay at most once per frame.

CSS selector not unique: Fall back to XPath when document.querySelectorAll(selector).length > 1. Use document.evaluate() to compute the XPath and switch to it automatically.


Try it: Paste your error into our Error Diagnostic for an instant fix.

I'm a solo developer in Vietnam. 50K Chrome extension users. $500K+ on Upwork. 5 Claude Max subscriptions running agent fleets in parallel. These are my actual CLAUDE.md templates, orchestration configs, and prompts. Not a course. Not theory. The files I copy into every project before I write a line of code. **[See what's inside →](https://zovo.one/lifetime?utm_source=ccg&utm_medium=cta-default&utm_campaign=chrome-extension-dom-inspector-tool)** $99 once. Free forever. 47/500 founding spots left.

Related Reading

Built by theluckystrike. More at zovo.one

Find the right skill → Browse 155+ skills in our Skill Finder.