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

Is there a JavaScript function available to remove comments from previously commented HTML code?

<div id="div1">bar</div> JQuery function addComment(element){ element.wrap(function() { return '<!--' + this.outerHTML + '"-->'; }); } addComment($('#div1')); Looking for assistance in unc ...

What is the best way to outline the specifications for a component?

I am currently working on a TypeScript component. component @customElement("my-component") export class MyComponent extends LitElement { @property({type: String}) myProperty = "" render() { return html`<p>my-component& ...

Binding text inputs in Laravel Vue.js does not work

I am working on building a basic edit application for a post using Laravel. I am utilizing Vue.js 2 to bind the data, but I am encountering an issue where the data is not displaying as expected. When I use Vue's syntax, such as @{{ postData.title }}, ...

The dynamic fusion of Typescript and Angular 2 creates a powerful

private nodes = []; constructor(private nodeService: NodeService) {} this.nodeService.fetchNodes('APIEndpoint') .subscribe((data) => { this.nodes.push(data); }); console.log(this.nodes) This ...

Is there a way to change HTML retrieved from an AJAX request before inserting it into the document?

I am utilizing an ajax call to retrieve an HTML page from the server; the ajax call is structured as follows: function loadHtml() { $.ajax({ type : 'GET', async : false, url : &apos ...

How can I access and modify objects within a state array in reactJS when using the setState() method?

Upon reviewing my code, I came across the following declaration: constructor(props) { super(props); this.state = { productArray: [{ barcode: '', name: '' }], numberOfRecords: ...

JavaScript failing to accurately measure the length

Currently experiencing a Javascript issue where the length of an element is not displayed correctly when using .length, even though it shows up in Chrome console. Here is what it looks like in Chrome console <html xmlns="http://www.w3.o ...

Mapping JSON objects to TypeScript Class Objects

I am in the process of transitioning my AngularJS application to Angular 6, and I'm encountering difficulties converting a JSON object into a TypeScript object list. In my Angular 6 application, I utilize this.http.get(Url) to retrieve data from an AP ...

Echo command fails to work with location.href

I am facing a small issue with my PHP echo. I have a JavaScript link function that is not working because the HTML code shows this type of link onclick="location.href="http://www.link.com/";". It's clear that this syntax won't work. However, if w ...

Guide to downloading a file from a byte base64 in Vue.js

I am dealing with a byte base64 string let data = 'UEsDBBQABgAIAAAAIQBi7...' If I want to download the file from this byte base64 in vue js, what should I do? Any suggestions or solutions would be greatly appreciated :') ...

Upon the initial hover, the data added to the title tag following an Ajax call is not appearing

I am currently working on an ajax request that retrieves information such as username, email, and user_id. Once the ajax call is successful, I use jQuery to append this data to the title tag. The main issue I am facing is that the data is only displayed af ...

Tips on converting a Java regular expression to JavaScript regular expression

Can someone assist me in translating the Java Regex code below to JavaScript Regex? (\\\p{Upper}{2})(\\\d{2})([\\\p{Upper}\\\p{Digit}]{1,30}+) I attempted using the following JavaScript Regex: ...

A guide on transferring a Vue component to a custom local library

After successfully creating components using template syntax (*vue files), I decided to move common components to a library. The component from the library (common/src/component/VButton): <template> <button ... </button> </templat ...

Manipulating CSS Class Properties Using JavaScript

In my JavaScript application, there is a functionality that loads a list of items for users to click and view detailed information in a separate div on the page. Users should be able to interact with and make modifications to these individual item overview ...

Using jQuery, generate a dynamic form to create a multidimensional array

I've set up a form where additional dropdowns can be dynamically added if the user clicks on a specific link. Here's an example of how it looks: <div class="dynamic-sale"> <select name="sizes[]" id="sizes" class="entry-dropdown"&g ...

I am interested in modifying the hover effect for the text letters within the material UI container when hovering over it

this is the code I am currently working with: import React, { Component } from "react"; import MobileDetect from "mobile-detect"; import { map, orderBy, flowRight as compose, isEmpty, get } from "lodash"; import { Grid, Li ...

What is the best way to incorporate a custom event listener into my React Native component?

Hello everyone, I am currently working with React Native+Expo and have created a custom component called a stepper. Here's how it looks: Below is the code for this custom stepper: import React, { useState } from 'react'; import { View, Text ...

After reaching a total of 20 entries, req.body will automatically convert the array into an

I have the ability to dynamically add properties to my form: <form action=""> <div class="property"> <label>Name : <input type="text" name="properties[1][name]"></label> <label>Order : <input type="text" na ...

spark of unique substance

Having trouble with cycle2 as all images are briefly displayed when the page loads. I tried the recommended solution http://jquery.malsup.com/cycle/faq.html but it didn't stop the flashing, indicating a different issue: The suggested fix for Cycle is ...

The error you are seeing is a result of your application code and not generated by Cypress

I attempted to test the following simple code snippet: type Website = string; it('loads examples', () => { const website: Website = 'https://www.ebay.com/'; cy.visit(website); cy.get('input[type="text"]').type(& ...