メインコンテンツへスキップ
無料20 minutes中級

カスタムフレームワーク統合

あらゆるJavaScriptフレームワークとZenovayを統合する方法 — バニラJS、Svelte、Angular、カスタム実装についてご説明します。統合設定とベストプラクティスを探索します。

integrationjavascriptcustomvanilla-jssvelte
最終更新日:

柔軟なトラッキングAPIを使用して、あらゆるJavaScriptフレームワークまたはバニラJavaScriptアプリケーションにZenovayアナリティクスを統合します。

ユニバーサルスクリプトのインストール

基本的なスクリプトタグ

あらゆるフレームワークで動作します:

<script
  defer
  data-tracking-code="YOUR_TRACKING_CODE"
  src="https://api.zenovay.com/z.js"
></script>

スクリプト属性

属性説明
data-tracking-code必須。ウェブサイトのトラッキングコード
defer推奨。ページレンダリングをブロックせずにスクリプトを読み込む

標準設定

<script
  defer
  data-tracking-code="YOUR_TRACKING_CODE"
  src="https://api.zenovay.com/z.js"
></script>

バニラJavaScript

基本的な使い方

// イベントをトラック
window.zenovay('track', 'button_click', {
  button_id: 'signup',
  location: 'hero'
});

// ページビューをトラック(自動トラック無効の場合)
window.zenovay('page');

// ユーザーを識別
window.zenovay('identify', 'user-123', {
  email: '[email protected]',
  plan: 'pro'
});

// ゴールをトラック
window.zenovay('goal', 'purchase', {
  value: 99.99
});

// 売上をトラック
window.zenovay('revenue', 149.99, 'JPY', {
  order_id: 'ORD-001'
});

スクリプト読み込みを待つ

function waitForZenovay(callback, maxWait = 5000) {
  const startTime = Date.now();

  function check() {
    if (window.zenovay) {
      callback(window.zenovay);
    } else if (Date.now() - startTime < maxWait) {
      requestAnimationFrame(check);
    }
  }

  check();
}

// 使用例
waitForZenovay((zenovay) => {
  zenovay('track', 'app_ready');
});

イベントキューパターン

// キュー関数(スクリプト読み込み前でも動作)
window.zenovay = window.zenovay || function() {
  (window.zenovay.q = window.zenovay.q || []).push(arguments);
};

// スクリプト読み込み前でも即座に呼び出し可能
window.zenovay('track', 'early_event', { source: 'inline' });
window.zenovay('page');

Svelte統合

App.svelteのセットアップ

<script>
  import { onMount } from 'svelte';
  import { page } from '$app/stores';

  onMount(() => {
    // 最初のページビューをトラック
    if (window.zenovay) {
      window.zenovay('page');
    }
  });

  // ルート変更をトラック
  $: if ($page && window.zenovay) {
    window.zenovay('page');
  }
</script>

Svelteストア

// stores/analytics.js
import { writable } from 'svelte/store';

function createAnalyticsStore() {
  const { subscribe } = writable(null);

  return {
    subscribe,
    track: (event, data) => {
      if (typeof window !== 'undefined' && window.zenovay) {
        window.zenovay('track', event, data);
      }
    },
    identify: (userId, properties) => {
      if (typeof window !== 'undefined' && window.zenovay) {
        window.zenovay('identify', userId, properties);
      }
    },
    trackGoal: (name, value) => {
      if (typeof window !== 'undefined' && window.zenovay) {
        window.zenovay('goal', name, { value });
      }
    }
  };
}

export const analytics = createAnalyticsStore();

Svelteコンポーネント使用例

<script>
  import { analytics } from '../stores/analytics';

  function handleClick() {
    analytics.track('button_click', { button: 'cta' });
  }
</script>

<button on:click={handleClick}>
  クリック
</button>

SvelteKit統合

// hooks.client.js
import { page } from '$app/stores';

page.subscribe(($page) => {
  if (typeof window !== 'undefined' && window.zenovay && $page) {
    window.zenovay('page');
  }
});

Angular統合

サービスの作成

// analytics.service.ts
import { Injectable } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';

declare global {
  interface Window {
    zenovay: {
      (command: 'track', event: string, data?: Record<string, any>): void;
      (command: 'page'): void;
      (command: 'goal', name: string, data?: Record<string, any>): void;
      (command: 'revenue', amount: number, currency: string, meta?: Record<string, any>): void;
      (command: 'identify', userId: string, properties?: Record<string, any>): void;
    };
  }
}

@Injectable({
  providedIn: 'root'
})
export class AnalyticsService {
  constructor(private router: Router) {
    this.setupRouteTracking();
  }

  private setupRouteTracking() {
    this.router.events.pipe(
      filter(event => event instanceof NavigationEnd)
    ).subscribe((event: NavigationEnd) => {
      this.trackPageview(event.urlAfterRedirects);
    });
  }

  track(event: string, data?: Record<string, any>) {
    if (window.zenovay) {
      (window as any).zenovay('track', event, data);
    }
  }

  trackPageview() {
    if (window.zenovay) {
      (window as any).zenovay('page');
    }
  }

  trackGoal(name: string, value?: number) {
    if (window.zenovay) {
      (window as any).zenovay('goal', name, { value });
    }
  }

  identify(userId: string, properties?: Record<string, any>) {
    if (window.zenovay) {
      (window as any).zenovay('identify', userId, properties);
    }
  }
}

コンポーネント使用例

// signup.component.ts
import { Component } from '@angular/core';
import { AnalyticsService } from './analytics.service';

@Component({
  selector: 'app-signup',
  template: `<button (click)="handleSignup()">登録</button>`
})
export class SignupComponent {
  constructor(private analytics: AnalyticsService) {}

  handleSignup() {
    this.analytics.track('signup_click', {
      source: 'header'
    });
  }
}

モジュール設定

// app.module.ts
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { AnalyticsService } from './analytics.service';

export function initAnalytics(analytics: AnalyticsService) {
  return () => {
    // トラッキング初期化
  };
}

@NgModule({
  providers: [
    AnalyticsService,
    {
      provide: APP_INITIALIZER,
      useFactory: initAnalytics,
      deps: [AnalyticsService],
      multi: true
    }
  ]
})
export class AppModule {}

Ember.js統合

サービスの作成

// app/services/analytics.js
import Service from '@ember/service';
import { inject as service } from '@ember/service';

export default class AnalyticsService extends Service {
  @service router;

  constructor() {
    super(...arguments);
    this.setupRouteTracking();
  }

  setupRouteTracking() {
    this.router.on('routeDidChange', () => {
      this.trackPageview(this.router.currentURL);
    });
  }

  track(event, data) {
    if (window.zenovay) {
      window.zenovay('track', event, data);
    }
  }

  trackPageview() {
    if (window.zenovay) {
      window.zenovay('page');
    }
  }

  identify(userId, properties) {
    if (window.zenovay) {
      window.zenovay('identify', userId, properties);
    }
  }
}

Alpine.js統合

<div x-data="{ analytics: $store.analytics }">
  <button @click="analytics.track('click', { button: 'cta' })">
    クリック
  </button>
</div>

<script>
document.addEventListener('alpine:init', () => {
  Alpine.store('analytics', {
    track(event, data) {
      if (window.zenovay) {
        window.zenovay('track', event, data);
      }
    },
    identify(userId, properties) {
      if (window.zenovay) {
        window.zenovay('identify', userId, properties);
      }
    }
  });
});
</script>

jQuery統合

// トラッキングヘルパー初期化
$.zenovay = {
  track: function(event, data) {
    if (window.zenovay) {
      window.zenovay('track', event, data);
    }
  },
  identify: function(userId, properties) {
    if (window.zenovay) {
      window.zenovay('identify', userId, properties);
    }
  }
};

// 使用例
$(document).ready(function() {
  $('#signup-btn').click(function() {
    $.zenovay.track('signup_click', {
      button: $(this).data('name')
    });
  });
});

Web Components

// analytics-tracker.js
class AnalyticsTracker extends HTMLElement {
  connectedCallback() {
    this.addEventListener('click', this.handleClick.bind(this));
  }

  handleClick() {
    const event = this.getAttribute('data-event');
    const dataAttrs = {};

    for (const attr of this.attributes) {
      if (attr.name.startsWith('data-prop-')) {
        const key = attr.name.replace('data-prop-', '');
        dataAttrs[key] = attr.value;
      }
    }

    if (window.zenovay && event) {
      window.zenovay('track', event, dataAttrs);
    }
  }
}

customElements.define('analytics-tracker', AnalyticsTracker);

使用例:

<analytics-tracker
  data-event="cta_click"
  data-prop-button="hero"
  data-prop-page="home"
>
  <button>スタート</button>
</analytics-tracker>

SPA(Single Page Application)パターン

History APIトラッキング

// 履歴変更をトラック
(function() {
  const pushState = history.pushState;
  const replaceState = history.replaceState;

  history.pushState = function() {
    pushState.apply(history, arguments);
    trackPageChange();
  };

  history.replaceState = function() {
    replaceState.apply(history, arguments);
    trackPageChange();
  };

  window.addEventListener('popstate', trackPageChange);

  function trackPageChange() {
    if (window.zenovay) {
      // DOMの更新を確認するため少し待つ
      setTimeout(() => {
        window.zenovay('page');
      }, 100);
    }
  }
})();

ハッシュベースルーティング

window.addEventListener('hashchange', function() {
  if (window.zenovay) {
    window.zenovay('page');
  }
});

TypeScript定義

// types/zenovay.d.ts
interface ZenovayEventData {
  [key: string]: string | number | boolean | undefined;
}

interface ZenovayRevenueData {
  order_id: string;
  value: number;
  currency?: string;
  items?: Array<{
    id: string;
    name: string;
    price: number;
    quantity: number;
  }>;
}

interface Zenovay {
  (command: 'track', event: string, data?: ZenovayEventData): void;
  (command: 'page'): void;
  (command: 'goal', name: string, data?: { value?: number }): void;
  (command: 'revenue', amount: number, currency: string, meta?: Record<string, unknown>): void;
  (command: 'identify', userId: string, properties?: ZenovayEventData): void;
}

declare global {
  interface Window {
    zenovay?: Zenovay;
  }
}

export {};

ラッパーライブラリパターン

再利用可能なラッパーを作成:

// lib/analytics.ts
class Analytics {
  private queue: Array<() => void> = [];
  private ready = false;

  constructor() {
    this.waitForScript();
  }

  private waitForScript() {
    const check = () => {
      if (window.zenovay) {
        this.ready = true;
        this.processQueue();
      } else {
        requestAnimationFrame(check);
      }
    };
    check();
  }

  private processQueue() {
    this.queue.forEach(fn => fn());
    this.queue = [];
  }

  private execute(fn: () => void) {
    if (this.ready) {
      fn();
    } else {
      this.queue.push(fn);
    }
  }

  track(event: string, data?: Record<string, any>) {
    this.execute(() => (window.zenovay as any)('track', event, data));
  }

  pageview() {
    this.execute(() => (window.zenovay as any)('page'));
  }

  identify(userId: string, properties?: Record<string, any>) {
    this.execute(() => (window.zenovay as any)('identify', userId, properties));
  }

  goal(name: string, value?: number) {
    this.execute(() => (window.zenovay as any)('goal', name, { value }));
  }

  revenue(amount: number, currency: string, meta?: Record<string, any>) {
    this.execute(() => (window.zenovay as any)('revenue', amount, currency, meta));
  }
}

export const analytics = new Analytics();

統合のテスト

デバッグモード

ブラウザコンソールでトラッカーアクティビティを見るためにデバッグログを有効化します。有効化する方法は3つあります:

<!-- 1. スクリプトタグにdata-debug属性を追加 -->
<script
  defer
  data-tracking-code="YOUR_TRACKING_CODE"
  data-debug="true"
  src="https://api.zenovay.com/z.js"
></script>
2. ページURLに ?zenovay_debug=true を追加(本番環境での確認に便利です)
3. localhost での実行時はデバッグログが自動的に有効になります

デバッグを有効化すると、トラッカーはコンソールに送信された各イベントをログします。

検証ステップ

  1. ブラウザのDevToolsを開く
  2. ネットワークタブに移動
  3. 「zenovay」または「analytics」でフィルタリング
  4. アクションを実行
  5. リクエストが送信されたことを確認

テストイベント

// クイックテスト
if (window.zenovay) {
  window.zenovay('track', 'test_event', {
    test: true,
    timestamp: Date.now()
  });
  console.log('テストイベントを送信しました');
} else {
  console.error('Zenovayが読み込まれていません');
}

トラブルシューティング

スクリプトが読み込まれない場合

確認事項:

  • スクリプトURLが正しいか
  • 広告ブロッカーによる干渉がないか
  • ウェブサイトIDが有効か
  • ネットワークリクエストが確認できるか

イベントがトラックされない場合

確認事項:

  • window.zenovayが存在するか
  • データ形式が正しいか
  • JavaScriptエラーがないか
  • ドメインが許可されているか

SPAナビゲーションの問題

確認事項:

  • ルート変更が検出されているか
  • ナビゲーション時にtrackPageviewが呼び出されているか
  • 重複トラッキングがないか

次のステップ

この記事は役に立ちましたか?