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:
- Bootstrap:
vAPI.bootstraprequests cosmetic filter data from the background process. - Data Transfer: The response contains
cfeDetails.injectedCSSwith specific and pre-computed generic selectors. - Instantiation: The content script creates a
DOMFiltererand callsaddCSSwith the received stylesheet (lines 96-99 incontentscript.js). - Registration:
DOMFiltererregisters the CSS withvAPI.userStylesheet. - Injection:
vAPI.userStylesheet.apply()inserts a<style>element that hides targeted elements. - 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), andcontentscript.js(injection). - Specific hiders target individual hostnames, while generic hiders apply broadly and rely on the
domSurveyorfor efficient matching. - The
DOMFiltererclass manages thevAPI.userStylesheetinterface 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →