Možná si myslíš, že prefix ad- v CSS je neškodný. Vždyť je to jen pár písmen, jasně čitelné i pro juniory. Jenže právě těch pár písmen dokáže tvému frontendu udělat dokonale prázdnou stránku.
Ještě horší? O chybě se dozvíš až od zuřivého klienta. V produkci. V pátek odpoledne. S GA4 tichým jako pěna, protože se nenačetl ani pixel.
Takže: chceš-li přežít a zachovat si UX (a duševní zdraví), čti dál. Dnes ti ukážu, proč ad-blokátory loví všechno, co zavání reklamou, kde přesně se pálíme nejčastěji a jak se tomu vyhnout bez šarlatánství typu „anti-ad-block skript“.
Audio verze článku
Délka záznamu 8 minut
Proč na tom záleží?
EasyList – základní muničák většiny ad-blokátorů – obsahuje tisíce pravidel, která se spouštějí ještě dřív, než blikne první pixel tvého layoutu. A mezi nimi i zdánlivě nevinné řádky jako:
1 2 3 | ##[id^="ad-"] ##[class*=" ads-"] |
Pozn. ## je zápis z adblockeru, normální css selector je bez něj.
Tahle regexová kudla skryje každý element, jehož id začíná na ad- nebo class obsahuje samostatné „ads“ .
Podobně síťové filtry stopnou requesty na URL typu /ads/, /banner/, nebo subdoménu ads.example.com (easylist.to). Výsledek? Soubor se nenačte, JavaScript hodí chybu, a ty dumáš, proč je komponenta rozbitá jen „u některých uživatelů“.
Varování: nejsou to jen ID a class
Blokátory dnes umí „kosmetické“ filtry i nad textovým obsahem – třeba :-abp-contains() schová <span>Reklama</span> čistě podle vnitřního textu (help.adblockplus.org). Jednoduché? Ani náhodou.
Jak loví blokátory tvé selektory?
- CSS injection – v manifestu extension se přidá stylopis s globálními selektory (viz výše).
- URL rewrite & cancel – pokud request odpovídá filtru, padá rovnou na síťové vrstvě.
- Scriptlet hijacking – uBlock Origin dovede vložit JS, jenž selektivně ruší či mění DOM.
Bacha, tady se chybuje nejčastěji: Když prvek nejdřív není v DOMu (lazy render) a ad-blokátor mezitím stihl nasypat filtr, skryje se okamžitě po injectu. Přepočet layoutu ti pak celé UI posune a pixel-perfektní grid je v háji.
Příklady z praxe
- U projektu, který prodává SaaS pro SMB, bylo tlačítko „Add-ons“ (
id="addons_btn"). Nevinné, že? Dostali skvělý nápad mu dát novou tříduclass="ads-variant"a konverze spadla o 32 %. Proč? Uživatelé s uBlockem tlačítko prostě neviděli. Víte jak se to blbě debaguje 🙂 ? - Několikrát jsem jsem viděl, třeba subdoménu pro Server GTM pojmenovanou GTM, analytics, 🙂 etc. a pak takové server GTM je blokované asi i více než to základní 🙂 . Proto doporučuji do takových subdomén dávat jména jako „chleba“, „zarovka“, „auto“ a nebo cokoliv náhodného, ale rozhodně nic spojeného s analytikou nebo reklamou.
- Znám projekt kde dali do názvu hlavní domény slovíčko analytics a pokud jste kliknuli na odkaz směřující na tuto doménu, tak vám to házelo varování 🙂 . To nebylo dobry, myslím si že ten projekt časem umřel.
- Tip od Robin Pokorný: I generovaný hash class HTML prvku může obsahovat slov „ad“ třeba hodnota „ad01“. Aneb další malá podmínku do kódu 🙂 .
- Tip od Martin Maly: Bacha i na názvy cookies, web vytvářel cookies obsahující ve jméně „ident“ a ty byli u uživatelů v prohlížeči mazány. Za mě i bacha na názvy jako „ga“, „analytics“ „id“ u názvů cookies, občas jsou bloknuté taky.
- A jelikož existují i blokátory cookie lišt, tak je velmi dobré se v nich vyhnout názvům a ID jako je cookie, consent etc. Aneb uživatel, co ani neuvidí vaši cookie lištu, tak vám jistě souhlas nedá.
Kde děláme chyby nejčastěji?
- Adresářová struktura:
/static/**ads**/banner.png - Hasherem generované ID:
gtm-ad-1234 - WordPress šablony:
theme-adwise,widget_banner - Alt a title:
alt="Advertisement" - Data atributy:
data-ad-slot="header" - Subdoména:
ads.mydomain.cz
A vždy stejný scénář: „U mě na lokále to funguje, v produkci u 30 % návštěv ne.“
Jak z toho ven?
1. Pojmenuj to jinak
Používej projektově-specifické aliasy: místo ad-slot třeba slotHero, slotSidemenu. Uvnitř slova nevadí (he*ad*er, ro*ad*map) – filtry většinou vyžadují hranice slova.
2. Hashuj cesty k assetům
Pokud CDN vyžaduje strukturu /img/ads/…, zaveď reverse-proxy přepis na /img/a1b2c3/…. Je to jeden Nginx rewrite a máš klid.
Z praxe webového analytika: Dříve se dokonce detekovali adblockery přes soubor pojmenovaný „ads.js“ a pokud se nenašetl a nezapsala se proměnné, tak jste věděli, že uživatel blokuje reklamu.
3. Odděl semantiku od layoutu
Potřebuješ-li prvek označit kvůli legislativnímu „Sponsored” štítku, dej mu dvě třídy:
1 2 | <span class="slotA1 sponsorLabel">Sponsored</span> |
Při stylování počítej, že sponsorLabel může zmizet – velikost a padding nech raději na rodiči.
4. Detect & fallback
1 2 3 4 5 6 | const slot = document.querySelector('#slotHero'); if (!slot || slot.offsetHeight === 0) { console.warn('Adblock? Switching to fallback.'); renderFallback(slot); } |
Žádná detekce API? Využij starý dobrý DOM.
Začni hned – budoucí já ti poděkuje.
Checklist před releasem
- Audit názvů – projdi build artefakty (
grep -R --line-number -E '\\b(ad|banner|promo|sponsor|analytics|tracking|gtm)\\b' dist/). - Lokální test – nainstaluj uBlock Origin a AdGuard. Obojí, občas mají různé feedy.
- Spusť Lighthouse + ad-block – sleduj rozdíl v JS errors.
- Check GA4 hits – trafí měření i s blokátorem? Pokud ne, zvaž server-side GTM proxy.
- E2E test na CI – Cypress + uBlock ve headless Chrome.
- Monitoruješ rozbitý layout? – jednoduchý visual-diff screenshot test.
- Log fallbacků – když se spustí
renderFallback, pošli event do Sentry. - Nastav alerty – 5% nárůst fallbacků == Slack ping.
- Re-audit při každém major refactoru frontendu.
- Dokumentuj – ať příští kolega ví, proč existuje
slotHeroa neadHeader.
Osobní tipy, které mi zachránily karieru
„Pojmenuj to nejdřív ošklivě, pak zkrášli“
Když vymýšlím ID komponent, napíšu schválně hnusný placeholder: XXX_NEJSEM_AD. Refaktoruju teprve, jakmile vidím layout na dev serveru. Pomáhá to zabránit reflexu napsat první, co mě napadne („adBox“).
„Mockni si ad-blokátor v API“
Potřebuješ-li ladit bez extension, nech reverse-proxy mazat /ads/ requesty. Rychlejší iterace, stejný výsledek.
„Neustále grepuj diff“
Hook v pre-commit, který zakáže commity obsahující \bads?\b v nových souborech, ti ušetří studené poty.
Shrnutí
- Ad-blokátory nečtou úmysly, jen regexy.
- Klíčová slova
ad,ads,banner,promoa slova příbuzná stejného významu v ID, class, URL či souboru ⇒ prvek zmizí. - K pádu layoutu stačí jeden skrytý wrapper.
- Řešení je primitivní: přejmenovat a fallbackovat.
- Automatizuj detekci, jinak na to zapomeneš.
Co máte udělat po přečtení článku?
- Udělat si kontrolu u sebe na webu.
- Přidat si to do dokumentace pro vývojáře,
Jak to vidíte vy? Máte vlastní horror-story s id="adBanner" nebo jste ustáli nasazení bez ztrát? Podělte se v komentářích na sociálních sítích, ať se příště smějeme společně, ne jednotně plačeme.
Zkrácena verze, co jsem dal k nám do dokumentace:
Vyhýbejte se slovům „ad“, „ads“, „advert“, „banner“… v identifikátorech a cestách
Platí pro: id, class, atributy data-*, názvy souborů a adresářů, subdomény, URL parametrů
Pravidlo
- Nikdy nepoužívejte uvedená klíčová slova ani jejich zkrácené/rozšířené varianty (ad, ads, adv, advert, banner, promo, sponsor, analytics, gtm, tracking, track…)
- …pokud stojí na začátku nebo za oddělovačem (
-,_,/,.).
- …pokud stojí na začátku nebo za oddělovačem (
- Povoleno je, když řetězec leží uvnitř jiného slova (header, roadmap).
Proč
- Blokátory reklamy (uBlock Origin, AdBlock Plus, AdGuard…) se řídí seznamy jako EasyList.
- Obsahují globální CSS filtry skrývající prvky podle selektorů typu
##[id^="ad-"],##.ads-bannerstackoverflow.com - A síťové filtry, které ruší požadavky na URL s částmi
/ads/,/banner/či doménamiads.example.comadblockplus.org
- Obsahují globální CSS filtry skrývající prvky podle selektorů typu
- Jakmile blokátor pravidlo trefí, prvek dostane
display:nonenebo se request vůbec nespustí → layout se rozpadne či kód selže „beze stopy“. - Incidenty s ID
ad_holder, tématem „adwise“ apod. ukazují, že jde o častý zdroj skrytých chyb.
Důsledky porušení
- Neviditelné komponenty (tlačítka, modaly, ikony…)
- Chybějící JS/CSS soubory ⇒ JS errors, rozhozený design
- Obtížné ladění – poskytovatelé i QA často nemají ad-block zapnutý
Výjimky
| Kde je to bezpečné | Proč |
|---|---|
| Viditelný text („Reklama“, „Sponsored“) | Filtry umísťují slovo do layoutu jen kosmeticky; nespoléhejte na jeho rozměry |
| Komentáře v HTML/JS, interní proměnné | Blokátory je neparsují |
Příklady
| ❌ Nesprávně | ✅ Správně |
|---|---|
<div id="ad_holder"> | <div id="slotHero"> |
/static/ads/logo.svg | /static/assets/logo.svg |
class="banner_top" | class="heroTop" |
Dodržováním tohoto pravidla předejdete nečekanému skrývání prvků a ušetříte hodiny pátrání po „záhadně mizících“ komponentech.
Protože to zaujalo i lidi z mezinárodních teamů, tak tu mám i verzi v angličtině.
