如何優雅高效地插入百度廣告

什麼是百度廣告

最近跟百度廣告打了會交道,若是您正在或者即將和百度廣告打交道,那太好了,本文必定會讓您不枉此行。百度廣告,即百度聯盟廣告,在 這裏 進行註冊後,通過 一些配置,即可以生成一段 js,將該 js 插入到 HTML 頁面中,便能出現百度的廣告。javascript

恩,百度廣告您必定見過,隨便截張圖:html

隨便給一段生成的百度廣告代碼感覺下:java

<script type="text/javascript">
   var cpro_id = "u2557752";
</script>
<script src="http://cpro.baidustatic.com/cpro/ui/cm.js" type="text/javascript"></script>

以上代碼如何可以展示廣告?最重要的緣由是 cm.js 中將廣告內容用 document.write 方法輸出。git

通常場景

通常場景,也是最簡單的使用,須要廣告出如今哪一個位置,就把該段 js 放在哪一個位置。很是容易理解,由於廣告的生成用的是 document.write,因此執行到該段 js 時,會同步輸出廣告內容。github

使用方式大概這樣:緩存

<html>
<body>
<!-- DOM 元素 -->
<script type="text/javascript">
   var cpro_id = "u2557752";
</script>
<script src="http://cpro.baidustatic.com/cpro/ui/cm.js" type="text/javascript"></script>
<!-- DOM 元素 -->
<script type="text/javascript">
    var cpro_id = "u2557760";
</script>
<script src="http://cpro.baidustatic.com/cpro/ui/cm.js" type="text/javascript"></script>
<!-- DOM 元素 -->
</body>
</html>

異步加載

以上的通常場景下,若是有性能瓶頸,很顯然是由於百度廣告圖片加載的問題,js 的加載並非首要緣由(js還有緩存)。出於好奇,仍是首先對 cm.js 試試可否異步加載。併發

<body>
<script>
  function scriptDomElement(u) {
    var s = document.createElement('script')
      , h = document.getElementsByTagName('head')[0];

    s.src = u;
    s.async = true;

    h && h.insertBefore(s, h.firstChild);
  }

  var cpro_id = "u2557760";
  
  scriptDomElement("http://cpro.baidustatic.com/cpro/ui/cm.js");
</script>
</body>

控制檯有個 warning,可是廣告也出來了:app

以前咱們說到百度廣告是用 document.write 同步輸出到頁面上的,很顯然,並不能異步加載有 document.write 方法的 js 文件(究竟是重寫頁面呢,仍是重寫頁面呢),因此會有該 warning。可是,爲何會有廣告呢?異步

這是由於百度有一套 "備胎" 方案。當 cm.js 內部判斷該 js 是被異步加載的時候,隨即執行這套備胎方案:async

createBackupWrapper: function(t) {
    try {
        var e = document.getElementsByTagName("script")
          , i = e[e.length - 1];
        if (i) {
            var n = i.parentNode;
            if (n) {
                var o = document.createElement("div");
                return o.id = t.containerId,
                n.insertBefore(o, i),
                !0
            }
        }
    } catch (r) {}
    return !1
    },

代碼寫的很清楚,就是把廣告元素插入到最後一個 script 標籤的前面。爲了保證廣告所在的位置便是咱們但願的位置,很顯然最後一個 script 元素必須就是 cm.js

這樣理解的話,咱們彷佛能夠得出這樣一個結論:當廣告位置在頁面最底部時(而且只有一處廣告位),咱們能夠對這段廣告的 js 進行如上的異步加載。可是 js 異步加載了,廣告所須要的圖片還得請求,標籤頁上的小圈圈仍是一直在轉,因此我以爲對 cm.js 文件進行異步加載,並無什麼卵用。

事實上,cm.js 內部就提供了異步加載的方案 -> 廣告位異步加載代碼

<div id="PAGE_AD_1"></div>
HELLO WORLD
<div id="PAGE_AD_2"></div>

<!--廣告位代碼放在頁面最後-->
<script type="text/javascript" src="http://cbjs.baidu.com/js/m.js"></script>

<!--異步加載開始-->
<script type="text/javascript">

BAIDU_CLB_fillSlotAsync('u2557752','PAGE_AD_1');//12345是廣告編號,PAGE_AD_1是您要投放廣告的位置

BAIDU_CLB_fillSlotAsync('u2557760','PAGE_AD_2');

</script>
<!--異步加載結束 -->

這樣不只 cm.js 只需加載一次,並且調用也方便多了。(好吧,以前的異步測試算是無用功)

延遲加載

單純的異步加載對於頁面總體的加載速度彷佛並無什麼提高(廣告圖片略多),是否能夠用 setTimeout 進行延遲的異步加載?ok,咱們對以前的代碼進行一點改造,用一個定時器延遲執行 scriptDomElement 函數。

<body>
<script>
  function scriptDomElement(u) {
    var s = document.createElement('script')
      , h = document.getElementsByTagName('head')[0];

    s.src = u;
    s.async = true;

    h && h.insertBefore(s, h.firstChild);
  }

  var cpro_id = "u2557760";
  
  setTimeout(function() {
      scriptDomElement("http://cpro.baidustatic.com/cpro/ui/cm.js");
  }, 2000);
</script>
</body>

前面說了,異步加載僅適用於 cm.js 做爲最後一個 script 標籤的狀況,也就是廣告在頁面最底部的狀況。what's more,通過這樣的處理,大多數狀況下是能夠看到廣告的,可是小部分狀況廣告沒法展示,究其緣由,樓主以爲是 cm.js 內部對該 js 是不是異步加載沒法精確判斷。

