Skip to content

Instantly share code, notes, and snippets.

@cspotcode
Created December 18, 2025 19:20
Show Gist options
  • Select an option

  • Save cspotcode/748976b32f4a2e9ce0021729f5383980 to your computer and use it in GitHub Desktop.

Select an option

Save cspotcode/748976b32f4a2e9ce0021729f5383980 to your computer and use it in GitHub Desktop.
Steal focus from Yomitan popup so that asbplayer hotkeys work
// ==UserScript==
// @name Auto-Restore Focus from Yomitan Iframe
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Automatically restore focus from Yomitan iframe to prevent hotkey conflicts with asbplayer
// @author You
// @match *://*/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
// Configuration
const CHECK_INTERVAL_MS = 50; // Check every 50ms (adjust if needed)
// Debug tags: only log messages with these tags
// Available tags: 'init', 'detect', 'restore', 'track', 'error'
// Use ['*'] to log everything, or [] to disable all logging
const DEBUG_TAGS = [/*'init', 'detect',*/ 'restore']; // Customize which logs you want to see
let lastNonIframeFocus = null;
let isRestoring = false;
function log(tag, ...args) {
// Check if this tag should be logged
if (DEBUG_TAGS.includes('*') || DEBUG_TAGS.includes(tag)) {
console.log(`[Focus Restorer:${tag}]`, ...args);
}
}
function isYomitanIframe(element) {
if (!element || element.tagName !== 'IFRAME') {
return false;
}
// Check for the specific class "yomitan-popup"
if (element.classList && element.classList.contains('yomitan-popup')) {
log('detect', 'Found Yomitan iframe with class "yomitan-popup"');
return true;
}
// Check other Yomitan identifiers as fallback
const id = element.id || '';
const className = element.className || '';
if (id.includes('yomitan') ||
id.includes('yomichan') ||
className.includes('yomitan') ||
className.includes('yomichan')) {
log('detect', 'Found Yomitan iframe with identifier:', id || className);
return true;
}
// Check parent elements for Yomitan container
let parent = element.parentElement;
while (parent) {
const parentId = parent.id || '';
const parentClass = parent.className || '';
if (parentId.includes('yomitan') ||
parentId.includes('yomichan') ||
parentClass.includes('yomitan') ||
parentClass.includes('yomichan')) {
log('detect', 'Found Yomitan iframe via parent container:', parentId || parentClass);
return true;
}
parent = parent.parentElement;
}
return false;
}
function isInsideIframe(element) {
if (!element) return false;
// Check if element is inside an iframe by checking if we can access it
try {
// If element is in an iframe, it will have a different window
return element.ownerDocument !== document;
} catch (e) {
// Cross-origin iframe - we can't access it
return true;
}
}
function shouldTrackElement(element) {
if (!element) return false;
// Don't track if it's an iframe or inside an iframe
if (element.tagName === 'IFRAME') return false;
if (isInsideIframe(element)) return false;
return true;
}
function checkAndRestoreFocus() {
if (isRestoring) return; // Prevent recursive restoration
const activeElement = document.activeElement;
// If focus is on an iframe (likely Yomitan)
if (activeElement && activeElement.tagName === 'IFRAME') {
if (isYomitanIframe(activeElement)) {
log('restore', 'Yomitan iframe has focus, attempting to restore...');
if (lastNonIframeFocus && document.contains(lastNonIframeFocus)) {
isRestoring = true;
try {
lastNonIframeFocus.focus();
log('restore', 'Restored focus to:', lastNonIframeFocus);
} catch (e) {
log('error', 'Failed to restore focus:', e);
} finally {
isRestoring = false;
}
} else {
// Fallback: try to focus the video element or body
isRestoring = true;
try {
const video = document.querySelector('video');
if (video) {
video.focus();
log('restore', 'Restored focus to video element');
} else {
document.body.focus();
log('restore', 'Restored focus to body');
}
} catch (e) {
log('error', 'Failed to restore focus:', e);
} finally {
isRestoring = false;
}
}
}
} else if (shouldTrackElement(activeElement)) {
// Track this as the last valid non-iframe focus
lastNonIframeFocus = activeElement;
log('track', 'Tracking focus:', activeElement.tagName, activeElement.id || activeElement.className);
}
}
// Start monitoring after page loads
function init() {
log('init', 'Focus restorer initialized');
// Set initial focus tracking
if (shouldTrackElement(document.activeElement)) {
lastNonIframeFocus = document.activeElement;
} else {
// Default to body
lastNonIframeFocus = document.body;
}
// Start the interval check
setInterval(checkAndRestoreFocus, CHECK_INTERVAL_MS);
// Also monitor focus changes via event (more responsive)
document.addEventListener('focusin', function(e) {
if (!isRestoring) {
checkAndRestoreFocus();
}
}, true);
log('init', 'Started monitoring focus with', CHECK_INTERVAL_MS, 'ms interval');
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
log('init', 'Script loaded and ready');
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment