您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
移动浏览器开发者调试工具
当前为
// ==UserScript== // @name 移动端开发者工具 // @namespace http://tampermonkey.net/ // @version 0.1 // @description 移动浏览器开发者调试工具 // @author Your name // @match *://*/* // @grant none // ==/UserScript== (function() { 'use strict'; // 样式定义 const styles = ` .devtools-container { position: fixed; top: 0; left: 0; right: 0; height: 50%; background: #fff; z-index: 10000; border-bottom: 1px solid #ccc; display: none; flex-direction: column; font-family: monospace; transition: transform 0.3s ease; transform: translateY(-100%); } .devtools-container.expanded { transform: translateY(0); } .devtools-header { display: flex; padding: 5px; border-bottom: 1px solid #ccc; background: #f5f5f5; } .devtools-tab { padding: 5px 10px; margin-right: 5px; cursor: pointer; border: 1px solid #ccc; border-radius: 3px; font-size: 14px; } .devtools-tab.active { background: #fff; border-bottom: none; } .devtools-content { flex: 1; overflow: auto; padding: 10px; } .devtools-panel { display: none; height: 100%; } .devtools-panel.active { display: block; } .toggle-devtools { position: fixed; top: 10px; right: 10px; z-index: 10001; padding: 5px 10px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 14px; box-shadow: 0 2px 5px rgba(0,0,0,0.2); } .expand-collapse { position: absolute; bottom: -20px; left: 50%; transform: translateX(-50%); background: #4CAF50; color: white; border: none; border-radius: 0 0 5px 5px; padding: 2px 10px; cursor: pointer; z-index: 10001; } .element-highlight { position: absolute; background: rgba(137, 196, 244, 0.3); border: 1px solid #89C4F4; pointer-events: none; z-index: 9999; } .console-input { width: 100%; padding: 5px; border: 1px solid #ccc; } .console-output { margin: 5px 0; padding: 5px; background: #f5f5f5; border-radius: 3px; } .network-item { padding: 10px; border-bottom: 1px solid #eee; cursor: pointer; margin-bottom: 10px; } .network-details { display: none; padding: 10px; background: #f9f9f9; margin-top: 5px; border-radius: 4px; } .network-item.expanded .network-details { display: block; } .network-summary { padding: 8px; background: #f5f5f5; border-radius: 4px; margin-bottom: 5px; } .network-summary div { margin: 2px 0; } .network-details pre { white-space: pre-wrap; word-wrap: break-word; background: #fff; padding: 8px; border-radius: 4px; border: 1px solid #eee; margin: 5px 0; max-height: 300px; overflow-y: auto; } .network-details h4 { margin: 10px 0 5px 0; color: #333; } .network-item.error { background: #ffe6e6; } .network-details-section { margin: 10px 0; } .network-details-content { max-height: 200px; overflow-y: auto; background: #fff; border: 1px solid #eee; border-radius: 4px; } `; // 添加样式 function addStyle(css) { const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); } // 创建开发者工具UI function createDevToolsUI() { const container = document.createElement('div'); container.className = 'devtools-container'; container.innerHTML = ` <div class="devtools-header"> <div class="devtools-tab" data-panel="elements">元素</div> <div class="devtools-tab" data-panel="console">控制台</div> <div class="devtools-tab active" data-panel="network">网络</div> <div class="devtools-tab" data-panel="styles">样式</div> </div> <div class="devtools-content"> <div class="devtools-panel elements-panel"></div> <div class="devtools-panel console-panel"> <div class="console-output-container"></div> <input type="text" class="console-input" placeholder="输入JavaScript代码"> </div> <div class="devtools-panel network-panel active"></div> <div class="devtools-panel styles-panel"></div> </div> <button class="expand-collapse">收起</button> `; const toggleButton = document.createElement('button'); toggleButton.className = 'toggle-devtools'; toggleButton.textContent = '开发工具'; document.body.appendChild(container); document.body.appendChild(toggleButton); return {container, toggleButton}; } // 元素检查器功能 function initElementInspector(elementsPanel) { let highlight = document.createElement('div'); highlight.className = 'element-highlight'; document.body.appendChild(highlight); function updateHighlight(element) { const rect = element.getBoundingClientRect(); highlight.style.top = rect.top + window.scrollY + 'px'; highlight.style.left = rect.left + window.scrollX + 'px'; highlight.style.width = rect.width + 'px'; highlight.style.height = rect.height + 'px'; } function showElementInfo(element) { elementsPanel.innerHTML = ` <h3>选中的元素:</h3> <pre>${element.outerHTML}</pre> <h3>计算样式:</h3> <pre>${JSON.stringify(window.getComputedStyle(element), null, 2)}</pre> `; } document.addEventListener('mousemove', (e) => { if (!elementsPanel.classList.contains('active')) return; const element = document.elementFromPoint(e.clientX, e.clientY); if (element && !element.closest('.devtools-container')) { updateHighlight(element); highlight.style.display = 'block'; } }); document.addEventListener('click', (e) => { if (!elementsPanel.classList.contains('active')) return; const element = document.elementFromPoint(e.clientX, e.clientY); if (element && !element.closest('.devtools-container')) { e.preventDefault(); showElementInfo(element); } }); } // Console功能 function initConsole(consolePanel) { const input = consolePanel.querySelector('.console-input'); const outputContainer = consolePanel.querySelector('.console-output-container'); function log(message, type = 'log') { const output = document.createElement('div'); output.className = `console-output ${type}`; output.textContent = message; outputContainer.appendChild(output); outputContainer.scrollTop = outputContainer.scrollHeight; } const originalConsole = { log: console.log, error: console.error, warn: console.warn }; console.log = function(...args) { log(args.join(' ')); originalConsole.log.apply(console, args); }; console.error = function(...args) { log(args.join(' '), 'error'); originalConsole.error.apply(console, args); }; console.warn = function(...args) { log(args.join(' '), 'warn'); originalConsole.warn.apply(console, args); }; input.addEventListener('keypress', (e) => { if (e.key === 'Enter') { try { const result = eval(input.value); log(`> ${input.value}`); log(result); } catch (error) { log(error.message, 'error'); } input.value = ''; } }); } // 网络请求监控 function initNetworkMonitor(networkPanel) { const originalFetch = window.fetch; const originalXHR = window.XMLHttpRequest.prototype.open; function formatHeaders(headers) { if (headers instanceof Headers) { return Array.from(headers.entries()) .map(([key, value]) => `${key}: ${value}`) .join('\n'); } return JSON.stringify(headers, null, 2); } window.fetch = async function(...args) { const startTime = Date.now(); const request = args[0]; const options = args[1] || {}; try { const response = await originalFetch.apply(this, args); const duration = Date.now() - startTime; const clone = response.clone(); const item = document.createElement('div'); item.className = 'network-item'; const responseText = await clone.text(); const responseHeaders = formatHeaders(response.headers); const requestHeaders = formatHeaders(options.headers); let formattedResponse = responseText; try { if (response.headers.get('content-type')?.includes('application/json')) { formattedResponse = JSON.stringify(JSON.parse(responseText), null, 2); } } catch (e) { formattedResponse = responseText; } item.innerHTML = ` <div class="network-summary"> <div><strong>URL:</strong> ${request instanceof Request ? request.url : request}</div> <div><strong>方法:</strong> ${options.method || 'GET'}</div> <div><strong>状态:</strong> ${response.status} ${response.statusText}</div> <div><strong>耗时:</strong> ${duration}ms</div> </div> <div class="network-details"> <div class="network-details-section"> <h4>请求头:</h4> <div class="network-details-content"> <pre>${requestHeaders}</pre> </div> </div> <div class="network-details-section"> <h4>响应头:</h4> <div class="network-details-content"> <pre>${responseHeaders}</pre> </div> </div> <div class="network-details-section"> <h4>响应内容:</h4> <div class="network-details-content"> <pre>${formattedResponse}</pre> </div> </div> </div> `; item.querySelector('.network-summary').addEventListener('click', () => { item.classList.toggle('expanded'); }); networkPanel.insertBefore(item, networkPanel.firstChild); return response; } catch (error) { const item = document.createElement('div'); item.className = 'network-item error'; item.innerHTML = ` <div>URL: ${request instanceof Request ? request.url : request}</div> <div>错误: ${error.message}</div> `; networkPanel.insertBefore(item, networkPanel.firstChild); throw error; } }; window.XMLHttpRequest.prototype.open = function(...args) { const startTime = Date.now(); this.addEventListener('load', function() { const duration = Date.now() - startTime; const item = document.createElement('div'); item.className = 'network-item'; const responseHeaders = this.getAllResponseHeaders(); let responseText = this.responseText; try { if (this.getResponseHeader('content-type')?.includes('application/json')) { responseText = JSON.stringify(JSON.parse(responseText), null, 2); } } catch (e) {} item.innerHTML = ` <div class="network-summary"> <div><strong>URL:</strong> ${args[1]}</div> <div><strong>方法:</strong> ${args[0]}</div> <div><strong>状态:</strong> ${this.status} ${this.statusText}</div> <div><strong>耗时:</strong> ${duration}ms</div> </div> <div class="network-details"> <div class="network-details-section"> <h4>响应头:</h4> <div class="network-details-content"> <pre>${responseHeaders}</pre> </div> </div> <div class="network-details-section"> <h4>响应内容:</h4> <div class="network-details-content"> <pre>${responseText}</pre> </div> </div> </div> `; item.querySelector('.network-summary').addEventListener('click', () => { item.classList.toggle('expanded'); }); networkPanel.insertBefore(item, networkPanel.firstChild); }); return originalXHR.apply(this, args); }; } // 样式查看/修改功能 function initStylesPanel(stylesPanel) { function updateStyles() { const styleSheets = Array.from(document.styleSheets); let html = '<h3>页面样式:</h3>'; styleSheets.forEach((sheet, index) => { try { const rules = Array.from(sheet.cssRules); html += `<details> <summary>样式表 ${index + 1}</summary> <pre>${rules.map(rule => rule.cssText).join('\n')}</pre> </details>`; } catch (e) { html += `<p>无法访问样式表 ${index + 1} (CORS限制)</p>`; } }); stylesPanel.innerHTML = html; } updateStyles(); } // 初始化开发者工具 function initDevTools() { addStyle(styles); const {container, toggleButton} = createDevToolsUI(); const elementsPanel = container.querySelector('.elements-panel'); const consolePanel = container.querySelector('.console-panel'); const networkPanel = container.querySelector('.network-panel'); const stylesPanel = container.querySelector('.styles-panel'); const expandCollapseBtn = container.querySelector('.expand-collapse'); // 初始化各个面板功能 initElementInspector(elementsPanel); initConsole(consolePanel); initNetworkMonitor(networkPanel); initStylesPanel(stylesPanel); // 标签切换 container.querySelector('.devtools-header').addEventListener('click', (e) => { if (e.target.classList.contains('devtools-tab')) { const panelName = e.target.dataset.panel; container.querySelectorAll('.devtools-tab').forEach(tab => tab.classList.remove('active')); container.querySelectorAll('.devtools-panel').forEach(panel => panel.classList.remove('active')); e.target.classList.add('active'); container.querySelector(`.${panelName}-panel`).classList.add('active'); } }); // 展开/收起面板 expandCollapseBtn.addEventListener('click', () => { container.classList.toggle('expanded'); expandCollapseBtn.textContent = container.classList.contains('expanded') ? '收起' : '展开'; }); // 显示/隐藏开发者工具 toggleButton.addEventListener('click', () => { container.style.display = container.style.display === 'none' ? 'flex' : 'none'; if (container.style.display === 'flex') { container.classList.add('expanded'); expandCollapseBtn.textContent = '收起'; } }); } // 页面加载完成后初始化 window.addEventListener('load', initDevTools); })();