JavaScriptウィジェットの作り方【埋め込みスクリプト解説】
埋め込みJavaScriptウィジェットとは
外部サイトに埋め込めるJavaScriptウィジェットとは、<script> タグを1行追加するだけで他のWebサイトにコンテンツを表示できる仕組みです。Googleアナリティクス・チャットボット・テスティモニアルウィジェットなど、多くのSaaS製品がこの形式でサードパーティ統合を提供しています。
テスティモニアルウィジェットを例にとると、顧客が自社サイトに次の2行を貼るだけでレビューが表示される仕組みです。
<div data-koe-widget="widget-123"></div>
<script src="https://koecollect.com/embed.js" async></script>
このページでは、このような埋め込みウィジェットの実装パターンを解説します。
基本的なウィジェットスクリプトの構成
シンプルな実装パターン
// embed.js
(function() {
'use strict';
// 設定の取得
const containers = document.querySelectorAll('[data-koe-widget]');
containers.forEach(function(container) {
const widgetId = container.getAttribute('data-koe-widget');
if (!widgetId) return;
// APIからデータを取得
fetch('https://api.koecollect.com/widget/' + widgetId, {
headers: {
'Accept': 'application/json'
}
})
.then(function(response) {
if (!response.ok) throw new Error('API error');
return response.json();
})
.then(function(data) {
renderWidget(container, data);
})
.catch(function(error) {
console.warn('Koe widget failed to load:', error);
// エラー時はコンテナを非表示にする(ページレイアウトへの影響を最小化)
container.style.display = 'none';
});
});
function renderWidget(container, data) {
// HTMLを生成してDOMに挿入
const html = data.testimonials.map(function(t) {
return '<div class="koe-card">' +
'<p class="koe-text">' + escapeHtml(t.text) + '</p>' +
'<p class="koe-author">' + escapeHtml(t.name) + '</p>' +
'</div>';
}).join('');
container.innerHTML = '<div class="koe-widget">' + html + '</div>';
injectStyles();
}
function escapeHtml(str) {
const div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
function injectStyles() {
if (document.getElementById('koe-styles')) return;
const style = document.createElement('style');
style.id = 'koe-styles';
style.textContent = '.koe-widget { font-family: sans-serif; }' +
'.koe-card { border: 1px solid #eee; padding: 16px; margin: 8px 0; border-radius: 8px; }' +
'.koe-author { font-weight: bold; margin-top: 8px; }';
document.head.appendChild(style);
}
})();
重要なポイント:
- 即時実行関数(IIFE)でグローバルスコープを汚染しない
escapeHtmlでXSSを防ぐ- エラーは静かに失敗させる(ページ全体を壊さない)
Shadow DOMを使ったスタイル分離
埋め込み先のCSSと干渉しないように、Shadow DOMを使ってウィジェットをカプセル化する方法です。
function renderWithShadowDOM(container, data) {
// Shadow rootを作成
const shadow = container.attachShadow({ mode: 'closed' });
// スタイルをShadow DOM内に閉じ込める
const style = document.createElement('style');
style.textContent = `
.koe-card {
border: 1px solid #eee;
padding: 16px;
border-radius: 8px;
font-family: -apple-system, sans-serif;
}
`;
shadow.appendChild(style);
// コンテンツを追加
const wrapper = document.createElement('div');
data.testimonials.forEach(function(t) {
const card = document.createElement('div');
card.className = 'koe-card';
card.textContent = t.text;
wrapper.appendChild(card);
});
shadow.appendChild(wrapper);
}
Shadow DOMを使うとウィジェットのCSSが完全に外部と分離されるため、埋め込み先のCSSの影響を受けません。ただし、ウィジェット側からも埋め込み先のCSSを参照できなくなります。
CORS(クロスオリジンリソース共有)の設定
埋め込みウィジェットがAPIにアクセスする場合、APIサーバー側でCORS設定が必要です。
Next.js App RouterでのCORSレスポンスヘッダーの設定例:
// app/api/widget/[widgetId]/route.ts
export async function GET(
request: Request,
{ params }: { params: { widgetId: string } }
) {
const data = await fetchWidgetData(params.widgetId);
return Response.json(data, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET',
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300',
},
});
}
Access-Control-Allow-Origin: * は全てのオリジンからのアクセスを許可します。セキュリティを強化する場合は、* の代わりに許可するドメインのリストを指定します。
パフォーマンス最適化
キャッシュ戦略
ウィジェットのデータは頻繁に変わらないため、適切なキャッシュを設定することでAPIサーバーの負荷を大幅に削減できます。
- ブラウザキャッシュ:
Cache-Control: public, max-age=60(60秒間ブラウザキャッシュ) - CDNキャッシュ:
s-maxage=300, stale-while-revalidate=600(CDNで5分キャッシュ)
スクリプトの非同期読み込み
埋め込みスクリプトには必ず async または defer を付けてください。これがないと、スクリプトの読み込みが完了するまでページのレンダリングがブロックされます。
<!-- NG: ページレンダリングをブロックする -->
<script src="https://koecollect.com/embed.js"></script>
<!-- OK: 非同期で読み込む -->
<script src="https://koecollect.com/embed.js" async></script>
DOM操作のタイミング問題
スクリプトがasyncで読み込まれる場合、DOMの状態(読み込み完了かどうか)によって動作が変わります。
// DOMの状態を確認してから実行
(function() {
function init() {
// ウィジェットの初期化処理
const containers = document.querySelectorAll('[data-koe-widget]');
// ...
}
if (document.readyState === 'loading') {
// まだ読み込み中 → イベントリスナーで待機
document.addEventListener('DOMContentLoaded', init);
} else {
// すでに読み込み完了 → 即実行
init();
}
})();
iframeとの使い分け
詳しくはWebサイトのiframeとEmbed完全ガイドを参照してください。
簡単な判断基準:
- 外部サービス(YouTube・Google Maps)の埋め込み → iframe(提供された埋め込みコードを使う)
- 自社ウィジェット・カスタマイズが必要なもの → JavaScriptスクリプト方式(レビューウィジェットの埋め込み方法も参考になります)
まとめ
埋め込みJavaScriptウィジェットの開発では、グローバルスコープの汚染防止・XSS対策・CORS設定・非同期読み込みの4点が特に重要です。Shadow DOMを使ったスタイル分離まで実装できれば、どの埋め込み先でも一貫した表示が保てます。顧客データを自動化するAPI連携については、こちらの記事も参考になります。
テスティモニアルの収集から管理・ウィジェット表示まで一括で対応したい場合は、Koeをご活用ください。