What Are Element Hiders in uBlock Origin Content Scripts?

Element hiders in uBlock Origin are CSS rules generated from ## cosmetic filters that the content script injects into web pages to hide DOM elements matching specific selectors.

In the gorhill/uBlock repository, element hiders (also called element hiding) form the client-side component of cosmetic filtering. They transform static filter list rules into dynamic stylesheets that remove unwanted page elements at the DOM level.

How Element Hiders Work in uBlock Origin

The architecture follows a three-stage pipeline that moves from filter text to injected CSS.

Parsing the Static Extended Filter

The process begins in src/js/static-ext-filtering.js, where the engine identifies filters containing the ## (hide) or #@# (unhide) syntax. Lines 40-45 describe this as cosmetic filtering (aka "element hiding" in Adblock Plus). When detected, these filters are routed to the cosmetic filtering engine for compilation.

Compiling to CSS Selectors

In src/js/cosmetic-filtering.js, the CosmeticFilteringEngine compiles raw selectors into executable CSS. The compileGenericHideSelector method (lines 42-84) builds final selectors and stores them in generic or specific buckets. For simple selectors reducible to a hash key, the engine stores the hash for fast lookup; complex selectors remain as-is.

Injecting Styles via the Content Script

After compilation, src/js/contentscript.js handles the actual DOM manipulation. The content script creates a DOMFilterer instance that receives compiled CSS (e.g., .ad{display:none!important;}). Lines 96-99 show how DOMFilterer.addCSS records the stylesheet and, when active, adds it to the per-page user stylesheet via vAPI.userStylesheet.add. The userStylesheet.apply() call finally injects a <style> element into the document.

Generic vs. Specific Element Hiders

uBlock Origin distinguishes between two element hider types based on hostname specificity.

Specific element hiders (example.com##.ad) originate from CosmeticFilteringEngine.compileSpecificSelector (lines 87-96 in cosmetic-filtering.js). These are stored in cfeDetails.injectedCSS and cfeDetails.exceptionFilters, then applied through domFilterer.addCSS or removed via domFilterer.exceptCSSRules.

Generic element hiders (no hostname restriction) are compiled by compileGenericHideSelector into hash-based low-generic or high-generic buckets (lines 74-94). These travel to the content script on-demand via the survey mechanism. The domSurveyor queries the engine for matching generic selectors, and the results are added through domFilterer.addCSS.

The Element Hider Lifecycle in Content Scripts

The typical page load follows this execution path:

  1. Bootstrap: vAPI.bootstrap requests cosmetic filter data from the background process.
  2. Data Transfer: The response contains cfeDetails.injectedCSS with specific and pre-computed generic selectors.
  3. Instantiation: The content script creates a DOMFilterer and calls addCSS with the received stylesheet (lines 96-99 in contentscript.js).
  4. Registration: DOMFilterer registers the CSS with vAPI.userStylesheet.
  5. Injection: vAPI.userStylesheet.apply() inserts a <style> element that hides targeted elements.
  6. Dynamic Surveying: If generic selectors require DOM analysis, the domSurveyor walks the page, collects class/id hashes, queries the background for matches, and injects additional CSS via step 3.

Code Examples

Converting Filters to CSS Rules

// Filter written by the user or loaded from a filter list:
example.com##.sponsored

// Compilation step (simplified):
const selector = '.sponsored';
const css = `${selector}{display:none!important;}`;

// Injection via the content script API:
vAPI.userStylesheet.add(css, true);   // true applies immediately

Injecting Element Hiders from the Content Script

// Compiled CSS string received from background:
const injectedCSS = '.ad-banner{display:none!important;}\n#pop{display:none!important;}';

// Inside the content script after bootstrap:
domFilterer.addCSS(injectedCSS, { mustInject: true });

Handling Exception Filters

// Exception filter syntax disables a specific hider:
example.com#@#.sponsored

// The content script removes the corresponding rule:
vAPI.userStylesheet.remove('.sponsored{display:none!important;}');

Summary

  • Element hiders are CSS rules derived from ## cosmetic filters that hide DOM elements without blocking network requests.
  • The pipeline flows through static-ext-filtering.js (parsing), cosmetic-filtering.js (compilation), and contentscript.js (injection).
  • Specific hiders target individual hostnames, while generic hiders apply broadly and rely on the domSurveyor for efficient matching.
  • The DOMFilterer class manages the vAPI.userStylesheet interface that ultimately injects <style> tags into the page.

Frequently Asked Questions

What is the difference between element hiding and network filtering in uBlock Origin?

Element hiding operates on the DOM using CSS selectors to visually remove elements, while network filtering intercepts HTTP requests before they reach the browser. Element hiders use the ## syntax and work entirely within content scripts, whereas network filters use || or | syntax and function at the browser's webRequest API level.

How does uBlock Origin compile ## filters into CSS?

In src/js/cosmetic-filtering.js, the CosmeticFilteringEngine processes ## filters through methods like compileGenericHideSelector and compileSpecificSelector. These methods wrap the raw selector in a CSS rule template ({display:none!important;}) and organize them into buckets for efficient retrieval based on hostname hashes or generic applicability.

Where are element hider styles injected in the DOM?

The DOMFilterer class in src/js/contentscript.js (lines 69-105) manages injection through vAPI.userStylesheet.add(), which creates and inserts a <style> element into the document. This occurs after the content script receives compiled CSS from the background process during the bootstrap phase.

Can exception filters disable specific element hiders?

Yes. Filters using the #@# syntax create exceptions that prevent specific ## rules from applying. According to the source code in static-ext-filtering.js, these exceptions are stored separately and passed to domFilterer.exceptCSSRules, which removes the corresponding CSS from the active stylesheet before injection.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →