5 Custom AdBlock Extensions
Built for Amazon, LinkedIn, CNBC, etc.
5 Custom AdBlock Extensions
(Read with no paywall here.)
It’s hard to imagine an Internet without ads. Google, Amazon, Microsoft, and Meta together make over half a trillion dollars in annual revenue by selling targeted ad placements. Media platforms like Spotify, Netflix, X, and TikTok also generate billions from ads. While “sponsored content” is basically useless to users, it enables businesses to keep the lights on and lead massive infrastructure builds like the ones currently underway.
For years I have used Chrome extensions like uBlock and alternative browsers like Brave to keep some sponsored content at bay. But as ads become more embedded into Internet services, they have become harder to remove without triggering anti-adblock popups or breaking the page. And while unique platforms like Medium are able to avoid ad services altogether, the more established tech and media firms don’t seem able to consider alternative business models which could better benefit society. Hence the constant game of cat-and-mouse.
I’ve always wanted to build my own browser extensions and online ads presented a great use case to explore. Many websites have custom ads which require custom-built solutions to target and remove. Tonight I’m excited to share 5 local extensions that are ready to use. I’ve selected some websites which, in my opinion, have an annoying and excessive amount of ads: Amazon, LinkedIn, Reddit, CNBC, and Morning Brew (which is primarily known for its email newsletters).
Each adblock extension is scoped as narrowly as possible for its purpose. The Amazon, LinkedIn, Reddit, and CNBC extensions run individually on these websites to remove ads and other promotional content, whereas the Morning Brew extension runs on Gmail to clean up Morning Brew emails when they are opened.
These extensions are unaffiliated with the websites named here and are designed for local, personal use. As written, they do not send browsing data, identifiers, or page contents to me or any remote server. They may violate some websites’ terms of service, so you are responsible for reviewing the code and deciding whether, if, and how to use them.
Loading a Local Chrome Extension
Each extension directory, like amazon-ad-blocker, will contain 1 config file and 1 script file as shown:
/{website}-ad-blocker (e.g. amazon-ad-blocker)
├── manifest.json
├── content.js
The JSON and JavaScript files for each extension have been pasted below and separated into 5 sections for maintainability. Once you’ve saved the respective files for a website, follow the steps below to load and enable the extension on Chrome:
- Head to the URL
chrome://extensions/ - Click the “Load unpacked” button
- Choose the extension directory, e.g.
amazon-ad-blocker - Click “Select”
- Optional: Click “Details” on the new extension pane, scroll down, and toggle the “Allow in Incognito” switch to enable in private windows

Each extension should work automatically once it’s loaded in, and each has limited access to only the relevant website. So the Amazon ad blocker will only be able to view and change components on amazon.com, the LinkedIn ad blocker will only work on linkedin.com, and so on.
Amazon
As someone who doesn’t order a lot of things online, I was surprised to see how many sponsored products are shown on Amazon.com. Even when I zoomed out to take the picture below (left), the initial page showing my search results screen was completely covered in ads. The top row is for a company with which I’m not familiar. Under that is a row of sponsored products. The top of every section has a row of ads, the bottom row on the page is a bunch of ads, and there are a couple banner ads running along the sides of the page.


Before and after enabling the custom Amazon adblock extension. (Source: Amazon.com)
Overall these may be relevant suggestions, and companies like Amazon are incentivized to list them for both monetization and customer satisfaction. I’m just not a fan of clicking on ads because I feel like sponsored products may not be best and most reliable products available to me. I, of course, would trust hundreds or thousands of positive human reviews over a single positive “hit” from a recommendation algorithm.
So all sponsored products are now being removed on my end when I visit Amazon.com. As you can see in the above image (right), when I make a search with the extension enabled, I can immediately see the list of product recommendations without having to scroll past any ads. There’s still an algorithm behind these suggestions, but at least visually, it’s clear that the products are genuinely relevant to my needs and not to some advertisement deal taking place in the backend.
This extension works for all pages on Amazon, and it was built in just a few minutes with ChatGPT. All I did was upload the manifest.json and content.js files I created a few months ago for LinkedIn and list the new requirements for Amazon by copying/pasting every unique HTML component, covering all the ad sections I saw on the website.
The steps to load and enable an extension are at the top of this story. Save these 2 files in a folder called amazon-ad-blocker and follow the steps listed above to try it out.
manifest.json
{
"manifest_version": 3,
"name": "Amazon Ad Blocker",
"version": "1.0.0",
"description": "Removes sponsored Amazon search results and ad sections.",
"content_scripts": [
{
"matches": [
"*://amazon.com/*",
"*://*.amazon.com/*"
],
"js": ["content.js"],
"run_at": "document_start",
"all_frames": false
}
],
"permissions": []
}
content.js
(function() {
'use strict';
const REMOVED_ATTR = 'data-amazon-ad-blocker-removed';
const FAST_HIDE_CSS = `
.s-result-item.AdHolder,
.s-left-ads-item,
[data-cel-widget*="adplacements"],
[cel_widget_id*="adplacements"],
[data-cel-widget*="advertising"],
[cel_widget_id*="advertising"],
[data-cel-widget*="sp-rhf"],
[cel_widget_id*="sp-rhf"],
[data-card-metrics-id*="sp-rhf"],
[data-cel-widget*="multi-brand-creative"],
[cel_widget_id*="multi-brand-creative"],
[data-card-metrics-id*="multi-brand-creative"],
.ape-placement,
iframe[title*="Sponsored ad" i] {
display: none !important;
}
`;
const SPONSORED_TEXT_SELECTORS = [
'span',
'a',
'[aria-label*="Sponsored" i]',
'[aria-label*="sponsored" i]'
].join(',');
const SPONSORED_MARKER_SELECTORS = [
'.s-result-item.AdHolder',
'.s-left-ads-item',
'[data-component-props*="SponsoredProductsEventTracking"]',
'[data-component-props*="sponsoredProductsLoom"]',
'[data-component-props*="sp_search"]',
'[data-component-props*="sp_atf"]',
'[data-component-props*="sp_rhf"]',
'[data-cel-widget*="adplacements"]',
'[cel_widget_id*="adplacements"]',
'[data-cel-widget*="advertising"]',
'[cel_widget_id*="advertising"]',
'[data-cel-widget*="sp-rhf"]',
'[cel_widget_id*="sp-rhf"]',
'[data-card-metrics-id*="sp-rhf"]',
'[data-cel-widget*="multi-brand-creative"]',
'[cel_widget_id*="multi-brand-creative"]',
'[data-card-metrics-id*="multi-brand-creative"]',
'[href*="/sspa/"]',
'[href*="sspa_dk_"]',
'[href*="sp_csd="]',
'[href*="aax-us-east-retail-direct.amazon.com"]',
'[href*="amazon-adsystem.com"]',
'[src*="amazon-adsystem.com"]',
'[id^="ape_"][id*="_placement"]',
'.ape-placement',
'iframe[title*="Sponsored ad" i]',
'[aria-label^="Sponsored Ad" i]',
'[aria-label*="Sponsored ad" i]',
'[aria-label*="Leave feedback on Sponsored ad" i]',
'[aria-label*="View Sponsored information" i]'
].join(',');
const CONTAINER_SELECTORS = [
'.s-left-ads-item',
'[data-component-type="s-search-result"].s-result-item',
'[data-asin][class~="s-result-item"]',
'.s-result-item',
'[data-cel-widget*="sp-rhf"]',
'[cel_widget_id*="sp-rhf"]',
'[data-card-metrics-id*="sp-rhf"]',
'[data-cel-widget*="adplacements"]',
'[cel_widget_id*="adplacements"]',
'[data-cel-widget*="advertising"]',
'[cel_widget_id*="advertising"]',
'[data-cel-widget*="multi-brand-creative"]',
'[cel_widget_id*="multi-brand-creative"]',
'[data-card-metrics-id*="multi-brand-creative"]',
'[id^="ape_"][id*="_placement"]',
'.ape-placement',
'.celwidget'
];
function addFastHideStyle() {
const style = document.createElement('style');
style.textContent = FAST_HIDE_CSS;
(document.head || document.documentElement).appendChild(style);
}
function cleanText(value) {
return (value || '').replace(/\s+/g, ' ').trim();
}
function isSponsoredLabel(el) {
if (!el || el.nodeType !== Node.ELEMENT_NODE) return false;
const text = cleanText(el.textContent);
const aria = cleanText(el.getAttribute('aria-label'));
if (/^Sponsored$/i.test(text)) return true;
if (/^Sponsored Ad\b/i.test(aria)) return true;
if (/\bSponsored ad\b/i.test(aria)) return true;
if (/View Sponsored information/i.test(aria)) return true;
if (/Leave feedback on Sponsored ad/i.test(aria)) return true;
return false;
}
function hasSponsoredMarker(el) {
if (!el || el.nodeType !== Node.ELEMENT_NODE) return false;
if (el.matches(SPONSORED_MARKER_SELECTORS)) return true;
return Boolean(el.querySelector(SPONSORED_MARKER_SELECTORS));
}
function bestContainer(el) {
for (const selector of CONTAINER_SELECTORS) {
const container = el.closest(selector);
if (isRemovable(container)) return container;
}
return null;
}
function isRemovable(el) {
return Boolean(
el &&
el.nodeType === Node.ELEMENT_NODE &&
el !== document.documentElement &&
el !== document.body &&
el.parentNode &&
!el.hasAttribute(REMOVED_ATTR)
);
}
function removeContainer(el) {
const container = bestContainer(el);
if (!container) return false;
container.setAttribute(REMOVED_ATTR, 'true');
container.remove();
return true;
}
function scanSponsoredLabels(root) {
if (!root.querySelectorAll) return;
if (isSponsoredLabel(root)) {
removeContainer(root);
return;
}
for (const el of root.querySelectorAll(SPONSORED_TEXT_SELECTORS)) {
if (isSponsoredLabel(el)) removeContainer(el);
}
}
function scanMarkers(root) {
if (!root.querySelectorAll) return;
if (hasSponsoredMarker(root)) {
removeContainer(root);
}
for (const el of root.querySelectorAll(SPONSORED_MARKER_SELECTORS)) {
removeContainer(el);
}
}
function scan(root) {
if (!root || root.nodeType !== Node.ELEMENT_NODE && root !== document) return;
scanMarkers(root);
scanSponsoredLabels(root);
}
function observe() {
if (!document.body) return;
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) scan(node);
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
addFastHideStyle();
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
scan(document);
observe();
}, { once: true });
} else {
scan(document);
observe();
}
})();
This is actually the website which inspired me to start making ad-blocker extensions in the first place. I’m not really sure why I still use LinkedIn (maybe just to check my messages), because there isn’t much useful information on my feed. Job recruitment in general seems like a heavily flawed process, especially when it’s done over a website like this one.
Also for some unknowable reason, I chose to pay for LinkedIn Premium for several years and only canceled my subscription after calculating my total fees to be over $1,000. I soon found after canceling the service that I would be facing a lot more promoted content than I did as a paid user.
What’s most annoying about these sponsored posts is that they are extremely repetitive. I’m not sure if there just aren’t enough advertisers on the platform to create an interesting experience, but anyway, here’s some code if you’d like to stop seeing ads on LinkedIn. Feel free to save these files to a linkedin-ad-blocker folder on your machine and see the above steps to load the extension.
manifest.json
{
"manifest_version": 3,
"name": "LinkedIn Ad Blocker",
"version": "1.0",
"description": "Blocks promoted/sponsored posts on LinkedIn feed",
"content_scripts": [
{
"matches": ["*://*.linkedin.com/*"],
"js": ["content.js"],
"run_at": "document_start",
"all_frames": false
}
],
"permissions": []
}
content.js
(function() {
'use strict';
// Detect if a <p> element is a promoted label (short text starting with "Promoted")
function isPromotedLabel(el) {
if (el?.tagName !== 'P') return false;
const text = el.textContent.trim();
return text.startsWith('Promoted') && text.length < 100;
}
// Find the feed item container to remove
function findFeedItem(el) {
return el?.closest?.('[role="listitem"]') ||
el?.closest?.('div[data-display-contents="true"]') ||
el?.closest?.('div._45cac3d2.e828c08e');
}
// Remove the promoted post safely
function removePromoted(el) {
const item = findFeedItem(el);
if (item?.parentNode) {
item.parentNode.removeChild(item);
return true;
}
return false;
}
// Scan a root element for promoted posts
function scan(root) {
if (!root?.querySelectorAll) return;
for (const p of root.querySelectorAll('p')) {
if (isPromotedLabel(p)) removePromoted(p);
}
}
// Handle newly added DOM nodes
function handleNodes(nodes) {
for (const node of nodes) {
if (node.nodeType !== Node.ELEMENT_NODE) continue;
if (isPromotedLabel(node)) {
removePromoted(node);
continue;
}
if (node.querySelector) {
for (const p of node.querySelectorAll('p')) {
if (isPromotedLabel(p)) {
removePromoted(p);
break;
}
}
}
}
}
// Initial scan
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => scan(document));
} else {
scan(document);
}
// Observe dynamic content (infinite scroll)
const observer = new MutationObserver((mutations) => {
for (const m of mutations) {
if (m.addedNodes?.length) handleNodes(m.addedNodes);
}
});
const startObserving = () => {
if (document.body) {
observer.observe(document.body, { childList: true, subtree: true });
}
};
if (document.body) {
startObserving();
} else {
document.addEventListener('DOMContentLoaded', startObserving);
}
})();
I don’t use Reddit often, but when I do, I see too many ads. Here’s the code to handle them cleanly. These files can be saved to a reddit-ad-blocker folder and loaded in your Chrome app.
manifest.json
{
"manifest_version": 3,
"name": "Reddit Ad Blocker",
"version": "1.0.0",
"description": "Removes promoted Reddit feed posts and their dividers.",
"content_scripts": [
{
"matches": [
"*://reddit.com/*",
"*://*.reddit.com/*"
],
"js": ["content.js"],
"run_at": "document_start",
"all_frames": false
}
],
"permissions": []
}
content.js
(function() {
'use strict';
const REMOVED_ATTR = 'data-reddit-ad-blocker-removed';
const AD_SELECTORS = [
'shreddit-ad-post',
'shreddit-ad-post[promoted]',
'shreddit-ad-post[ad-type]',
'shreddit-ad-post[ad-events]',
'shreddit-ad-post.promotedlink',
'article[promoted]',
'article[data-promoted]',
'article[ad-type]',
'article[ad-events]',
'article.promotedlink'
].join(',');
const FAST_HIDE_CSS = `
${AD_SELECTORS},
shreddit-ad-post + hr,
shreddit-ad-post[promoted] + hr,
shreddit-ad-post[ad-type] + hr,
shreddit-ad-post[ad-events] + hr,
shreddit-ad-post.promotedlink + hr,
article[promoted] + hr,
article[data-promoted] + hr,
article[ad-type] + hr,
article[ad-events] + hr,
article.promotedlink + hr {
display: none !important;
}
`;
function addFastHideStyle() {
const style = document.createElement('style');
style.textContent = FAST_HIDE_CSS;
(document.head || document.documentElement).appendChild(style);
}
function isFeedDivider(el) {
if (!el || el.nodeType !== Node.ELEMENT_NODE || el.tagName !== 'HR') return false;
const classes = el.classList;
return (
classes.contains('border-neutral-border-weak') ||
classes.contains('border-b-neutral-border-weak') ||
classes.contains('border-solid') ||
classes.contains('border-b-sm')
);
}
function isPromotedLabel(el) {
if (!el || el.nodeType !== Node.ELEMENT_NODE) return false;
if (!el.classList.contains('promoted-label')) return false;
return el.textContent.trim().toLowerCase() === 'promoted';
}
function adContainerFromLabel(label) {
return label.closest('shreddit-ad-post, article[promoted], article[data-promoted], article[ad-type], article[ad-events], article.promotedlink');
}
function removeAd(ad) {
if (!ad || ad.nodeType !== Node.ELEMENT_NODE || !ad.parentNode || ad.hasAttribute(REMOVED_ATTR)) return;
const next = ad.nextElementSibling;
ad.setAttribute(REMOVED_ATTR, 'true');
ad.remove();
if (isFeedDivider(next)) {
next.remove();
}
}
function scan(root) {
if (!root || !root.querySelectorAll) return;
if (root.matches && root.matches(AD_SELECTORS)) {
removeAd(root);
return;
}
for (const ad of root.querySelectorAll(AD_SELECTORS)) {
removeAd(ad);
}
if (isPromotedLabel(root)) {
removeAd(adContainerFromLabel(root));
}
for (const label of root.querySelectorAll('.promoted-label')) {
if (isPromotedLabel(label)) removeAd(adContainerFromLabel(label));
}
}
function startObserver() {
if (!document.body) return;
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) scan(node);
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
addFastHideStyle();
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
scan(document);
startObserver();
}, { once: true });
} else {
scan(document);
startObserver();
}
})();
CNBC
I built this one with my dad in mind, because in the past couple of years we’ve bonded over our shared annoyance for all of the paid offerings shown on CNBC. This was probably the hardest extension to build since there are so many different articles, sections, and links that are essentially a waste of space for regular users.
If you visit this website frequently, you’ll see how much Exclusive, Pro, and CNBC+ content is now missing. To me this is like a breath of fresh air.

