文章目錄

讀者在站內搜尋輸入的字詞,記錄的是他們想找、卻沒有直接在頁面上看到的內容,搜不到結果的詞也能看出網站還缺哪些文章。Google Analytics 4(GA4)內建的站內搜尋追蹤只認網址裡的查詢參數,像本站這種邊打字邊顯示結果、網址不會改變的搜尋面板,GA4 一筆也記錄不到。只要網站在讀者停在一個查詢時,把搜尋事件推進資料層(Data Layer),再用 Google 代碼管理工具(GTM)轉成 GA4 的事件,GA4 就會記錄每次搜尋。事件處理完成後,就能在探索報表看到讀者搜過的字詞,以及每個詞找到幾篇文章。
這個做法適用於已經安裝 GTM 的網站。搜尋面板本身的做法寫在 Pagefind 中文搜尋。
需要自己送事件的情況
GA4 的加強型評估預設會記錄站內搜尋,條件是搜尋結果頁的網址帶有查詢參數,預設認得 q、s、search、query、keyword 這幾個。網址符合條件時,GA4 會自動送出 view_search_results 事件,不用另外設定。網址沒有變化時,就要由網站自己送出搜尋事件。兩種搜尋送進 GA4 的路線如下,下排就是後面各段的設定順序:
網站推送的搜尋事件
搜尋面板通常邊打字邊更新結果。如果每次更新都送一筆事件,讀者輸入 cmd 的過程就會留下 cm、cmd 兩筆,報表裡會混進打到一半的字。本站等讀者停在一個查詢上才送出 search 事件,送出的時機有四種:停止打字 2 秒、從結果開啟文章、關閉搜尋面板,以及離開頁面。同一個詞在同一次瀏覽只送一次。看起來像電子郵件、電話號碼或 8 位數以上數字的查詢不會送出,因為 Google 的個人識別資訊規範不允許把這類資料送進 GA4。
搜尋面板要在四個地方呼叫下面的函式:讀者每次輸入時呼叫 typing(),每次搜尋完成後呼叫 settle(),沒有結果時 total 傳 0。讀者從結果開啟文章時呼叫 trackOpen(),滑鼠點擊、中鍵與鍵盤 Enter 都要算,關閉搜尋面板時則呼叫 flushSearch():
1function track(event, params) {
2 window.dataLayer = window.dataLayer || [];
3 window.dataLayer.push({ event, ...params });
4}
5
6// 電子郵件、電話號碼與 8 位數以上的數字不送
7function analyticsTerm(query) {
8 const term = query.trim().replace(/\s+/g, ' ');
9 if (!term || /\S+@\S+\.\S+|\+?\d[\d\s()-]{7,}\d|\d{8,}/.test(term))
10 return null;
11 return term.slice(0, 100);
12}
13
14const tracked = new Set();
15let settled = null;
16let timer = 0;
17
18// 讀者還在輸入,上一個查詢不是他要的
19function typing() {
20 clearTimeout(timer);
21 settled = null;
22}
23
24function settle(query, total) {
25 clearTimeout(timer);
26 settled = { query, total };
27 timer = setTimeout(flushSearch, 2000);
28}
29
30function flushSearch() {
31 clearTimeout(timer);
32 if (!settled) return;
33 const term = analyticsTerm(settled.query);
34 if (term && !tracked.has(term)) {
35 tracked.add(term);
36 track('search', {
37 search_term: term,
38 search_results: settled.total,
39 search_language: document.documentElement.lang, // 單一語言的網站可以拿掉
40 });
41 }
42 settled = null;
43}
44
45function trackOpen(query, href, position) {
46 flushSearch();
47 const term = analyticsTerm(query);
48 if (!term) return;
49 track('select_content', {
50 content_type: 'search_result',
51 content_id: new URL(href, location.href).pathname,
52 search_term: term,
53 search_position: position,
54 });
55}
56
57// 關閉搜尋面板時也呼叫 flushSearch()
58window.addEventListener('pagehide', flushSearch);
事件名稱用 GA4 的建議事件 search 與 select_content,不用加強型評估的 view_search_results。兩者都會填入「搜尋字詞」,查看時用事件名稱篩選,就能和網址型搜尋的資料分開。
兩個事件帶的參數如下,GTM 與 GA4 的設定都照這張表做,「GA4 維度」欄寫著「註冊為」的 3 個參數要在 GA4 建立自訂維度:
| 參數 | 事件 | 內容 | GA4 維度 |
|---|---|---|---|
search_term |
search、select_content |
讀者輸入的查詢 | 內建的「搜尋字詞」 |
search_results |
search |
找到幾篇文章 | 註冊為「搜尋結果數」 |
search_language |
search |
頁面語言,多語言網站才需要 | 註冊為「搜尋語言」 |
content_type |
select_content |
固定是 search_result |
內建的「內容類型」 |
content_id |
select_content |
開啟的文章路徑 | 內建的「內容 ID」 |
search_position |
select_content |
開啟的是第幾筆結果 | 註冊為「搜尋結果位置」 |
單一語言的網站拿掉 search_language 後,後面的 GTM 變數、代碼參數與 GA4 維度也不用建這一項。程式改好後,在網站搜尋一次並停 2 秒,在瀏覽器開發人員工具的 Console 輸入 dataLayer,可以找到 event 為 search 的一筆,裡面有剛才的查詢與結果數。
GTM 的設定
GTM 要讀出事件裡的參數、在事件出現時觸發,再把參數交給 GA4。以下都在工作區完成,按「提交」之前不會影響正式站。
-
「變數」→「使用者定義的變數」→「新增」,類型選「資料層變數」。「資料層變數名稱」填上表的參數名稱,例如
search_term,「資料層版本」保持「版本 2」。名稱的大小寫與底線要和網站推送的完全相同,GTM 才讀得到值。本站把變數命名為DLV - search_term,上表的每個參數各建一個。 -
「觸發條件」→「新增」→「自訂事件」,「事件名稱」填
search,啟動時機選「所有的自訂事件」,命名為CE - search。select_content也建一個CE - select_content。 -
「代碼」→「新增」→「Google Analytics:GA4 事件」,代碼名稱填
GA4 - search,「評估 ID」填 GA4 的評估 ID(G-開頭)。畫面出現「在這個容器中找到了 Google 代碼」時,事件代碼會沿用容器裡 Google 代碼的設定。 -
「事件名稱」填
search,「事件參數」逐一加上search的參數,值按積木圖示選對應的DLV -變數,觸發條件選CE - search:
select_content照第 3、4 步再建一個GA4 - select_content,接上CE - select_content。
代碼都建好後,「觸發條件」清單的「代碼」欄會從 0 變成 1,代表每個觸發條件都接上了事件代碼。
第 3 步沒有出現「找到了 Google 代碼」時,代表 GA4 不是由這個容器的 Google 代碼載入。這時先新增一個「Google 代碼」,代碼 ID 填同一個評估 ID,觸發條件選「Initialization - All Pages」。容器裡如果還留著通用 Analytics(UA)代碼,可以一起刪掉,UA 從 2023 年 7 月 1 日起就不再處理資料。
GA4 的設定
GTM 發布之前,先到 GA4 做兩項設定。
-
「管理」→「資料顯示」→「自訂定義」→「建立自訂維度」,範圍選「事件」,把上表要註冊的參數建成維度。網站還沒送出這些參數時,「事件參數」的下拉選單裡找不到它們,直接輸入參數名稱即可。
search_results註冊成維度,才能和「搜尋字詞」一起放在探索報表的列,依結果數排序:
依官方說明,自訂維度建立後約 24 到 48 小時才能在報表使用,所以要在網站開始送事件前先建好。
-
「管理」→「資料收集和修改」→「資料保留」,「事件資料」從預設的 2 個月改成 14 個月。資料保留只限制探索與漏斗報表,標準報表不受影響。「搜尋字詞」只能在探索報表查看,保留 2 個月時,就只能查 2 個月內的搜尋。調長後,還沒刪除的既有資料也會一起延長保留。
發布與確認
-
網站的推送程式要先部署到要測試的網址。GA4 設定好後,回到 GTM 按「預覽」,Tag Assistant 會在新分頁開啟。網址填網站首頁後按「連結」,網站會在另一個視窗開啟。沒有安裝 Tag Assistant 擴充功能時,只有它開出的這個視窗會連線。
-
在這個視窗搜尋一次並停 2 秒,Tag Assistant 左側會多出
search事件。點開後,「已觸發的代碼」出現「GA4 - search」,下方標示「成功」:
本站容器裡還有其他用途的代碼,所以「未觸發的代碼」列了好幾個,只要「GA4 - search」在已觸發的那一區就對了。
-
確認後,按網站右下角 Tag Assistant 徽章上的「Finish」結束預覽。預覽時送出的事件帶有除錯標記,預設也會算進報表,結束預覽後再測試,就不會再帶這個標記。想讓之後預覽時的測試不算進報表,可以建立開發人員流量的資料篩選器,但篩選器只會套用到建立之後的資料。
-
回到 GTM 按「提交」,填寫版本名稱後按「發布」。本站的 GTM 容器在瀏覽器裡快取 15 分鐘,發布後馬上檢查還看到舊的行為時,先強制重新整理。
-
在正式站搜尋一次,再到 GA4「報表」→「即時總覽」,從「事件計數」資訊卡點進
search,再點search_term,大約一分鐘內就會看到剛才搜尋的字詞:
看到剛才的字詞,就代表網站、GTM 與 GA4 都接通了。
讀者搜尋的字詞
「搜尋字詞」是 GA4 的內建維度,但「事件」與「網頁和畫面」報表的維度選單都找不到它,要到「探索」建一份任意形式報表。剛送出的事件要等 GA4 處理幾個小時才會出現在探索,在那之前表格會提示沒有任何資料。
-
「探索」→「任意形式」。
-
「維度」旁的「+」搜尋「搜尋」,勾選「搜尋字詞」與自訂的「搜尋結果數」後按「確認」。「指標」確認有「事件計數」。
-
「列」放「搜尋字詞」與「搜尋結果數」,「值」放「事件計數」。範本預設的「城市」與「裝置類別」可以移除。
-
「篩選器」選「事件名稱」,「完全符合」
search,表格只會留下搜尋事件。 -
點「搜尋結果數」的欄名改成遞增排序,結果數為 0 的查詢會排在最上面:

