What methods can be used to monitor changes made to thumbnails on the YouTube platform?

I have embarked on a project to create a Chrome extension that alters the information displayed on the thumbnails of YouTube's recommended videos. In this case, I am looking to replace the video length with the name of the channel.

Imagine you are on a YouTube video page with the video player in the center and a list of thumbnails on the right side. The following code snippet allows for this replacement:

function processNode(node: HTMLElement) {
    const channelName = node
        .closest('ytd-thumbnail')
        ?.parentElement?.querySelector('.ytd-channel-name')
        ?.querySelector('yt-formatted-string');

    if (channelName?.textContent) node.textContent = channelName?.textContent;
}

async function replaceCurrentThumbnailTimes(): Promise<void> {
    for (const node of document.querySelectorAll(
        'span.ytd-thumbnail-overlay-time-status-renderer',
    )) {
        processNode(node as HTMLElement);
    }
}

void replaceCurrentThumbnailTimes();

While this approach works initially, it fails to update the replaced values when navigating to a new page within YouTube. Despite refreshing the thumbnails, the outdated information persists.

For instance, upon opening a video by Alice where her name replaces the time on the thumbnail, clicking on another video featuring Bob does not update the previous Alice thumbnail with the latest info.

In an attempt to address this issue, I explored using the MutationObserver API. However, my efforts proved ineffective as it only worked for newly added thumbnails, neglecting existing ones that were altered.