A demo displaying the CNBC web interface with no promoted content. (Source: CNBC.com)
Save the following files to a folder called cnbc-ad-blocker and follow the steps above to load and enable the extension in your browser.
manifest.json
{
"manifest_version": 3,
"name": "CNBC Ad Blocker",
"version": "1.0",
"description": "Removes Pro and Investing Club ads from CNBC.",
"content_scripts": [
{
"matches": [
"*://*.cnbc.com/*"
],
"js": [
"content.js"
],
"run_at": "document_start"
}
]
}
content.js
(() => {
'use strict';
// 1. Aggressive CSS Injection
const style = document.createElement('style');
style.textContent = `
/* Top Nav, Mega Menu, and Promo Sections */
.PortfolioBannerAd-adContainer,
.nav-menu-primaryLink.investing_club, .nav-menu-primaryLink.pro, .nav-menu-primaryLink.livestream, [data-testid="livestream"], .nav-menu-investingClubPillIcon, .nav-menu-proPillIcon, .pillIcon,
li:has(> .EditionMenu-featuredMenuButton), .EditionMenu-featuredMenuButton, li.featured-menu-button:has(a[href*="/make-it/"]),
.mega-links-item:has([data-testid="/investingclub/"]), .mega-links-item:has([data-testid="/pro/"]), .mega-links-item:has([data-testid="/select/"]),
span[class*="transition-fade"]:has(section[id*="ProNewsandAnalysis"]), span[class*="transition-fade"]:has(section[id*="InvestingClub"]), span[class*="transition-fade"]:has(section[id*="CNBCSelect"]),
.TwoColumnImageDense-pageRow:has(section[id*="ProNewsandAnalysis"]), .TwoColumnImageDense-pageRow:has(section[id*="InvestingClub"]), .TwoColumnImageDense-pageRow:has(section[id*="CNBCSelect"]),
[id^="Homepage-riverPlus"]:has(.ProPill-proPillLink), [id^="Homepage-riverPlus"]:has(.InvestingClubPill-investingClubPillLink),
.CNBCFooter-social,
/* Target QuickLinks by title */
.QuickLinks-quickLink:has(a[title^="Pro:"]), .QuickLinks-quickLink:has(a[title^="Club:"]),
a[title^="Pro:"], a[title^="Club:"],
/* Target the Latest News items (Sidebar and Main Feed) */
.LatestNews-item:has(.InvestingClubPill-investingClubPillLink),
.LatestNews-item:has(.ProPill-proPillLink),
.LatestNews-item:has(a[title^="Pro:"]),
.LatestNews-item:has(a[title^="Club:"]),
/* Catch-all for standalone Pro/Club Cards */
.Card-pro,
[class*="Card"]:has(.ProPill-proPillLink),
[class*="Card"]:has(.InvestingClubPill-investingClubPillLink),
div:has(> div > .ProPill-proPillLink),
div:has(> div > .InvestingClubPill-investingClubPillLink) {
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
height: 0 !important;
overflow: hidden !important;
}
`;
document.documentElement.appendChild(style);
// 2. JavaScript Watcher (Handles dynamic text and Iframes)
const cleanDynamicDOM = () => {
// Hide promotional offer modules
if (window !== window.top) {
if (document.getElementById('view-offer')) document.body.style.display = 'none';
return;
}
// Hide iframe parent wrappers
document.querySelectorAll('iframe').forEach(iframe => {
if (iframe.id.startsWith('offer-') || iframe.src.includes('offer')) {
const wrapper = iframe.parentElement;
if (wrapper) wrapper.style.display = 'none';
}
});
// Final sweep for text-based prefixes in any list or headline
const targetPrefixes = ['Pro:', 'Club:', 'Subscribe to'];
const selectors = '.CNBCFooter-listItem, .QuickLinks-quickLink, .Card-title, .RiverHeadline-headline, .LatestNews-item';
document.querySelectorAll(selectors).forEach(el => {
const text = el.textContent.trim();
if (targetPrefixes.some(prefix => text.startsWith(prefix))) {
// Hide the closest card/item wrapper instead of just the text element
const wrapper = el.closest('[class*="Card"], .LatestNews-item, .QuickLinks-quickLink, .CNBCFooter-listItem, .RiverHeadline-headline') || el;
wrapper.style.display = 'none';
}
});
};
// Run immediately and observe for "View More" or scroll-loading
cleanDynamicDOM();
new MutationObserver(cleanDynamicDOM).observe(document.documentElement, {
childList: true,
subtree: true
});
})();
Morning Brew
I’ve saved the best for last. I really respect this company, have been reading its email newsletters for over 7 years, and enjoy a lot of the video content I’ve been seeing from Morning Brew recently. But unfortunately there are too many ads on their emails, and I believe the frequency has been increasing in size and frequency.
Interestingly, I have no recollection of clicking on, or even remembering, any of these ads. I think I just scroll right past them with my blinders up, and in rare cases I get momentarily tripped up by the embedded ads content. So it’s not like by creating an ad blocker I’m meaningfully reducing revenue or attention for the company.
Still, I felt kind of bad for building this on a product that I genuinely like, so I went ahead and purchased a hoodie on the Morning Brew Store to make up for it. If you implement this extension I’d ask that you consider doing the same. Their newsletters are pretty great, and even better when the ads are removed. (This is not a sponsored post, though that would be funny.)


