turbolinksとGoogle Ad Manager (旧 DoubleClick for Publishers)

Google Ad Managerのレポートを見ていたら、Key-Valueと広告ユニットの組み合わせとしてありえないものが出力されていた。 turbolinksのせいで前の画面のKey-Valueが残っているのだろうと思い、調べてみると、

Turbolinks Compatibility with DoubleClick for Publishers

こういうのが出てきた。すごくよさそうだけど、使っているイベントはturbolinks 5に対応していないので、リンクされているGitHubのissueを参照してみる。

Google Adsense strategy not supported by Google Loader · Issue #36 · reed/turbolinks-compatibility · GitHub

CoffeeScriptなのを書き換えて、レスポンシブ対応とKey-Value対応を入れるとこんな感じ。

export default class Gpt {
  constructor() {
    this.slots = {};
    window.googletag = window.googletag || {};
    window.googletag.cmd = window.googletag.cmd || [];

    document.addEventListener('turbolinks:before-visit', this.clearAds.bind(this));
    document.addEventListener('turbolinks:load', this.evaluate.bind(this));
    this.evaluate();
  }

  evaluate() {
    let targeting = document.querySelector('meta[name=gpt-targeting]');
    if (targeting) {
      targeting = JSON.parse(targeting.content);
    }

    window.googletag.cmd.push(() => {
      for (let target in targeting) {
        window.googletag.pubads().setTargeting(target, targeting[target]);
      }
    });

    const ads = document.querySelectorAll('.gpt-ad');
    for (var slot of ads) {
      const cachedSlot = this.slots[slot.id];
      if (cachedSlot) {
        this.refreshSlot(cachedSlot);
      } else {
        this.defineSlot(slot);
      }
    }
  }

  defineSlot(slotEl) {
    const divId = slotEl.id;
    const path = slotEl.dataset.gptPath;
    const dimensions = JSON.parse(slotEl.dataset.gptDimensions);

    window.googletag.cmd.push(() => {
      // 指定できるブラウザサイズは実際の端末サイズよりも小さくなります。
      // https://support.google.com/admanager/answer/3423562?hl=ja
      // 実際には15程度小さくなるので、調整する
      const mapping = window.googletag.sizeMapping()
            .addSize([992 - 15, 690], [[728, 90], [1080, 128]])
            .addSize([0, 0], [[320, 50], [280, 200]])
            .build();
      const slot = window.googletag.defineSlot(path, dimensions, divId).defineSizeMapping(mapping).addService(window.googletag.pubads());
      window.googletag.enableServices();
      window.googletag.display(divId);
      this.slots[divId] = slot;
    });
  }

  refreshSlot(slot) {
    window.googletag.cmd.push(() => {
      window.googletag.pubads().refresh([slot]);
    });
  }

  clearAds() {
    window.googletag.cmd.push(() => {
      window.googletag.pubads().clearTargeting();
      window.googletag.pubads().clear();
    });
  }
}

ページのKey-ValueはmetaタグにJSONで書く。 defineSizeMappingはページごとにカスタマイズできずサイトで1つになってしまっているけど、とりあえずはこれで困っていないのでいいかな。