這個維度的值是文字,所以 18 會排在 3 的前面。
結果數為 0 的詞,可能是讀者拼錯字,也可能是網站真的沒有讀者想找的內容。Nielsen Norman Group 的搜尋紀錄分析提到,只出現幾次的零結果查詢不必花太多心思,搜尋次數多的才值得處理。
想看哪些詞搜完有人開啟文章,把篩選器改成「事件名稱」與規則運算式相符 search|select_content,「列」再加上「事件名稱」,同一個詞底下會分成 search 與 select_content 兩列。select_content 也可以在「管理」→「資料顯示」→「事件」的「近期事件」點星號,標成重要事件,報表的「重要活動」欄就會算進這個動作。事件要先出現在「近期事件」清單才能標,本站是在開始送出後的隔天出現。
實測紀錄
- 本站的 GTM 不在網站的原始碼裡,而是由 Cloudflare 的「Google 標籤閘道」在 HTML 經過 Cloudflare 時注入,GTM 與 GA4 的請求都走 vervecode.dev 自己的網域(Google 的設定說明)。Workers 的預覽網址(
*.workers.dev)不屬於這個網域,閘道不會注入 GTM,所以搜尋事件只能在正式站驗證。 - 2026 年 11 月 8 日發布 GTM 與站內搜尋,當天在正式站搜尋
cmd並開啟第一筆結果,即時總覽就收到search與select_content。 - 到 11 月 9 日的過去 28 天,GA4 收到 14 次
search,來自 3 位使用者,共 9 個字詞。扣除作者測試用的cmd,還有minecraft、touchdesigner、vscode、「傳送玩家」等 8 個字詞,其中 3 個詞搜完有人開啟文章。 - 11 月 10 日查看時,報表的維度選單都沒有「搜尋字詞」。
- 11 月 11 日上午 9 點前在正式站搜尋「樹莓派」測試零結果,GA4 收到的請求帶著
search_results=0。11 點半查看探索還沒有資料,12 點半才出現在搜尋結果數排序的第一列。
總結
網址不變的搜尋面板,要由網站在讀者停在一個查詢時送出 search 事件,再用 GTM 轉成 GA4 事件,並在探索報表查看搜尋字詞。本站上線兩天,除了測試用的 cmd,還收到 8 個不同的搜尋字詞。