async function replaceFutureThumbnailTimes(): Promise<void> {
    const observer = new MutationObserver((mutations) => {
        // Check for video thumbnail times in each new node added
        for (const mutation of mutations) {
            for (const node of mutation.addedNodes) {
                if (
                    node instanceof HTMLElement &&
                    node.classList.contains(
                        'ytd-thumbnail-overlay-time-status-renderer',
                    ) &&
                    node.getAttribute('id') === 'text'
                ) {
                    processNode(node);
                }
            }
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true,
        characterData: true,
        attributes: true,
    });
}

void replaceFutureThumbnailTimes();

This challenge may be related to shadow/shady DOM structures, posing difficulties in finding a workaround. If you wish to test the code, I have shared a pure JavaScript version on pastebin for easy replication: https://pastebin.com/NWKfzCwQ

Answer №1

Both @RoryMcCrossan and @wOxxOm provided valuable suggestions in response to the question, confirming that the MutationObserver is effective and my previous misuse of it was the issue. Many thanks to both individuals for their help!

Specifically, the need was to track attribute changes and monitor any alterations in the aria-label within nodes identified by the ID text.

Below is a javascript code snippet that achieves this functionality:

async function replaceFutureThumbnailTimes() {
    const observer = new MutationObserver((mutations) => {
        for (const mutation of mutations) {
            // Check if attributes were modified and if it's related to thumbnail time
            if (
                mutation.type === 'attributes' && 
                mutation.attributeName === 'aria-label' && 
                mutation.target.getAttribute('id') === 'text'
            ) {
                processNode(mutation.target);
            }
            // For each newly added node, verify if it represents a video thumbnail time
            for (const node of mutation.addedNodes) {
                if (
                    node instanceof HTMLElement &&
                    node.classList.contains(
                        'ytd-thumbnail-overlay-time-status-renderer',
                    ) &&
                    node.getAttribute('id') === 'text'
                ) {
                    processNode(node);
                }
            }
        }
    });
 
    observer.observe(document.body, {
        childList: true,
        subtree: true,
        characterData: false,
        attributes: true,
    });
}
 
void replaceFutureThumbnailTimes();

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The message shown on items.map stating that parameter 'item' is implicitly assigned the type 'any'

Currently, I am delving into the world of Ionic React with Typescript by developing a basic app for personal use. My current challenge involves dynamically populating an IonSegment from an array. Here is the relevant code snippet: const [items, setItems] ...

A guide on implementing TypeScript with React Native's platform-specific extensions

The issue at hand: In my react native project, I am using a custom hook that has platform-specific code. I need to import this hook based on the platform in use. When I import it as import useWifi from 'hooks/use-wifi.android';, everything works ...

Why isn't Nodemon monitoring the directory in webpack-typescript-node.js?

Here are the contents of the package.json file for a TypeScript project using webpack and node.js: "scripts": { "build": "webpack", "dev:start": "nodemon --watch src --exec \"node -r dotenv/co ...

Collection of functions featuring specific data types

I'm currently exploring the idea of composing functions in a way that allows me to specify names, input types, and return types, and then access them from a central function. However, I've encountered an issue where I lose typing information when ...

When the JSON array is converted into a string, it appears as undefined

Below is a snippet of my service.spec.ts file: service.spec.ts it('should mock the http requests', inject([Service, MockBackend], (service, mockBackend) => { let result:any; mockBackend.connections.subscribe((connection) => { ...

Instructions for transferring a JavaScript array to a Java servlet

Greetings everyone! I am currently working on a web application with an orthodox approach, utilizing AJAX in Java and JavaScript. I am wondering if it is feasible to pass an array from JavaScript to a Servlet. ...

What is the best way to create divs that can close and hide themselves when clicked from inside the div itself?

I have a situation in my code where clicking a link reveals a div, and then the same link must be clicked again to hide it. However, I want to modify this so that any link within the div can also hide it when clicked. I currently have eight divs set up lik ...

What is the best way to apply index-based filtering in Angular JS?

I am working on a tab system using ng-repeat to display tabs and content, but I'm facing some challenges in making it work seamlessly. Below are the tabs being generated: <ul class="job-title-list"> <li ng-repeat="tab in tabBlocks"> ...

Looping through an array of nested objects using Vue

I have encountered a challenge with accessing specific data within an array that I am iterating over. The array is structured as follows, using Vue.js: companies: [ name: "company1" id: 1 type: "finance" additionalData: "{& ...

I'm having trouble getting Remix.run and Chart.js to cooperate, can anyone offer some guidance?

I've encountered a challenge with Remix.run and chart.js (react-chartjs-2) when attempting to display the chart. I followed the documentation and installed the necessary dependencies: react-chartjs-2 and chart.js. Here's the snippet from my pac ...

Received the error 'Headers cannot be set after they have been sent to the client' upon the second request

I created a web server that acts as a client-side application using socket.io-client and express. This setup is necessary for another project I am working on. The web server emits the posted string and responds by sending the served string when it receive ...

How can I use oboe.js to target a specific node instead of selecting all matching nodes?

Currently, I am receiving a JSON array through an http stream. The objects within this array are structured as follows: { "ID" : 1234, "Item" : { "ID" : "ABC123", "name" : "a thing" } } In reality, the objects are part of an array and look ...

Tips for customizing the AjaxComplete function for individual ajax calls

I need help figuring out how to display various loading symbols depending on the ajax call on my website. Currently, I only have a default loading symbol that appears in a fixed window at the center of the screen. The issue arises because I have multiple ...

What is the best way to reveal and automatically scroll to a hidden div located at the bottom of a webpage?

Whenever the image is clicked, I want to display a hidden div with effects like automatically scrolling down to the bottom This is what my code looks like: $(document).ready(function() { $('#test').click(function() { $(&apos ...

"Hidden panels in Sencha Touch only respond to show() and hide() methods after a resize event

Check out this demonstration of a Sencha Touch app by visiting this link. The button located in the bottom-left corner is supposed to show or hide the menu panel on top of the "Location info goes here" bar, but it seems to be functioning in an unexpected m ...

Sort through a list of objects using the criteria from a separate array

Looking to apply a filter on an array of objects: const myArray = [{ id: 4, filters: ["Norway", "Sweden"] }, { id: 2, filters :["Norway", "Sweden"] }, { id: 3, filters:["Denmark", "Sweden&q ...

Is there a method in VBA to access elements generated by javascript code?

After spending several hours conducting thorough research on Google (including browsing StackOverflow), I've been trying to find a method that would allow me to target HTML elements generated by JavaScript in VBA. For instance, using ie.Document.getE ...

Is there a way to access the value or key of a JSON property in an Angular template for rendering purposes?

Having trouble displaying the JSON values of certain properties on screen. Utilizing Angular Material table to showcase my JSON response. The code snippet below is responsible for rendering the JSON data: <mat-card-content class="dashboard-card-cont ...

"Transmitting a message with socket.io upon establishing connection

Currently getting the hang of socket.io and giving it a try. I have a straightforward app where clicking the display button shows an image on the screen in real time. I have a couple of questions. Firstly, my main concern is regarding the synchronization ...

When setting an attribute on load, Javascript may not properly apply the change to the attribute

I designed a webpage where images have a fade-in effect using a CSS class. Initially, 4 images load with the desired effect. However, I encounter an issue when trying to apply the same effect to a new image that appears upon clicking a button. Below is th ...