I’m working on a chrome extension that allows users to input exact time locations into their video player. It’s a simple overlay that appears over a video frame. It works on Youtube videos (although I still have to deal with periodically ‘losing connection’ with the tab), and does in fact appear over videos in the Spotify player, but I cannot enter text into the input.
To be more specific, the input appears and displays a typing cursor on mouseover. However, the input cannot be focused and the blinking cursor does not appear. No text will appear if I click on the input and begin typing. If I do submit the form, the video’s time input will be set to 0 as I assume the default empty value would be 0 (so, 0 seconds into the video).
The input is z-indexed to the top-most and above the underlying div it’s nested in. There are no changes made to the HTML when injected into the Spotify site, and no error messages in the console.
Previously, the overlay would not appear on top of the video. The issue came from Spotify requiring an id and class name for the HTML element in order to display.
Again, my cursor does in fact change on mouseover. Clicking on the input, however, does not focus the input and I cannot enter text into the input at all.
Code:
There is only one input, with the id “timeInput” and the styling “input_style”.
console.log("content.js");
let overlayVisibleBool = false;
// Function to handle overlay display
function toggleOverlay(overlayVisible) {
chrome.runtime.sendMessage(
{ command: "checkActiveTab" },
function (response) {
if (response && response.isValid) {
const videoElement = document.getElementsByTagName("video")[0];
const popup = document.getElementById("rr_overlay");
if (videoElement && popup) {
const videoRect = videoElement.getBoundingClientRect();
overlayVisible = !overlayVisible;
if (!overlayVisible) {
console.log(videoRect);
popup.style.display = "flex";
popup.style.position = "absolute";
popup.style.left = videoRect.left + "px";
popup.style.bottom = videoRect.bottom + "px";
popup.style.top = videoRect.top + "px";
popup.style.width = videoRect.width + "px";
popup.style.height = videoRect.height + "px";
popup.style.zIndex = 9999;
popup.style.justifyContent = "center";
popup.style.alignItems = "center";
popup.style.flexDirection = "column";
const timeInput = document.getElementById("timeInput");
if (timeInput) {
timeInput.focus();
}
} else {
// Hide the overlay
popup.style.display = "none";
}
}
}
}
);
}
window.onresize = function () {
const videoElement = document.querySelector("video");
const videoRect = videoElement.getBoundingClientRect();
const popup = document.getElementById("rr_overlay");
popup.style.left = videoRect.left + "px";
popup.style.bottom = videoRect.bottom + "px";
popup.style.top = videoRect.top + "px";
popup.style.width = videoRect.width + "px";
popup.style.height = videoRect.height + "px";
};
let overlay_style = `
display: none;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.75);
clear: both;
`;
let image_style = `
width: 200px;
height: auto;
margin-top: -50px;
margin-bottom: -15px;
`;
let link_logo_style = `
width: 12px;
height: auto;
`;
let input_style = `
border: 0px solid;
background-color: transparent;
color: white;
font-size: 24px;
padding: 10px;
width: auto;
outline: none;
font-family: Arial, sans-serif;
z-index: 99999 !important;
clear: both;
position: relative;
`;
let form_style = `
display: flex;
align-items: center;
`;
let button_style = `
width: 100px;
height: 30px;
line-height: 20px;
padding: 0;
border: none;
background: rgb(255,27,0);
background: linear-gradient(0deg, rgba(255,27,0,1) 0%, rgba(251,75,2,1) 100%);
cursor: pointer;
border-radius: 5px;
font-size: 12px;
color: white;
`;
let link_button_style = `
width: 30px;
height: 30px;
line-height: 20px;
padding: 0;
border: none;
background: rgb(255,27,0);
background: linear-gradient(0deg, rgba(255,27,0,1) 0%, rgba(251,75,2,1) 100%);
cursor: pointer;
border-radius: 5px;
margin-left: 10px;
display: flex;
justify-content: center;
align-items: center;
`;
const rr_logo = chrome.runtime.getURL("assets/rrewind.svg");
const link_logo = chrome.runtime.getURL("assets/link.svg");
const overlay = **image attached**
const popupElement = document.createElement("div");
popupElement.id = "popup_container";
popupElement.className = "popup_container";
popupElement.innerHTML = overlay;
document.body.appendChild(popupElement);
console.log("Plugged in.");
const jumpForm = document.getElementById("jumpForm");
jumpForm.addEventListener("submit", function (e) {
e.preventDefault();
console.log("submitting jump");
manageTime(e);
});
function manageTime(e) {
console.log(timeInput);
chrome.runtime.sendMessage(
{ command: "checkActiveTab" },
function (response) {
if (response && response.isValid) {
const action = e.submitter.value;
console.log(action);
if (action === "jump") {
const timeInput = document
.getElementById("timeInput")
.value.split(":");
console.log(timeInput);
tl = timeInput.length;
let hoursInput, minutesInput, secondsInput, totalSeconds;
if (tl === 3) {
hoursInput = parseInt(timeInput[0], 10) || 0;
minutesInput = parseInt(timeInput[1], 10) || 0;
secondsInput = parseInt(timeInput[2], 10) || 0;
totalSeconds = hoursInput * 3600 + minutesInput * 60 + secondsInput;
} else if (tl === 2) {
minutesInput = parseInt(timeInput[0], 10) || 0;
secondsInput = parseInt(timeInput[1], 10) || 0;
totalSeconds = minutesInput * 60 + secondsInput;
} else if (tl === 1) {
secondsInput = parseInt(timeInput[0], 10) || 0;
totalSeconds = secondsInput;
} else {
return;
}
chrome.runtime.sendMessage({
command: "jumpToTime",
time: totalSeconds,
});
overlayVisibleBool = false;
toggleOverlay(overlayVisibleBool);
} else if (action === "link") {
const videoElement = document.querySelector("video");
let totalSeconds = videoElement.currentTime;
chrome.runtime.sendMessage({
command: "getLink",
time: totalSeconds,
});
}
}
}
);
}
document.addEventListener("keydown", function (event) {
// Check if CTRL + ` is pressed
if (event.ctrlKey && event.key === "`") {
console.log("TOGGLE ACTIVATED!");
overlayVisibleBool = !overlayVisibleBool;
console.log("overlay: ", overlayVisibleBool);
toggleOverlay(overlayVisibleBool);
}
});
The injected HTML (couldn’t attach the text):

