Chrome扩展开发:解决YouTube相关视频栏注入HTML被挤到底部的问题
Hey there! I totally get why this is frustrating—you inject your header, it shows up for a second, then gets pushed down once YouTube loads those related videos. Let's break down what's happening and how to fix it.
Why This Happens
YouTube uses dynamic content loading (AJAX) for almost everything on the watch page, including the related videos section. Your current code runs once when document.ready fires, but after that, YouTube's scripts are still updating the #related DOM element as it loads video thumbnails and metadata. When those new elements get added, they end up pushing your injected <h1> to the bottom because the dynamic content is being inserted above it (or the section is re-rendered entirely).
The Solution: Use MutationObserver
Instead of injecting your content once, we can use the browser's MutationObserver API to watch for changes to the #related element. This way, whenever YouTube updates that section, we can re-position our injected content to stay at the top.
Here's how to modify your inject.js code:
// Helper function to wait for a specific DOM element to exist function waitForElement(selector, callback) { const element = document.querySelector(selector); if (element) { callback(element); } else { // Check again in 100ms if the element isn't there yet setTimeout(() => waitForElement(selector, callback), 100); } } // Wait for the #related section to load first waitForElement('#related', function(relatedSection) { // Create our injected header element (reuse this so we don't make duplicates) const injectedHeader = $('<h1>This was injected through jQuery.</h1>'); // Insert it initially $(relatedSection).prepend(injectedHeader); // Set up an observer to watch for changes to the #related section's children const observer = new MutationObserver(function(mutations) { mutations.forEach(function() { // Check if our header is still the first child of #related if (relatedSection.firstChild !== injectedHeader[0]) { // If not, move it back to the top $(relatedSection).prepend(injectedHeader); } }); }); // Configure the observer to watch for added/removed child elements const observerConfig = { childList: true }; // Start watching the #related section observer.observe(relatedSection, observerConfig); });
How This Works
waitForElement: Ensures we don't try to inject content until the#relatedelement actually exists on the page (since YouTube might load it afterdocument.ready).- Reusable Element: We create our
<h1>once and reuse it, so we don't end up with duplicate headers every time the DOM updates. - MutationObserver: This listens for any changes to the child elements of
#related. Whenever YouTube adds new related videos, the observer triggers, and we check if our header is still at the top. If it's not, we move it back to the first position withprepend().
Bonus Tips
- Stable Selectors: YouTube changes its DOM structure occasionally, so keep an eye on
#related—if it stops working, you might need to update the selector to something more reliable (like a data attribute or a parent element that's less likely to change). - Handle Page Navigation: If you want your extension to work when users navigate between videos without reloading the page, you'll also need to listen for URL changes in the SPA. You can do this with
window.addEventListener('popstate', ...)or by watching thehistoryobject.
内容的提问来源于stack exchange,提问作者jack novotny

