// ==UserScript==
// @name 元素层级网页截图工具
// @namespace http://tampermonkey.net/
// @version 0.4
// @description 网页内容元素级选择,可以进行编辑 | 支持窗口拖动 | 截图不包含工具自身
// @author wilsend
// @match *://*/*
// @grant GM_registerMenuCommand
// @require https://html2canvas.hertzen.com/dist/html2canvas.min.js
// ==/UserScript==
(function() {
'use strict';
// 全局变量存储UI元素引用
let uiContainer = null;
let closeButton = null;
let elementInput = null;
let inputConfirmBtn = null;
// 编辑窗口相关变量
let editWindow = null;
let editContentContainer = null;
let removeElemBtn = null;
let saveEditBtn = null;
let cancelEditBtn = null;
let isEditing = false;
let isRemovingElem = false;
let editHighlight = null;
// 存储编辑时的元素副本和原始元素
let editedElementClone = null;
let originalSelectedElement = null;
// 存储页面原始样式表(用于保留样式)
let originalStylesheets = [];
// 拖动相关变量
let isDragging = false;
let dragOffsetX = 0;
let dragOffsetY = 0;
let hasDragged = false;
// 创建操作界面元素
function createUI() {
if (document.getElementById('element-screenshot-tool')) {
uiContainer = document.getElementById('element-screenshot-tool');
closeButton = uiContainer.querySelector('#close-btn');
elementInput = uiContainer.querySelector('#element-input');
inputConfirmBtn = uiContainer.querySelector('#input-confirm-btn');
return uiContainer;
}
// 主容器(添加忽略截图属性)
const container = document.createElement('div');
container.id = 'element-screenshot-tool';
container.setAttribute('data-html2canvas-ignore', 'true'); // 核心:截图忽略
container.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
z-index: 999999;
background: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 15px rgba(0,0,0,0.2);
max-width: 300px;
font-family: Arial, sans-serif;
cursor: move;
user-select: none;
`;
// 标题
const title = document.createElement('h3');
title.textContent = '任意元素截图工具';
title.style.marginTop = '0';
title.style.color = '#333';
// 状态显示
const status = document.createElement('div');
status.id = 'screenshot-status';
status.textContent = '请选择一个操作';
status.style.padding = '8px 0';
status.style.color = '#666';
status.style.fontSize = '14px';
// 手动输入元素区域
const inputArea = document.createElement('div');
inputArea.style.cssText = 'margin: 10px 0; display: flex; gap: 8px;';
const inputLabel = document.createElement('label');
inputLabel.textContent = '手动输入元素:';
inputLabel.style.fontSize = '12px';
inputLabel.style.alignSelf = 'center';
elementInput = document.createElement('input');
elementInput.id = 'element-input';
elementInput.type = 'text';
elementInput.placeholder = '标签名或#ID(例:div、#header)';
elementInput.style.flex = '1';
elementInput.style.padding = '6px';
elementInput.style.border = '1px solid #ddd';
elementInput.style.borderRadius = '4px';
elementInput.style.fontSize = '12px';
elementInput.style.cursor = 'text';
inputConfirmBtn = document.createElement('button');
inputConfirmBtn.id = 'input-confirm-btn';
inputConfirmBtn.textContent = '确认';
inputConfirmBtn.style.padding = '6px 12px';
inputConfirmBtn.style.backgroundColor = '#4285f4';
inputConfirmBtn.style.color = 'white';
inputConfirmBtn.style.border = 'none';
inputConfirmBtn.style.borderRadius = '4px';
inputConfirmBtn.style.cursor = 'pointer';
inputConfirmBtn.style.fontSize = '12px';
inputArea.appendChild(inputLabel);
inputArea.appendChild(elementInput);
inputArea.appendChild(inputConfirmBtn);
// 操作提示
const hint = document.createElement('div');
hint.style.cssText = `
font-size: 12px;
color: #888;
margin: 10px 0;
padding: 5px;
background: #f5f5f4;
border-radius: 4px;
`;
hint.innerHTML = '操作提示:
[ 扩大选择(父级)
] 缩小选择(子级)
回车/点击确认 | ESC取消';
// 按钮容器
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex';
buttonContainer.style.gap = '8px';
buttonContainer.style.marginBottom = '10px';
const selectButton = document.createElement('button');
selectButton.textContent = '鼠标选择元素';
selectButton.style.flex = '1';
selectButton.style.padding = '8px';
selectButton.style.backgroundColor = '#4285f4';
selectButton.style.color = 'white';
selectButton.style.border = 'none';
selectButton.style.borderRadius = '4px';
selectButton.style.cursor = 'pointer';
selectButton.id = 'select-element-button';
const editButton = document.createElement('button');
editButton.textContent = '编辑元素';
editButton.id = 'edit-element-button';
editButton.style.flex = '1';
editButton.style.padding = '8px';
editButton.style.backgroundColor = '#fbbc05';
editButton.style.color = 'white';
editButton.style.border = 'none';
editButton.style.borderRadius = '4px';
editButton.style.cursor = 'pointer';
editButton.disabled = true;
const captureButton = document.createElement('button');
captureButton.textContent = '保存截图';
captureButton.id = 'capture-button';
captureButton.style.flex = '1';
captureButton.style.padding = '8px';
captureButton.style.backgroundColor = '#34a853';
captureButton.style.color = 'white';
captureButton.style.border = 'none';
captureButton.style.borderRadius = '4px';
captureButton.style.cursor = 'pointer';
captureButton.disabled = true;
// 已选元素信息
const selectedInfo = document.createElement('div');
selectedInfo.id = 'selected-element-info';
selectedInfo.style.margin = '10px 0';
selectedInfo.style.padding = '8px';
selectedInfo.style.border = '1px dashed #ddd';
selectedInfo.style.borderRadius = '4px';
selectedInfo.style.fontSize = '12px';
selectedInfo.style.color = '#666';
selectedInfo.textContent = '未选择任何元素';
// 关闭按钮
const closeBtn = document.createElement('button');
closeBtn.id = 'close-btn';
closeBtn.textContent = '关闭';
closeBtn.style.width = '100%';
closeBtn.style.padding = '8px';
closeBtn.style.backgroundColor = '#ea4335';
closeBtn.style.color = 'white';
closeBtn.style.border = 'none';
closeBtn.style.borderRadius = '4px';
closeBtn.style.cursor = 'pointer';
// 组装界面
buttonContainer.appendChild(selectButton);
buttonContainer.appendChild(editButton);
buttonContainer.appendChild(captureButton);
container.appendChild(title);
container.appendChild(status);
container.appendChild(inputArea);
container.appendChild(hint);
container.appendChild(buttonContainer);
container.appendChild(selectedInfo);
container.appendChild(closeBtn);
document.body.appendChild(container);
// 更新全局引用
uiContainer = container;
closeButton = closeBtn;
// 绑定拖动事件
bindDragEvents();
return container;
}
// 绑定窗口拖动事件
function bindDragEvents() {
const container = uiContainer;
container.addEventListener('mousedown', (e) => {
if (e.target.tagName === 'BUTTON' || e.target.tagName === 'INPUT') return;
isDragging = true;
hasDragged = false;
dragOffsetX = e.clientX - container.getBoundingClientRect().left;
dragOffsetY = e.clientY - container.getBoundingClientRect().top;
container.style.opacity = '0.8';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
hasDragged = true;
const left = e.clientX - dragOffsetX;
const top = e.clientY - dragOffsetY;
container.style.left = `${left}px`;
container.style.top = `${top}px`;
container.style.right = 'auto';
});
document.addEventListener('mouseup', (e) => {
if (isDragging) {
isDragging = false;
container.style.opacity = '1';
if (hasDragged) {
e.preventDefault();
e.stopPropagation();
}
}
});
}
// 创建全屏编辑窗口
function createEditWindow() {
if (editWindow) return editWindow;
editWindow = document.createElement('div');
editWindow.id = 'edit-element-window';
editWindow.setAttribute('data-html2canvas-ignore', 'true'); // 截图忽略
editWindow.style.cssText = `
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: white;
z-index: 9999999;
overflow: auto;
display: none;
`;
const editToolbar = document.createElement('div');
editToolbar.style.cssText = `
position: sticky;
top: 0;
background: #333;
padding: 12px 20px;
display: flex;
gap: 10px;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
z-index: 10;
`;
removeElemBtn = document.createElement('button');
removeElemBtn.textContent = '移除元素';
removeElemBtn.style.padding = '8px 16px';
removeElemBtn.style.backgroundColor = '#ea4335';
removeElemBtn.style.color = 'white';
removeElemBtn.style.border = 'none';
removeElemBtn.style.borderRadius = '4px';
removeElemBtn.style.cursor = 'pointer';
const removeHint = document.createElement('span');
removeHint.id = 'remove-mode-hint';
removeHint.textContent = '';
removeHint.style.color = 'white';
removeHint.style.marginLeft = '10px';
removeHint.style.alignSelf = 'center';
removeHint.style.fontSize = '14px';
saveEditBtn = document.createElement('button');
saveEditBtn.textContent = '保存编辑并截图';
saveEditBtn.style.padding = '8px 16px';
saveEditBtn.style.backgroundColor = '#34a853';
saveEditBtn.style.color = 'white';
saveEditBtn.style.border = 'none';
saveEditBtn.style.borderRadius = '4px';
saveEditBtn.style.cursor = 'pointer';
cancelEditBtn = document.createElement('button');
cancelEditBtn.textContent = '取消编辑';
cancelEditBtn.style.padding = '8px 16px';
cancelEditBtn.style.backgroundColor = '#999';
cancelEditBtn.style.color = 'white';
cancelEditBtn.style.border = 'none';
cancelEditBtn.style.borderRadius = '4px';
cancelEditBtn.style.cursor = 'pointer';
editContentContainer = document.createElement('div');
editContentContainer.id = 'edit-content-container';
editContentContainer.style.cssText = `
padding: 0;
margin: 0;
`;
editHighlight = document.createElement('div');
editHighlight.id = 'edit-element-highlight';
editHighlight.setAttribute('data-html2canvas-ignore', 'true'); // 截图忽略
editHighlight.style.cssText = `
position: absolute;
background: rgba(234, 67, 53, 0.3);
border: 2px solid #ea4335;
pointer-events: none;
z-index: 99999999;
display: none;
`;
document.body.appendChild(editHighlight);
editToolbar.appendChild(removeElemBtn);
editToolbar.appendChild(removeHint);
editToolbar.appendChild(saveEditBtn);
editToolbar.appendChild(cancelEditBtn);
editWindow.appendChild(editToolbar);
editWindow.appendChild(editContentContainer);
document.body.appendChild(editWindow);
bindEditEvents();
return editWindow;
}
// 绑定编辑窗口相关事件
function bindEditEvents() {
removeElemBtn.addEventListener('click', toggleRemoveMode);
saveEditBtn.addEventListener('click', () => {
if (editedElementClone) {
captureAndSave(editedElementClone);
closeEditWindow();
}
});
cancelEditBtn.addEventListener('click', closeEditWindow);
document.addEventListener('keydown', handleEditKeyDown);
}
// 切换移除元素模式
function toggleRemoveMode() {
isRemovingElem = !isRemovingElem;
const removeHint = document.getElementById('remove-mode-hint');
if (isRemovingElem) {
removeElemBtn.style.backgroundColor = '#ff6b6b';
removeHint.textContent = '移除模式:鼠标选择元素点击删除(ESC退出)';
editContentContainer.addEventListener('mousemove', handleEditMouseMove);
editContentContainer.addEventListener('click', handleEditElementClick);
} else {
removeElemBtn.style.backgroundColor = '#ea4335';
removeHint.textContent = '';
editContentContainer.removeEventListener('mousemove', handleEditMouseMove);
editContentContainer.removeEventListener('click', handleEditElementClick);
editHighlight.style.display = 'none';
}
}
// 编辑窗口键盘事件处理
function handleEditKeyDown(e) {
if (!isEditing) return;
if (e.key === 'Escape') {
if (isRemovingElem) {
isRemovingElem = false;
const removeHint = document.getElementById('remove-mode-hint');
removeElemBtn.style.backgroundColor = '#ea4335';
removeHint.textContent = '';
editContentContainer.removeEventListener('mousemove', handleEditMouseMove);
editContentContainer.removeEventListener('click', handleEditElementClick);
editHighlight.style.display = 'none';
} else {
closeEditWindow();
}
}
}
// 保存页面原始样式表
function saveOriginalStylesheets() {
originalStylesheets = [];
Array.from(document.styleSheets).forEach(stylesheet => {
try {
if (stylesheet.href) {
originalStylesheets.push(stylesheet.href);
} else {
const rules = Array.from(stylesheet.cssRules).map(rule => rule.cssText).join('\n');
originalStylesheets.push({ type: 'inline', content: rules });
}
} catch (e) {
console.log('无法访问样式表:', e);
}
});
}
// 在编辑窗口中恢复样式表
function restoreStylesInEditWindow() {
const styleContainer = document.createElement('div');
styleContainer.id = 'original-styles-container';
originalStylesheets.forEach(style => {
if (typeof style === 'string') {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = style;
link.crossOrigin = 'anonymous';
styleContainer.appendChild(link);
} else if (style.type === 'inline') {
const styleElem = document.createElement('style');
styleElem.textContent = style.content;
styleContainer.appendChild(styleElem);
}
});
if (editContentContainer.firstChild) {
editContentContainer.insertBefore(styleContainer, editContentContainer.firstChild);
} else {
editContentContainer.appendChild(styleContainer);
}
}
// 显示编辑窗口
function showEditWindow(targetElement) {
createEditWindow();
originalSelectedElement = targetElement;
saveOriginalStylesheets();
editedElementClone = targetElement.cloneNode(true);
const computedStyle = window.getComputedStyle(targetElement);
editedElementClone.style.width = computedStyle.width;
editedElementClone.style.minWidth = computedStyle.minWidth;
editedElementClone.style.maxWidth = computedStyle.maxWidth;
editedElementClone.style.margin = computedStyle.margin;
editedElementClone.style.padding = computedStyle.padding;
editContentContainer.innerHTML = '';
editContentContainer.appendChild(editedElementClone);
restoreStylesInEditWindow();
editWindow.style.display = 'block';
isEditing = true;
}
// 关闭编辑窗口
function closeEditWindow() {
if (editWindow) editWindow.style.display = 'none';
isEditing = false;
isRemovingElem = false;
if (removeElemBtn) removeElemBtn.style.backgroundColor = '#ea4335';
if (editHighlight) editHighlight.style.display = 'none';
if (document.getElementById('remove-mode-hint')) document.getElementById('remove-mode-hint').textContent = '';
if (editContentContainer) {
editContentContainer.removeEventListener('mousemove', handleEditMouseMove);
editContentContainer.removeEventListener('click', handleEditElementClick);
}
}
// 编辑时鼠标移动高亮元素
function handleEditMouseMove(e) {
if (!isRemovingElem) return;
if (e.target.closest('#edit-element-window > div:first-child')) return;
const targetElem = e.target;
const rect = targetElem.getBoundingClientRect();
editHighlight.style.top = `${rect.top}px`;
editHighlight.style.left = `${rect.left}px`;
editHighlight.style.width = `${rect.width}px`;
editHighlight.style.height = `${rect.height}px`;
editHighlight.style.display = 'block';
}
// 编辑时点击移除元素
function handleEditElementClick(e) {
if (!isRemovingElem) return;
if (e.target.closest('#edit-element-window > div:first-child')) return;
const targetElem = e.target;
if (confirm('确定要移除这个元素吗?')) targetElem.remove();
editHighlight.style.display = 'none';
}
// 创建遮罩层
function createOverlay() {
if (document.getElementById('screenshot-overlay')) return document.getElementById('screenshot-overlay');
const overlay = document.createElement('div');
overlay.id = 'screenshot-overlay';
overlay.setAttribute('data-html2canvas-ignore', 'true'); // 截图忽略
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
z-index: 999997;
display: none;
pointer-events: none;
`;
const hintContainer = document.createElement('div');
hintContainer.style.cssText = `
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
pointer-events: auto;
`;
const hint = document.createElement('div');
hint.style.cssText = `
color: white;
font-size: 16px;
padding: 10px 20px;
background: rgba(0,0,0,0.5);
border-radius: 4px;
`;
hint.textContent = '[ 扩大选择 | ] 缩小选择 | 回车/点击确认 | ESC取消';
hintContainer.appendChild(hint);
overlay.appendChild(hintContainer);
document.body.appendChild(overlay);
return overlay;
}
// 显示/隐藏遮罩层
function toggleOverlay(show) {
const overlay = createOverlay();
overlay.style.display = show ? 'block' : 'none';
}
// 显示操作界面
function showUI() {
const ui = createUI();
ui.style.display = 'block';
bindEvents();
}
// 隐藏操作界面
function hideUI() {
if (uiContainer) uiContainer.style.display = 'none';
}
// 根据输入内容查找元素
function findElementByInput(inputVal) {
if (!inputVal.trim()) return null;
if (inputVal.startsWith('#')) return document.getElementById(inputVal.slice(1));
return document.querySelector(inputVal);
}
// 手动输入元素确认后的处理
function handleInputConfirm() {
const inputVal = elementInput.value.trim();
const status = document.getElementById('screenshot-status');
const info = document.getElementById('selected-element-info');
const captureBtn = document.getElementById('capture-button');
const editBtn = document.getElementById('edit-element-button');
const targetElement = findElementByInput(inputVal);
if (targetElement) {
selectedElement = targetElement;
const elementInfo = targetElement.id ? `${targetElement.tagName.toLowerCase()}#${targetElement.id}` : targetElement.tagName.toLowerCase();
status.textContent = `已选中输入元素: <${elementInfo}>`;
status.style.color = '#333';
info.textContent = `已选择: <${elementInfo}>`;
info.style.color = '#34a853';
info.style.borderColor = '#34a853';
captureBtn.disabled = false;
editBtn.disabled = false;
updateHighlight(targetElement);
} else {
status.textContent = '未找到该元素,请检查输入格式';
status.style.color = '#ea4335';
info.textContent = '未选择任何元素';
info.style.color = '#666';
info.style.borderColor = '#ddd';
captureBtn.disabled = true;
editBtn.disabled = true;
selectedElement = null;
updateHighlight(null);
}
}
// 变量存储
let selectedElement = null;
let isSelecting = false;
let highlightOverlay = null;
let elementHistory = [];
let historyIndex = -1;
// 创建高亮覆盖层
function createHighlightOverlay() {
if (highlightOverlay) return highlightOverlay;
highlightOverlay = document.createElement('div');
highlightOverlay.id = 'screenshot-highlight';
highlightOverlay.setAttribute('data-html2canvas-ignore', 'true'); // 截图忽略
highlightOverlay.style.cssText = `
position: absolute;
background: rgba(66, 133, 244, 0.3);
border: 2px solid #4285f4;
pointer-events: none;
z-index: 999998;
display: none;
transition: all 0.1s ease;
`;
document.body.appendChild(highlightOverlay);
return highlightOverlay;
}
// 更新高亮显示
function updateHighlight(element) {
if (!element) {
if (highlightOverlay) highlightOverlay.style.display = 'none';
return;
}
const rect = element.getBoundingClientRect();
const overlay = createHighlightOverlay();
overlay.style.top = `${rect.top + window.scrollY}px`;
overlay.style.left = `${rect.left + window.scrollX}px`;
overlay.style.width = `${rect.width}px`;
overlay.style.height = `${rect.height}px`;
overlay.style.display = 'block';
overlay.style.transform = 'scale(1)';
setTimeout(() => overlay.style.transform = 'scale(1.01)', 50);
setTimeout(() => overlay.style.transform = 'scale(1)', 100);
const status = document.getElementById('screenshot-status');
const elementInfo = element.id ? `${element.tagName.toLowerCase()}#${element.id}` : element.tagName.toLowerCase();
status.textContent = `已选择: <${elementInfo}>`;
}
// 过滤工具自身元素
function isToolElement(element) {
return element.id && (element.id.startsWith('element-screenshot-') || element.id.startsWith('screenshot-') || element.id.startsWith('edit-'));
}
// 获取元素的所有父级元素
function getParentElements(element) {
const parents = [];
let current = element.parentElement;
while (current) {
if (!isToolElement(current)) parents.push(current);
current = current.parentElement;
}
return parents;
}
// 获取元素的直接子级元素
function getChildElements(element) {
return Array.from(element.children).filter(child => !isToolElement(child));
}
// 向上选择父级元素
function selectParentElement() {
if (!selectedElement) return;
const parents = getParentElements(selectedElement);
if (parents.length > 0) {
if (historyIndex === elementHistory.length - 1) elementHistory.push(selectedElement);
historyIndex++;
selectedElement = parents[0];
updateHighlight(selectedElement);
}
}
// 向下选择子级元素
function selectChildElement() {
if (!selectedElement) return;
const children = getChildElements(selectedElement);
if (children.length > 0) {
if (historyIndex === elementHistory.length - 1) elementHistory.push(selectedElement);
historyIndex++;
selectedElement = children[0];
updateHighlight(selectedElement);
}
}
// 开始选择元素
function startSelecting() {
if (isSelecting) return;
isSelecting = true;
selectedElement = null;
elementHistory = [];
historyIndex = -1;
const status = document.getElementById('screenshot-status');
status.textContent = '移动鼠标选择初始元素(回车确认)...';
status.style.color = '#4285f4';
toggleOverlay(true);
document.body.style.cursor = 'crosshair';
document.getElementById('select-element-button').disabled = true;
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('click', handleMouseClick);
document.addEventListener('keydown', handleKeyDown);
}
// 结束选择元素
function stopSelecting(confirmed = false) {
isSelecting = false;
toggleOverlay(false);
document.body.style.cursor = '';
document.getElementById('select-element-button').disabled = false;
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('click', handleMouseClick);
document.removeEventListener('keydown', handleKeyDown);
if (highlightOverlay) highlightOverlay.style.display = 'none';
elementHistory = [];
historyIndex = -1;
const status = document.getElementById('screenshot-status');
const info = document.getElementById('selected-element-info');
const captureBtn = document.getElementById('capture-button');
const editBtn = document.getElementById('edit-element-button');
if (confirmed && selectedElement) {
status.textContent = '已选择元素,可编辑或直接截图';
status.style.color = '#333';
const elementInfo = selectedElement.id ? `${selectedElement.tagName.toLowerCase()}#${selectedElement.id}` : selectedElement.tagName.toLowerCase();
info.textContent = `已选择: <${elementInfo}>`;
info.style.color = '#34a853';
info.style.borderColor = '#34a853';
captureBtn.disabled = false;
editBtn.disabled = false;
} else {
status.textContent = '选择已取消';
status.style.color = '#ea4335';
info.textContent = '未选择任何元素';
info.style.color = '#666';
info.style.borderColor = '#ddd';
captureBtn.disabled = true;
editBtn.disabled = true;
selectedElement = null;
}
}
// 处理鼠标移动
function handleMouseMove(e) {
if (!isSelecting) return;
const elements = document.elementsFromPoint(e.clientX, e.clientY);
const targetElement = elements.find(el => !isToolElement(el));
if (targetElement) {
if (!selectedElement || targetElement !== selectedElement) {
selectedElement = targetElement;
elementHistory = [];
historyIndex = -1;
updateHighlight(targetElement);
}
} else {
updateHighlight(null);
selectedElement = null;
elementHistory = [];
historyIndex = -1;
}
}
// 处理鼠标点击
function handleMouseClick(e) {
if (!isSelecting) return;
e.stopPropagation();
e.preventDefault();
if (selectedElement) stopSelecting(true);
}
// 处理键盘事件
function handleKeyDown(e) {
if (!isSelecting) {
if (document.activeElement === elementInput && e.key === 'Enter') {
e.preventDefault();
handleInputConfirm();
}
return;
}
if (e.key === 'Escape') {
stopSelecting(false);
return;
}
if (e.key === 'Enter') {
e.preventDefault();
if (selectedElement) stopSelecting(true);
return;
}
if (e.key === '[') {
e.preventDefault();
selectParentElement();
return;
}
if (e.key === ']') {
e.preventDefault();
selectChildElement();
return;
}
}
// 捕获并保存截图(核心:排除工具自身元素)
function captureAndSave(targetElem = null) {
const captureTarget = targetElem || selectedElement;
if (!captureTarget) return;
const status = document.getElementById('screenshot-status');
status.textContent = '正在生成截图...';
status.style.color = '#fbbc05';
html2canvas(captureTarget, {
scale: window.devicePixelRatio || 1,
useCORS: true,
logging: false,
allowTaint: false,
backgroundColor: null,
// 双重保险:忽略工具自身ID
ignoreElements: (el) => {
return el.id && (
el.id === 'element-screenshot-tool' ||
el.id === 'screenshot-overlay' ||
el.id === 'screenshot-highlight' ||
el.id === 'edit-element-window' ||
el.id === 'edit-element-highlight'
);
}
}).then(canvas => {
const elementName = captureTarget.tagName.toLowerCase();
const fileName = targetElem ? `${elementName}-edited-screenshot-${new Date().getTime()}.png` : `${elementName}-screenshot-${new Date().getTime()}.png`;
const link = document.createElement('a');
link.download = fileName;
link.href = canvas.toDataURL('image/png');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
status.textContent = '截图已保存!';
status.style.color = '#34a853';
}).catch(error => {
console.error('截图失败:', error);
status.textContent = '截图失败,请重试';
status.style.color = '#ea4335';
});
}
// 绑定事件
function bindEvents() {
if (closeButton) closeButton.removeEventListener('click', handleClose);
if (inputConfirmBtn) inputConfirmBtn.removeEventListener('click', handleInputConfirm);
const selectBtn = document.getElementById('select-element-button');
const captureBtn = document.getElementById('capture-button');
const editBtn = document.getElementById('edit-element-button');
if (selectBtn) {
selectBtn.removeEventListener('click', startSelecting);
selectBtn.addEventListener('click', startSelecting);
}
if (captureBtn) {
captureBtn.removeEventListener('click', () => captureAndSave());
captureBtn.addEventListener('click', () => captureAndSave());
}
if (editBtn) {
editBtn.removeEventListener('click', () => {
if (selectedElement) showEditWindow(selectedElement);
});
editBtn.addEventListener('click', () => {
if (selectedElement) showEditWindow(selectedElement);
});
}
if (closeButton) closeButton.addEventListener('click', handleClose);
if (inputConfirmBtn) inputConfirmBtn.addEventListener('click', handleInputConfirm);
elementInput.removeEventListener('keydown', inputEnterHandler);
elementInput.addEventListener('keydown', inputEnterHandler);
}
// 输入框回车辅助处理函数
function inputEnterHandler(e) {
if (e.key === 'Enter') {
e.preventDefault();
handleInputConfirm();
}
}
// 关闭按钮处理函数
function handleClose() {
if (isSelecting) stopSelecting(false);
if (isEditing) closeEditWindow();
hideUI();
}
// 注册油猴菜单命令
GM_registerMenuCommand('点击截图', showUI);
// 初始化界面(默认隐藏)
createUI();
hideUI();
})();