background.js:
function getActiveTabId(callback) {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
if (tabs && tabs.length > 0) {
const tabId = tabs[0].id;
callback(tabId);
}
});
}
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
if (message.command === "checkActiveTab") {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
const isValid = tabs && tabs.length > 0;
sendResponse({ isValid: isValid });
});
return true; // Indicates that sendResponse will be called asynchronously
}
});
chrome.runtime.onMessage.addListener(async function (
message,
sender,
sendResponse
) {
getActiveTabId((tabId) => {
if (message.command === "jumpToTime") {
console.log("message sent: jumpToTime");
chrome.scripting.executeScript(
{
target: { tabId: tabId },
func: (time) => {
const videoElement = document.querySelector("video");
if (videoElement) {
console.log("total time: ", time);
videoElement.currentTime = time;
}
},
args: [message.time], // Pass the time parameter
},
(result) => {
if (chrome.runtime.lastError) {
console.error(
"Background script: Error executing script in content script:",
chrome.runtime.lastError
);
} else {
console.log(
"Background script: Script executed successfully:",
result
);
}
}
);
} else if (message.command === "getLink") {
chrome.scripting.executeScript({
target: { tabId: tabId },
func: (time) => {
if (window.location.href.includes("youtube")) {
const pageUrl = window.location.href
.replace("www.", "")
.replace("youtube.com", "youtu.be")
.replace("watch?v=", "");
navigator.clipboard.writeText(
pageUrl + "?feature=shared&t=" + Math.ceil(time)
);
} else {
const pageUrl = window.location.href;
navigator.clipboard.writeText(pageUrl);
}
},
args: [message.time],
});
}
});
return true;
});
manifest.json:
{
"manifest_version": 3,
"name": "RedbarRewind",
"version": "1.0.0",
"description": "Give it a year.",
"permissions": ["storage", "tabs", "activeTab", "scripting"],
"host_permissions": ["https://*.youtube.com/watch*", "https://*.spotify.com/*"],
"action": {
"default_icon": {
"16": "assets/rrewind16.png",
"48": "assets/rrewind48.png",
"128": "assets/rrewind128.png"
}
},
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["https://*.youtube.com/watch*", "https://*.spotify.com/*"],
"js": ["content.js"]
}
],
"web_accessible_resources": [
{
"resources": [
"assets/rrewind.svg",
"assets/redbarrewind.svg",
"assets/link.svg"
],
"matches": ["https://*.youtube.com/*", "https://*.spotify.com/*"]
}
],
"icons": {
"16": "assets/rrewind16.png",
"48": "assets/rrewind48.png",
"128": "assets/rrewind128.png"
}
}
The main problem, I’m assuming, lies with a hidden necessity in the HTML or styling that I’m missing. As I mentioned before, the parent div required a class and id in order to appear overlaid on the video, something I’d noticed in the CSS injected by the Spotify styling.
There is also no hint that the input is in any way disabled or readonly in the DOM. It might also be an issue with the CSP (Content Security Policy) although those error pop up in the console regardless of whether the addon is enabled or not. Thank you in advance for any help!