埋設 DFP - DoubleClick for Publishers 廣告

意象說明

廣告媒合

埋設 DFP - DoubleClick for Publishers 廣告

初始設定

比較跳脫一般經驗法則的是,在載入 script 之後,服務仍然不是可用狀態,因此,此時不可呼叫相關服務 API。

譬如,以下是錯誤做法:

1
2
3
4
$.getScript('//www.googletagservices.com/tag/js/gpt.js').then(function () {
window.googletag.pubads().enableSingleRequest();
window.googletag.enableServices();
});

不論是透過直接在 html 中埋入 script tag 的方式載入,或是以上面的方式動態載入 DFP script,都不可以直接呼叫 DFP 的 API。

正確的做法,須將命令以函數的形式,推到 window.googletag.cmd 陣列中。注意,此時 googletaggoogletag.cmd 可以只是普通物件和陣列。待 DFP 服務啟動後,會在 googletag 中添加功能,然後逐一呼叫被推入 googletag.cmd 陣列的函數。也就是說,在該函數中才能保證 DFP 的服務已備妥,並且才能呼叫 API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var adUnitPath = 'pub-id/slot-id';
var adSize = [300, 25];
var adElementId = 'id-to-html-element';

$.getScript('//www.googletagservices.com/tag/js/gpt.js').then(function () {
var googletag = window.googletag || (window.googletag = []);
googletag.cmd = (googletag.cmd || []);
googletag.cmd.push(function () {
initDfp();
googletag.defineSlot(adUnitPath, adSize, adElementId)
.addService(googletag.pubads());
googletag.display(adElementId);
});
});

function initDfp() {
googletag.pubads().enableSingleRequest();
googletag.enableServices();
}

上面的 adElementId,雖然在 DFP 上使用 generate tag 時,會產生一個 div-gpt-ad-1234567890123-0 格式的字串,要我們填在 html 元素作為 id 使用,但其實只要保握 html 的基本原則:id 不要重複,在這裡使用任意 id 皆可,並不影響廣告呈現。不過 DFP 仍然建議,id 最好能反映廣告版位,以方便投廣告的客戶確認。

多個廣告版位

上面的做法,如果直接套用在多個廣告上,雖然不會有什麼問題,但是重複執行 DFP 的設定,還是讓人渾身不舒服。

可以將 $.getScript()initDfp() 的處理包裝起來,讓它們永遠只會執行一次:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function once(fn) {
var promise;
return function () {
var args;
if (!promise) {
args = Array.prototype.slice.call(arguments);
promise = Promise.resolve(fn.apply(null, args));
}
return promise;
};
}

var loadDfp = once(function () {
return $.getScript('//www.googletagservices.com/tag/js/gpt.js').then(function () {
window.googletag = window.googletag || {};
window.googletag.cmd = window.googletag.cmd || [];
return window.googletag;
});
});

var initDfp = once(function (googletag) {
googletag.pubads().enableSingleRequest();
googletag.enableServices();
});

然後,每個要新增版位的地方,都這樣呼叫:

1
2
3
4
5
6
7
8
loadDfp().then(function (googletag) {
googletag.cmd.push(function () {
initDfp(googletag);
googletag.defineSlot(adUnitPath, adSize, adElementId)
.addService(googletag.pubads());
googletag.display(adElementId);
});
});

注意到,initDfp() 的呼叫,不能併到 loadDfp() 中執行,只能在 googletag.cmd.push() 的函數中執行,否則就又犯了一開始的錯誤。

上面的寫法,還是有點囉唆,於是,我們還可以進一步包裝成這樣:

1
2
3
4
5
6
7
8
function dfp(fn) {
loadDfp().then(function (googletag) {
initDfp(googletag);
googletag.cmd.push(function () {
fn(googletag);
});
});
}
1
2
3
4
5
dfp(function(googletag) {
googletag.defineSlot(adUnitPath, adSize, adElementId)
.addService(googletag.pubads());
googletag.display(adElementId);
});

動態新增廣告

上面的做法,適用於內容不會變動的頁面。如果是無限捲動列表形式的頁面,可能需要每隔幾個項目就顯示一則廣告。官方建議的做法,是透過呼叫 disableInitialLoad() 函數禁止初始廣告載入,然後於版面備妥時,再呼叫 refresh() 更新廣告內容。

1
2
3
4
5
function initDfp(googletag) {
googletag.pubads().enableSingleRequest();
googletag.pubads().disableInitialLoad();
googletag.enableServices();
}

注意上面 googletag.pubads().disableInitialLoad() 的呼叫。此呼叫將改變 display() 函數的行為,使 display() 函數只註冊廣告版位,而不進行顯示。

在動態插入廣告之後,再呼叫 refresh() 函數,更新廣告:

1
2
3
4
5
6
dfp(function(googletag) {
var slot = googletag.defineSlot(adUnitPath, adSize, adElementId)
.addService(googletag.pubads());
googletag.display(adElementId);
googletag.pubads().refresh([slot]);
});

注意上面如何儲存 slot,然後用以呼叫 refresh() 函數。

自動縮合沒有廣告的版面

也許我們的版位不是非常熱門,經常開天窗(希望不是)。一旦廣告版位沒有人投遞廣告,該廣告版位就會顯示成空白。如果這是所有的版面的常態的話,可以考慮直接在初始化的時候,設定所有的版面在沒有廣告的時候,自動縮合。

1
2
3
4
5
6
function initDfp(googletag) {
googletag.pubads().enableSingleRequest();
googletag.pubads().disableInitialLoad();
googletag.pubads().collapseEmptyDivs();
googletag.enableServices();
}

