Why YouTube feed extensions break, and how to filter Polymer without polling
If you have used a browser extension to hide YouTube clickbait, shorts, or recommendation rabbit holes, you probably ran into the same failure: it works on initial load, then starts flickering, misses videos as you scroll, uses 20% CPU in the background, or breaks completely after an hour.
Building a reliable client-side content filter on modern YouTube is deceptively difficult. YouTube runs as a Polymer single-page application that reuses a fixed pool of DOM elements as you scroll.
This note covers the DOM recycling problem, why interval polling and global observers fail, and how FeedTamer filters Polymer feeds with zero polling and zero telemetry.
1. The DOM recycling trap
YouTube's virtualized scroller keeps a bounded pool of DOM nodes, typically 16 to 32 card containers depending on viewport resolution. As you scroll downward:
- Cards that scroll out of the top of the viewport are detached or repurposed.
- Their container elements are reused to display newly fetched videos entering from the bottom.
- YouTube's client runtime mutates internal properties on the existing node: changing title text, thumbnail URLs, channel links, and duration badges.
This virtualization causes two common bugs in simple extensions:
- Ghost hiding. If an extension applies
element.style.display = 'none'to a video card that matched a block rule, that card stays hidden when YouTube later repopulates that same recycled element with an entirely different video that should have passed your filters. - Scroll-in misses. Extensions that only inspect elements through a
MutationObserverlistening for added nodes (record.addedNodes) miss recycled elements entirely, because the node was never inserted into the DOM again. Only its inner text attributes changed.
2. What failed: interval polling and global observers
When developers discover that addedNodes is insufficient, the two common fallbacks usually make browser performance worse.
Interval polling
The simplest workaround is running a periodic query:
// The battery burner
setInterval(() => {
document.querySelectorAll('ytd-rich-item-renderer').forEach(processCard);
}, 250);
On high-refresh laptop displays, polling every 250ms forces constant DOM traversals and layout queries on the main thread. During 4K 60fps video playback, this drops frames and drains battery. Blocked content can also flash on screen for up to a quarter of a second before the interval runs.
Unbounded MutationObservers
The second workaround is observing everything:
// The layout thrashing pattern
new MutationObserver(callback).observe(document.body, {
childList: true,
subtree: true,
attributes: true
});
YouTube's client runtime updates player progress bars, avatar hover previews, and internal telemetry markers multiple times a second. An observer attached to document.body that listens to all attributes and subtrees receives hundreds of mutation records per second, freezing the tab.
3. The architecture that worked
FeedTamer uses four coordinated components designed for YouTube's Polymer runtime:
Targeted scoped observers
Instead of watching document.body, FeedTamer attaches independent observers strictly to known container surfaces:
- Home grid:
ytd-rich-grid-renderer - Watch page recommendations:
ytd-watch-next-secondary-results-renderer #items - Search results:
ytd-section-list-renderer #contents - Channel video grids:
ytd-item-section-renderer #items
Mutations inside the video player or unrelated navigation bars never wake up the filtering engine.
Identity extraction over fragile CSS classes
Web applications frequently regenerate or obfuscate CSS class names between deployments. FeedTamer avoids class-based matching by resolving entity identity through stable semantic custom element slots and anchor paths, extracting verified channel handles and canonical video identifiers directly from the document hierarchy.
Synchronous data-mutation binding
To catch DOM recycling without intervals, the scoped observer monitors both childList changes for newly rendered shelves and targeted attributes on title containers, such as title and aria-label on anchor elements. When YouTube recycles a card and assigns a new video title, the attribute change triggers immediate evaluation of that single card in 0ms before the browser paints the next frame.
Deterministic priority ladder
Filtering logic runs through a strict, zero-allocation sequence:
- Explicit allowlist. Channels or keywords you explicitly marked to keep always pass.
- Shorts and format toggles. Shorts shelves and standalone cards are removed at the root element level.
- Explicit channel blocks. Immediate block with reason tracking.
- Duration bounds. Videos shorter than your minimum or longer than your maximum are removed.
- Keyword and regex filters. Evaluated against title and description snippets.
- Repetition limits. Bounded counts prevent a single channel from dominating your feed.
Zero network latency: All rule evaluations run in memory inside the client browser. No video IDs, titles, or browsing activity are transmitted to an external server or API.
4. Honest limitations
Local-first browser extensions have real technical constraints:
- DOM dependency. If YouTube changes its custom element naming conventions, extension selectors must be updated to match the new tags.
- Stream immutability. FeedTamer filters recommendation cards and shelves in the feed; it does not alter playback inside the video player.
- Desktop focus. FeedTamer runs as a standard Manifest V3 extension on desktop browsers (Chrome, Brave, Edge, Firefox). Mobile web YouTube uses a different light-DOM layout.
5. Try it yourself
FeedTamer is built for professionals, students, and families who rely on YouTube for learning and technical talks, but want to remove algorithmically engineered ragebait and rabbit holes.
You can test the interactive simulator directly on thefeedtamer.com.