在前文《Angular 项目如何接入 Google Adsense,并实现动态读取配置和反 Adblock Plus 拦截?》中,提到了如何进行 Adblock Plus、AdGuard 等广告拦截插件的检测,本文对此作一详细分析。

目前网络上常见的检测方式有几种:

  1. 设置全局变量,并将其文件名命名为类似ads.js等带关键词的名字。
  2. 设置定时器,在定时器中检测某个元素是否能正常显示。

一个典型的实现,如阮一峰的 Blog:

typescriptCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
function checker() { if (/mobile|android/i.test(navigator.userAgent)) return; var img = document.querySelector('a > img[src*="wangbase.com/blogimg/asset/"]'); var isAdblocker = (typeof adblock === 'undefined'); if ( (img && window.getComputedStyle(img).display === 'none') || (img && window.getComputedStyle(img.parentElement).display === 'none') ){ var sponsor = document.querySelector('#main-content'); var prompt = document.createElement('div'); prompt.style = 'border: 1px solid #c6c6c6;border-radius: 4px;background-color: #f5f2f0;padding: 15px; font-size: 14px;'; prompt.innerHTML = '<p>您使用了广告拦截器,导致本站内容无法显示。</p><p>请将 www.ruanyifeng.com 加入白名单,解除广告屏蔽后,刷新页面。谢谢。</p>'; sponsor.parentNode.replaceChild(prompt, sponsor); } } setTimeout(checker, 1000);
function checker() { if (/mobile|android/i.test(navigator.userAgent)) return; var img = document.querySelector('a > img[src*="wangbase.com/blogimg/asset/"]'); var isAdblocker = (typeof adblock === 'undefined'); if ( (img && window.getComputedStyle(img).display === 'none') || (img && window.getComputedStyle(img.parentElement).display === 'none') ){ var sponsor = document.querySelector('#main-content'); var prompt = document.createElement('div'); prompt.style = 'border: 1px solid #c6c6c6;border-radius: 4px;background-color: #f5f2f0;padding: 15px; font-size: 14px;'; prompt.innerHTML = '

您使用了广告拦截器,导致本站内容无法显示。

请将 www.ruanyifeng.com 加入白名单,解除广告屏蔽后,刷新页面。谢谢。

'; sponsor.parentNode.replaceChild(prompt, sponsor); } } setTimeout(checker, 1000);

这两种方式的原理其实也很简单:

  1. 利用广告拦截插件的关键词检测特性。
  2. 利用是否拦截的实际结果,如上例中,自定义了需拦截的 URL,从而在检测到未显示元素(图片)时,判断开启了广告拦截插件。

然而,这两种方式要么无效,要么过于复杂,并不想采用。实际上,通过 Google AdSense、百度联盟的广告调用代码可以很容易地找到解决方案。

Google AdSense 的调用代码:

typescriptCopy code
  • 1
(adsbygoogle = window.adsbygoogle || []).push({});
(adsbygoogle = window.adsbygoogle || []).push({});

百度联盟的调用代码:

typescriptCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
(window.slotbydup = window.slotbydup || []).push({ id: "u6001133", container: "_iweq3ve2cyf", async: true });
(window.slotbydup = window.slotbydup || []).push({ id: "u6001133", container: "_iweq3ve2cyf", async: true });

有没有发现一些端倪?

可以看到,二者都将广告对象模拟成了数组(实际上是对象),并在 js 被拦截之后成为了真正的数组。这么做的原因也很简单,防止报错。但,我们也可以很容易地利用这一特点来检测是否开启了广告拦截插件。

typescriptCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
if (Array.isArray((window as any).adsbygoogle)) { this.console.warn('Ads is blocked.'); this.hideAdsEle(); } else { this.commonService.updateAdsFlag(false); }
if (Array.isArray((window as any).adsbygoogle)) { this.console.warn('Ads is blocked.'); this.hideAdsEle(); } else { this.commonService.updateAdsFlag(false); }

上述代码中,只需要一行 Array.isArray 调用即可完成广告是否被拦截的判断。

实际上,这种方式适用于检测任何类型的广告拦截插件,包括因网络等各种问题导致的 js 加载失败。

完美![]~( ̄▽ ̄)~*

实际效果可以直接在本页面上体验(切换广告拦截插件启用状态)。

完整代码请访问:https://github.com/ifuyun/ifuyun.com