# What Are Element Hiders in uBlock Origin Content Scripts?

> Discover how element hiders in uBlock Origin content scripts use CSS rules to hide unwanted DOM elements. Learn to customize your browsing experience.

- Repository: [Raymond Hill/uBlock](https://github.com/gorhill/uBlock)
- Tags: internals
- Published: 2026-03-05

---

**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`](https://github.com/gorhill/uBlock/blob/main/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`](https://github.com/gorhill/uBlock/blob/main/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`](https://github.com/gorhill/uBlock/blob/main/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`](https://github.com/gorhill/uBlock/blob/main/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`](https://github.com/gorhill/uBlock/blob/main/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

```javascript
// 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

```javascript
// 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

```javascript
// 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`](https://github.com/gorhill/uBlock/blob/main/static-ext-filtering.js) (parsing), [`cosmetic-filtering.js`](https://github.com/gorhill/uBlock/blob/main/cosmetic-filtering.js) (compilation), and [`contentscript.js`](https://github.com/gorhill/uBlock/blob/main/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`](https://github.com/gorhill/uBlock/blob/main/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`](https://github.com/gorhill/uBlock/blob/main/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`](https://github.com/gorhill/uBlock/blob/main/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.