因此樓主以爲,若是能夠接受某些機器下沒法展示百度廣告,這個方法仍是能夠一試的。

恩,其實咱們徹底能夠用 cm.js 提供的 BAIDU_CLB_fillSlotAsync 方法和 setTimeout 進行配合。

重寫 document.write

強勢插入硬廣一枚:樓主 Github 求關注~

很是簡單,寫個簡單的 DEMO(重寫完後記得改回來,DEMO 中沒改回來):

<body>
Hello world!
<div id='ad'>
</div>
<script>
  // 重寫 document.write
  document.write = function( text ){
    document.getElementById('ad').innerHTML = text;
  };

  function scriptDomElement(u) {
    var s = document.createElement('script')
      , h = document.getElementsByTagName('head')[0];

    s.src = u;
    s.async = true;

    h && h.insertBefore(s, h.firstChild);
  }

  var cpro_id = "u2557760";
  scriptDomElement("http://cpro.baidustatic.com/cpro/ui/cm.js");
</script>
</body>

如何能作到延遲加載?咱們能夠採用 BigRender 的思路,將廣告代碼放在 textarea 標籤中,當 textarea 出如今視野中時,取出廣告代碼執行。參考雨夜帶刀的代碼:

<div>
<textarea style="display:none">
<script type="text/javascript" src="http://gg.5173.com/adpolestar/5173/
;ap=2EBE5681_1BA3_4663_FA3F_E73D2B83FBDC;ct=js;pu=5173;/?"></script>
</textarea>
</div>
延遲加載script並重寫document.write,下面是代碼實現:

var loadScript = function( elem ){
    var url = elem.value.match( /src="([\s\S]*?)"/i )[1],
        parent = elem.parentNode,
        // 緩存原生的document.write
        docWrite = document.write,    
        // 建立一個新script來加載
        script = document.createElement( 'script' ), 
        head = document.head || 
            document.getElementsByTagName( 'head' )[0] || 
            document.documentElement;
    
    // 重寫document.write
    document.write = function( text ){
        parent.innerHTML = text;
    };

    script.type = 'text/javascript';
    script.src = url;
    
    script.onerror = 
    script.onload = 
    script.onreadystatechange = function( e ){
        e = e || window.event;
        if( !script.readyState || 
        /loaded|complete/.test(script.readyState) ||
        e === 'error'
        ){

            // 恢復原生的document.write
            document.write = docWrite;
            head.removeChild( script );
            
            // 卸載事件和斷開DOM的引用
            // 儘可能避免內存泄漏
            head =             
            parent = 
            elem =
            script = 
            script.onerror = 
            script.onload = 
            script.onreadystatechange = null;

        }
    }
    
    // 加載script
    head.insertBefore( script, head.firstChild );
};

若是有多個廣告片斷,由於 document.write 是全局方法,因此不得不維護個腳本隊列,一個一個執行,又退化成了同步執行腳本。若是異步併發執行的話,極可能廣告的位置會出現對調現象。固然,有些百度廣告並不會十分在意順序,好比下面要說的新聞信息流。

其實我以爲若是要延遲加載某些特定位置的廣告區域,能夠用 BAIDU_CLB_fillSlotAsync 方法,將該方法所在的代碼塞入 textarea 中。 有一點須要注意的是,BAIDU_CLB_fillSlotAsync 必須指定廣告位置的 DOM id。

插入到新聞信息流

將百度廣告插入到新聞信息流,這是很廣泛的作法。

好比網易:

那麼如何將廣告插入到新聞信息流當中去呢?咱們仍是能夠用重寫 document.write 的方法。

舉個簡單的例子:

<body>
<ul>
  <li class="news"></li>
  <li class="news"></li>
  <li class="news"></li>
  <li class="bdad"></li>
  <li class="news"></li>
</ul>
<script>
  // 重寫 document.write
  document.write = function( text ){
    document.getElementsByClassName('bdad')[0].innerHTML = text;
  };
  
  function scriptDomElement(u) {
    var s = document.createElement('script')
      , h = document.getElementsByTagName('head')[0];

    s.src = u;
    s.async = true;

    h && h.insertBefore(s, h.firstChild);
  }

  var cpro_id = "u2557760";
  scriptDomElement("http://cpro.baidustatic.com/cpro/ui/cm.js");
</script>
</body>

重寫一個系統的方法畢竟不是什麼好事,網易頭條新聞 採用的都是另外一種方法,套一個 iframe,很是巧妙。

index.htm 文件:

<body>
<ul>
  <li class="news"></li>
  <li class="news"></li>
  <li class="news"></li>
  <li class="news"><iframe src="ad.htm"></iframe></li>
  <li class="news"></li>
</ul>
</body>

ad.htm 文件:

<script type="text/javascript">
  var cpro_id = "u2557760";
</script>
<script src="http://cpro.baidustatic.com/cpro/ui/cm.js"></script>

固然,一些樣式方面的細節還須要本身去把握,這裏只提供一個思路。

總結

對於百度廣告在不一樣環境中的投放,有不一樣的處理方式。主要有三種:

  • 利用 cm.js 中的 BAIDU_CLB_fillSlotAsync 方法(該方法須要廣告位置的 DOM id)

  • 重寫 document.write

  • 新建 iframe,在該 iframe 中同步輸出廣告代碼

仁者見仁智者見智吧。

Read more:

相關文章
相關標籤/搜索