Before and after enabling the Morning Brew adblock extension. (Source: Morning Brew, Gmail)
Once again, you can write the extension to a morningbrew-ad-blocker folder and enable it for your Gmail inbox by following the “Loading a Local Chrome Extension” section above.
manifest.json
{
"manifest_version": 3,
"name": "Morning Brew Ad Blocker",
"version": "1.0",
"content_scripts": [{
"matches": ["https://mail.google.com/*"],
"js": ["content.js"],
"run_at": "document_idle"
}],
"permissions": []
}
content.js
(() => {
'use strict';
let lastEmailId = null;
const getEmailId = () => {
const match = location.hash.match(/\/([A-Za-z0-9_-]{16,})$/);
return match ? match[1] : null;
};
const cleanEmail = () => {
const emailId = getEmailId();
if (!emailId) {
lastEmailId = null;
return;
}
const emailBody = document.querySelector('div[role="main"] .ii.gt');
if (!emailBody || emailId === lastEmailId) return;
if (!emailBody.textContent.includes('Morning Brew Inc. All rights reserved.')) return;
// 1. Surgical Strike on Headers, Banners & Mini-Widgets
emailBody.querySelectorAll('td, div, span, p, h3, strong').forEach(el => {
const trimmedText = el.textContent.trim();
if (trimmedText.length > 0 && trimmedText.length < 150) {
const txt = trimmedText.toUpperCase();
// Handle Sponsor Headers
if (txt === 'SPONSORED BY' || txt === 'PRESENTED BY' || txt === 'TOGETHER WITH' || txt.includes('SPONSORED BY')) {
const wrapperDiv = el.closest('div[style*="background-color"]');
const tr = el.closest('tr');
if (wrapperDiv && wrapperDiv.textContent.length < 400) {
wrapperDiv.style.display = 'none';
} else if (tr && tr.textContent.length < 400) {
tr.style.display = 'none';
const nextTr = tr.nextElementSibling;
if (nextTr && nextTr.textContent.trim().length < 150) {
nextTr.style.display = 'none';
}
} else {
el.style.display = 'none';
}
}
// Remove the "Data is provided by Yahoo Finance" block
if (txt === 'DATA IS PROVIDED BY') {
const table = el.closest('table');
if (table && table.textContent.length < 100) {
table.style.display = 'none';
}
}
// Referral Blocks & Specific Banners (Resource Roundup)
if (txt === 'SHARE THE BREW' || txt.includes('RESOURCES FROM OUR SPONSORS')) {
const outerTable = el.closest('table');
if (outerTable && outerTable.textContent.length < 2000) {
const wrapperDiv = outerTable.closest('div[style*="margin-bottom"]');
if (wrapperDiv && wrapperDiv.textContent.length < 2000) {
wrapperDiv.style.display = 'none';
} else {
outerTable.style.display = 'none';
}
} else {
el.style.display = 'none';
}
}
}
});
// 2. Hide Dedicated Middle/Bottom Sponsor Tables
const tables = Array.from(emailBody.querySelectorAll('table'));
tables.forEach(t => {
const text = t.textContent.trim().toUpperCase();
const first200 = text.substring(0, 200);
const isDedicatedSponsor = first200.includes('PRESENTED BY') || first200.includes('SPONSORED BY') || first200.includes('TOGETHER WITH') || text.includes('BREW.COM/R/');
if (isDedicatedSponsor) {
const hasInnerMatch = Array.from(t.querySelectorAll('table')).some(inner => {
const innerText = inner.textContent.toUpperCase();
return innerText.includes('PRESENTED BY') || innerText.includes('SPONSORED BY') || innerText.includes('TOGETHER WITH');
});
const isEditorial = text.includes('-WHIZY') || text.includes('-SAIRA') || text.includes('-NEAL');
if (!hasInnerMatch && t.textContent.length < 800 && !isEditorial) {
t.style.display = 'none';
}
}
});
// 3. Hide "A Note From" Ad Blocks
emailBody.querySelectorAll('strong, b, p, span').forEach(el => {
const trimmedText = el.textContent.trim();
if (trimmedText.includes('A Note From') && trimmedText.length < 100) {
const container = el.closest('div[style*="margin-bottom"]') || el.closest('table');
if (container && container !== emailBody && container.textContent.length < 3000) {
container.style.display = 'none';
}
}
});
// --- 4. Final State-Based Asterisk Scrub ---
const textElements = Array.from(emailBody.querySelectorAll('p, li, td, div, span'));
textElements.forEach(el => {
if (el.querySelector('p, li')) return;
if (el.textContent.length > 1500) return;
const text = el.textContent.trim();
const lower = text.toLowerCase();
const isDisclaimer = /^\*+/.test(text) && (lower.includes('sponsor') || lower.includes('recommendation') || lower.includes('writers') || lower.includes('commission'));
if (isDisclaimer) {
const p = el.closest('p');
if (p) p.style.display = 'none';
else el.style.display = 'none';
return;
}
if (text.length > 0) {
const match = text.match(/(\*+)[\s\u200B\uFEFF]*$/);
if (match) {
const li = el.closest('li');
const p = el.closest('p');
if (li) {
li.style.display = 'none';
} else if (p) {
p.style.display = 'none';
} else {
el.style.display = 'none';
}
}
}
});
// 5. Social Media clusters (Link-heavy, text-light)
emailBody.querySelectorAll('tr, td, table').forEach(el => {
const links = el.querySelectorAll('a');
if (links.length >= 2) {
const text = el.textContent.replace(/\s+/g, '').trim();
if (text.length < 3) {
el.style.display = 'none';
}
}
});
// 6. Clean Footer (Preserving "Written by" & the bottom border)
const footerTable = tables.findLast(t => t.textContent.includes('Morning Brew Inc. All rights reserved.'));
if (footerTable) {
const rows = Array.from(footerTable.rows);
rows.forEach((row, index) => {
// Always keep the very last row (ADVERTISE // CAREERS // SHOP // FAQ)
if (index === rows.length - 1) return;
// Check if this row contains the "Written by" block
if (row.textContent.includes('Written by')) {
const writtenByCell = Array.from(row.querySelectorAll('td')).find(td => td.textContent.trim().startsWith('Written by'));
if (writtenByCell) {
// Hide all children inside this cell EXCEPT the paragraph that actually says "Written by"
Array.from(writtenByCell.children).forEach(child => {
if (!child.textContent.trim().startsWith('Written by')) {
child.style.display = 'none';
}
});
}
return; // Do not hide this row
}
// Hide any other promotional/spacer rows in the footer block
row.style.display = 'none';
});
}
// 7. Clean Top Utility Navigation
emailBody.querySelectorAll('td, div, p').forEach(el => {
const text = el.textContent.trim();
if (text.includes('View Online') && text.includes('Shop') && text.length < 100) {
const viewOnlineLink = Array.from(el.querySelectorAll('a')).find(a => a.textContent.trim() === 'View Online');
if (viewOnlineLink) {
el.innerHTML = '';
el.appendChild(viewOnlineLink);
}
}
});
lastEmailId = emailId;
};
const observer = new MutationObserver(() => {
const emailBody = document.querySelector('div[role="main"] .ii.gt');
if (emailBody) cleanEmail();
});
observer.observe(document.body, { childList: true, subtree: true });
window.addEventListener('hashchange', () => setTimeout(cleanEmail, 1000));
})();
I hope that these tools provide you with a slightly better user experience as you traverse this very cluttered Internet. Thank you for following along!
- Amal
메타데이터
- post_id
- caebfd154bcd
- slug
- 5-custom-adblock-extensions-caebfd154bcd
- url
- https://medium.com/@amaltyagi/5-custom-adblock-extensions-caebfd154bcd
- canonical_url
- https://medium.com/@amaltyagi/5-custom-adblock-extensions-caebfd154bcd
- author_url
- https://medium.com/@amaltyagi
- status
- ok
- fetched_at
- 2026-07-09 13:32:43