Skip to content

Instantly share code, notes, and snippets.

@santaklouse
Created February 5, 2026 22:34
Show Gist options
  • Select an option

  • Save santaklouse/0cb2d2a2b973038ddc68838b3140afd6 to your computer and use it in GitHub Desktop.

Select an option

Save santaklouse/0cb2d2a2b973038ddc68838b3140afd6 to your computer and use it in GitHub Desktop.
Paste into devtools and element selector appears where you could click and you get screenshot of the scrolled content
(() => {
async function loadScript(filePath, cacheBuster = `?bust=${new Date().getTime()}`) {
return await new Promise((resolve, reject) => {
const scriptTag = document.createElement('script');
scriptTag.onload = () => console.log('loaded');
scriptTag.type = 'text/javascript';
scriptTag.src = `${filePath}${cacheBuster}`;
scriptTag.onload = resolve;
scriptTag.onerror = reject;
document.querySelector('head').appendChild(scriptTag);
});
}
if (window.__hoverInspectorCleanup) return window.__hoverInspectorCleanup();
const makeScreenshot = async (elem) => {
const SCROLL_SELECTOR = "body"; // <-- поменяй на нужный контейнер, например ".main-scroll"
const scrollEl = elem || (SCROLL_SELECTOR === "body"
? document.documentElement
: document.querySelector(SCROLL_SELECTOR)
);
if (!scrollEl) {
throw new Error("Scroll element not found: " + SCROLL_SELECTOR);
}
if (!window.html2canvas) {
await loadScript("https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js");
}
// Временно убираем oklch, чтобы html2canvas не падал
const override = document.createElement("style");
override.id = "__html2canvas_safe_colors__";
override.textContent = `
* {
color: rgb(0,0,0) !important;
background-color: transparent !important;
border-color: transparent !important;
outline-color: transparent !important;
box-shadow: none !important;
text-shadow: none !important;
caret-color: rgb(0,0,0) !important;
-webkit-text-fill-color: rgb(0,0,0) !important;
}
`;
document.head.appendChild(override);
const totalHeight = scrollEl.scrollHeight;
const viewportHeight = scrollEl.clientHeight;
const totalWidth = scrollEl.scrollWidth;
const viewportWidth = scrollEl.clientWidth;
const shots = [];
const originalScrollTop = scrollEl.scrollTop;
for (let y = 0; y < totalHeight; y += viewportHeight) {
scrollEl.scrollTop = y;
await new Promise(r => setTimeout(r, 120)); // дать странице перерисоваться
const canvas = await html2canvas(scrollEl, {
useCORS: true,
allowTaint: true,
scale: window.devicePixelRatio || 1,
width: viewportWidth,
height: viewportHeight,
});
shots.push({ canvas, y });
}
// Собираем общий холст
const finalCanvas = document.createElement("canvas");
const scale = window.devicePixelRatio || 1;
finalCanvas.width = totalWidth * scale;
finalCanvas.height = totalHeight * scale;
const ctx = finalCanvas.getContext("2d");
for (const shot of shots) {
ctx.drawImage(shot.canvas, 0, shot.y * scale);
}
scrollEl.scrollTop = originalScrollTop;
override.remove();
const link = document.createElement("a");
link.download = `screenshot-full-${Date.now()}.png`;
link.href = finalCanvas.toDataURL("image/png");
link.click();
}
const label = document.createElement('div');
label.style.cssText = `
position: fixed; z-index: 2147483647;
padding: 2px 6px; font: 11px/1.3 monospace;
background: rgba(0,0,0,.75); color: #fff; border-radius: 3px;
pointer-events: none; white-space: nowrap;
transform: translate(-1px, -100%); margin-top: -1px;
border-width: 1px 1px medium;
border-style: solid solid none;
border-color: rgb(255, 106, 0) rgb(255, 106, 0) currentcolor;
border-image: none;
padding: 10px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
`;
document.body.appendChild(label);
const style = document.createElement('style');
style.textContent = `.__hover_inspect { outline: 1px solid #ff6a00 !important; }`;
document.head.appendChild(style);
let last;
const getXPath = (el) => {
if (el.id) return `//*[@id="${el.id}"]`;
const parts = [];
while (el && el.nodeType === 1) {
let i = 1, sib = el.previousSibling;
while (sib) { if (sib.nodeType === 1 && sib.tagName === el.tagName) i++; sib = sib.previousSibling; }
parts.unshift(`${el.tagName.toLowerCase()}[${i}]`);
el = el.parentNode;
}
return '/' + parts.join('/');
};
const onMove = (e) => {
const el = e.target;
if (last && last !== el) last.classList.remove('__hover_inspect');
if (el && el.nodeType === 1) {
el.classList.add('__hover_inspect');
const cls = el.className && typeof el.className === 'string'
? '.' + el.className.trim().split(/\s+/).join('.')
: '';
label.textContent = (cls ? cls : getXPath(el)).replace('.__hover_inspect', '');
const r = el.getBoundingClientRect();
label.style.left = Math.max(0, Math.min(r.left, window.innerWidth - label.offsetWidth - 4)) + 'px';
label.style.top = Math.max(0, r.top) + 'px';
last = el;
}
};
document.addEventListener('mouseover', onMove, true);
document.body.addEventListener('click', function(event) {
if (event.target.matches('.__hover_inspect')) {
console.log('element clicked:', event.target.classList);
makeScreenshot(event.target);
}
});
window.__hoverInspectorCleanup = () => {
document.removeEventListener('mouseover', onMove, true);
if (last) last.classList.remove('__hover_inspect');
label.remove(); style.remove();
delete window.__hoverInspectorCleanup;
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment