1分でわかる要点
- 検索条件だけでURLを組み直すと、来訪元の目印であるUTMが消えます。
- 閲覧用URLは許可したUTMとhashを保持し、共有用URLは検索条件だけにします。
- 既存クエリを丸ごと残さず、allowlistで引き継ぐキーを決めます。
JavaScriptで検索条件をURLへ同期すると、検索語は反映されても、訪問時に付いていたUTMが消えることがあります。個人開発での修正をもとに、閲覧用URLと共有用URLを分ける方法を説明します。
UTMは「どこから来たか」を示すURLの目印
たとえば、メールに置くリンクに次の目印を付けます。
https://example.com/search?utm_source=newsletter&utm_medium=email&utm_campaign=autumn
utm_sourceは参照元、utm_mediumは媒体、utm_campaignはキャンペーンを区別します。この記事では、これらにutm_term、utm_content、utm_idを加えた6項目を保持します。UTM全種類の一覧ではありません。Google公式のUTMの説明
UTMの保持と計測の成功は別で、GA4の実データ集計や認証接続の成功は確認していません。
同意前の検索で消える経路
計測タグを同意後に読み込む画面では、次の順序でUTMを失うことがあります。
- UTM付きリンクから訪問する。
- 同意する前に検索する。
- 検索条件だけでURLを再構築する。
- 後から同意しても、その時点のURLにUTMがない。
検索語qだけから?q=webを作ると、元のUTMは自動では引き継がれません。URLSearchParamsが消したのではなく、新しいURLへ含めなかったためです。同意の条件は変えず、URL更新時に必要な情報を残します。
閲覧用と共有用で、残す情報を決める
図はURLの保持方針を示しています。計測データの送信経路ではありません。
- 閲覧用: 最新の検索条件に、許可したUTMとhashを追加する。
- 共有用: 検索結果の再現に必要な、最新の検索条件だけを使う。
既存クエリを丸ごとコピーせず、引き継ぐキーをallowlist(許可リスト)で決め、未知キーは除外します。元の訪問者のUTMを共有先へ持ち越さないための区別です。ただし、アドレス欄を直接コピーした場合はUTMも含まれます。
URLを作る処理を、画面の更新から分ける
以下は説明用に一般化したコードです。検索条件を文字列qに絞り、現在地はHTTP(S)のURLオブジェクトで受け取ります。
const UTM_KEYS = [
"utm_source", "utm_medium", "utm_campaign",
"utm_term", "utm_content", "utm_id",
];
/**
* @param {string} q 検索語
* @returns {URLSearchParams} 検索条件だけ
*/
function searchParams(q) {
const params = new URLSearchParams();
if (q.trim()) params.set("q", q.trim());
return params;
}
/**
* @param {URL} current 現在のURL
* @param {string} q 検索語
* @returns {string} 閲覧用の同一オリジンURL */
function browsingURL(current, q) {
const params = searchParams(q);
for (const key of UTM_KEYS) {
if (current.searchParams.has(key)) {
params.set(key, current.searchParams.get(key));
}
}
const query = params.toString();
return current.origin + current.pathname
+ (query ? "?" + query : "") + current.hash;
}
/**
* @param {URL} current 現在のURL
* @param {string} q 検索語
* @returns {string} UTMとhashを含まない共有用URL */
function sharingURL(current, q) {
const query = searchParams(q).toString();
return current.origin + current.pathname + (query ? "?" + query : "");
}
browsingURLだけがUTMとhashを引き継ぎ、sharingURLは検索条件だけから作ります。検索語を空にすればqだけが消え、閲覧用のUTMとhashは残ります。
UTMが重複した場合はget()で最初の値を取得し、set()で1つにします。空値はそのまま保持します。MDN: get()
日本語や&、+は値としてset()へ渡せば、toString()が変換します。先にencodeURIComponentをかける必要はありません。MDN: URLSearchParams
画面側では、入力から読んだ検索語qを渡します。
const current = new URL(location.href);
history.replaceState(history.state, "", browsingURL(current, q));
// 共有ボタンでは sharingURL(new URL(location.href), q) を使う。
replaceStateはページを再読み込みせず、現在の履歴エントリーを更新します。URLは同一オリジンに限定し、ルーターを使う場合はその更新APIに合わせてください。MDN: replaceState()
まとめ
URLを同期するときは、閲覧用と共有用で残す値を分けます。閲覧中のURLでは許可したUTMとhashを保持し、共有URLは検索条件だけから作ることで、元の流入元情報を共有先へ持ち越さずに済みます。
AIが本文整理と説明用コードの作成・実行確認を補助しました。題材・公開範囲・計測成功を主張しない方針は本人の指定に基づきます。