或者,也可以根據需要,依據版面別進行個別設定:

1
2
3
4
5
6
7
8
9
10
var adCollapse = false;

dfp(function(googletag) {
const slot = googletag.defineSlot(adUnitPath, adSize, adElementId)
.addService(googletag.pubads())
.setCollapseEmptyDiv(adCollapse);
googletag.display(adElementId);
googletag.pubads().refresh([slot]);

});

React Component 範例

最後,測試的過程中寫了一簡單的 React 範例程式,提供給大家參考。

參考資料

Modular CSS

意象說明

模組化 < 封裝 < 與世隔絕 < 受困於小船
絕佳搭配 < 天作之合 < 俊男美女

前言

這篇文章記錄個人嘗試 CSS-modules 過程中所遇到的問題,並且順便將目前業界各種對 modular css 的嘗試/進展,做一個相關資訊的摘錄。

還沒有找到適合自己偏好作法的讀者,可以將此文視為簡單的技術選型指南,從中挑選適合自己的作法。

Read More

AngularJS 1.5 最佳實務


前言

本文整理截至目前 AngularJS 1.5 為止, 個人以為的最佳實務做法。

為什麼現在?基於以下三點理由:

  • 雖然 AngularJS 2.0 發佈在即,但是既有的 AngularJS 1.x 程式碼仍需要維護。
  • 由於 AngularJS 1.x 受到廣泛的歡迎,前後做了不少的改進,因此網路上到處可見其實已經過時的作法。
  • 基於向後相容的原則,有些功能雖然被保留下來,但未必是最佳作法。

『你可以這麼做,不代表你必須這麼做』,基於『JavaScript: The Good Parts』的哲理,本文適合具有 AngularJS 1.x 實務開發經驗,然而卻隨著 AngularJS 的發展,逐漸對於網路上充斥各種主觀、矛盾的說法、用法感到困惑的開發者。

雖然遵循本文提出的原則,可以以趨近於元件化的風格開發 AngularJS 1.5 應用程式,但本文不涉及 AngularJS 1.x 移轉到 AngularJS 2.0 議題。

Read More

反駁:NPM & left-pad: Have We Forgotten How To Program?

意象說明

JavaScript 社群 < 百家爭鳴 < 車廠+車廂
黎明?黃昏?

前一陣子left-pad 的作者,因為不滿 npmjs.com 屈從財團的壓力,未經作者同意就擅自移轉所有權,因此憤而將全部模組從 npm 下架,而導致眾多知名模組受到波及。此事件引起了廣泛的反思。

然而,漸漸出現一種聲音,開始呼籲大家不要使用 open source、要盡量自己寫程式碼,不要依賴別人的模組,就像這篇文章:『NPM & left-pad: Have We Forgotten How To Program?』(中譯)。更有甚者,連『懂得怎麼寫程式之前,別去玩 node.js 好嗎?』這樣的話都出來了。

Read More

AMP - Accelerated Mobile Pages 技術預覽

意象說明

行動網頁

Google AMP 是用來加速顯示靜態網頁內容的技術。

主要由三個部份組成:AMP HTML (content), AMP JS (runtime), Google AMP Cache。

其中 AMP HTML 及 AMP JS 牽涉到內容的技術部份,可以一併來檢視。可以這麼說,為了達成瞬間顯示的效能,這兩項技術禁用一些容易阻塞瀏覽器的元素:

  1. Style 必須內聯定義,以加速版面的計算。
  2. 除了 AMP 元件必要的 JavaScript 之外,禁用其他 JavaScript。以避免諸如 document.write(), 或動態改變 class, style 等造成 reflow 的狀況。
  3. 所有的外部資源均需要使用訂製的 AMP 元件來引用,以便由 AMP 管理資源的載入順序。主要是為了要讓網頁最前面可視區域能最優先顯示。
  4. 所有的外部資源均需要設定尺寸大小,以便在資源實際載入前,先行計算 layout。讓畫面不會在資源載入後又重新計算。

Read More

The Problems of JSHint (JSHint 的問題)

意象說明

問題 < 一個頭兩個大 < 小孩讀聖經

最早的 JavaScript linterJSLint,但是由於作者 Douglas Crockford 設定了太多個人主觀認定必須要遵守的強制規則,而且很少有妥協的餘地,所以後來強調可以配置規則的 JSHint 漸漸崛起,我自己從一開始就是使用 JSHint,並且也用了蠻長一段時間。然而...

Read More

Logdown 學習筆記

意象說明

Logdown 諧音 Look down

前言

長期關注本部落格的朋友,可能有發現到,除了原本在 Blogger 的 格物致知 之外,還有一個同步更新的網站 Fragments of Time 位於 Logdown

這是因為我在之前的文章『Medium 與 Facebook Notes 簡短試用』中提到的,厭倦了使用 HTML 撰寫部落格,所以在 Logdown 發佈之後,曾經有一段時間,我是在 Logdown 先用 Markdown 語法撰寫文章,然後再將 HTML 複製到 Blogger 中。

現在決定改用 GitHub Pages / hexo,上面的步驟就省了,現在我可以直接用 Markdown 寫文章,然後下個指令,就自動轉成 HTML 並且發佈。當初除了必須手動複製這個原因之外,其實在使用 Logdown 時,還遇到一些不方便的地方,並且做了一些筆記。我不確定現在這些問題是否依舊沒有改善,整理出來,給不幸遇到的朋友參考看看。

Read More