
讀者看完一篇文章、又點進另一篇,過一陣子想回頭找剛才那篇,卻忘了標題。在側邊欄或文章下方放一個「最近看過的文章」清單,讀者一鍵就能回去,也能讓讀者在網站裡多看幾篇。
這篇用瀏覽器內建的 localStorage 來記錄,不需要外掛,也不用動資料庫。(原本另一篇〈使用 JS 實現 WordPress 文章瀏覽歷史功能〉的內容已經整合到這裡。)
為什麼用 localStorage?
localStorage 是瀏覽器提供的小型儲存空間,網站可以把資料存在訪客自己的瀏覽器裡。拿它來記錄瀏覽紀錄有幾個好處:
- 不用存資料庫:每個訪客的紀錄存在自己的電腦或手機上,網站主機完全不用處理。
- 不受快取影響:清單是在瀏覽器裡用 JavaScript 產生的,就算文章頁被快取外掛存成靜態 HTML,每個人看到的還是自己的紀錄。
- 資料不會傳到主機:跟 Cookie 不同,localStorage 的資料不會在每次連線時送到伺服器。
限制是:紀錄只存在那一台裝置、那一個瀏覽器,換手機就看不到;訪客清除瀏覽資料或使用無痕模式,紀錄也會消失。對「最近看過的文章」這種功能來說,這樣剛剛好。
舊版的 Cookie 寫法請不要再用
這篇文章的舊版是用 PHP 把瀏覽紀錄存在 Cookie 裡,那段程式碼在現在的環境有好幾個問題:
- 用了
preg_replace()的/e修飾字,這個功能在 PHP 7 就移除了,現在的主機執行會出錯。 - 用
unserialize()讀取 Cookie 內容。Cookie 是訪客可以任意修改的,這是已知有資安漏洞的寫法。 - 把 Cookie 裡的值直接組進資料庫查詢,有 SQL 注入的風險。
- 清單是在伺服器端產生的,網站一裝快取外掛,所有人看到的清單就會一樣。
如果你的主題裡還留著這段程式(函式名稱是 zg_ 開頭),建議整段刪除,改用下面的做法。
步驟一:提供文章資訊並載入 JavaScript
在子主題的 functions.php(或 Code Snippets 外掛)加入:
add_action( 'wp_enqueue_scripts', 'ahwei_view_history_scripts' );
function ahwei_view_history_scripts() {
wp_enqueue_script(
'ahwei-view-history',
get_stylesheet_directory_uri() . '/js/view-history.js',
array(),
'1.0',
true // 放在頁面最後面載入
);
// 只有單篇文章頁才記錄
if ( is_singular( 'post' ) ) {
$post_id = get_queried_object_id();
$data = array(
'id' => $post_id,
'title' => html_entity_decode( wp_strip_all_tags( get_the_title( $post_id ) ), ENT_QUOTES, 'UTF-8' ),
'url' => get_permalink( $post_id ),
);
wp_add_inline_script(
'ahwei-view-history',
'window.ahweiCurrentPost = ' . wp_json_encode( $data ) . ';',
'before'
);
}
}這段做兩件事:
- 每一頁都載入
view-history.js,因為要顯示清單的頁面都需要它。 - 在單篇文章頁,把這篇文章的 ID、標題、網址交給 JavaScript 記錄下來。
想連頁面也一起記錄,把 is_singular( 'post' ) 改成 is_singular();只想記錄文章和商品,改成 is_singular( array( 'post', 'product' ) )。
步驟二:建立 view-history.js
在子主題的資料夾裡建立 js 資料夾,新增 view-history.js:
(function () {
const KEY = 'ahwei_view_history';
const LIMIT = 8; // 最多記錄幾篇
function load() {
try {
const list = JSON.parse(localStorage.getItem(KEY) || '[]');
return Array.isArray(list) ? list : [];
} catch (e) {
return [];
}
}
function save(list) {
try {
localStorage.setItem(KEY, JSON.stringify(list));
} catch (e) {
// 無痕模式或瀏覽器封鎖儲存空間時,直接略過
}
}
// 只接受自己網站的網址
function isSameSite(url) {
try {
return new URL(url, location.href).origin === location.origin;
} catch (e) {
return false;
}
}
let list = load().filter((item) => item && item.url && isSameSite(item.url));
const current = window.ahweiCurrentPost;
// 1. 在文章頁:把這篇移到最前面,超過上限的舊紀錄刪掉
if (current && current.url) {
list = list.filter((item) => item.id !== current.id);
list.unshift({ id: current.id, title: current.title, url: current.url });
list = list.slice(0, LIMIT);
save(list);
}
// 2. 頁面上有 id="view-history" 的容器,就把清單放進去
const wrap = document.getElementById('view-history');
if (!wrap) {
return;
}
// 不列出正在看的這一篇
const items = list.filter((item) => !current || item.id !== current.id);
if (items.length === 0) {
wrap.style.display = 'none';
return;
}
const ul = document.createElement('ul');
items.forEach((item) => {
const li = document.createElement('li');
const a = document.createElement('a');
a.href = item.url;
a.textContent = item.title;
li.appendChild(a);
ul.appendChild(li);
});
wrap.appendChild(ul);
})();幾個重點:
- 同一篇文章重複看,只會移到最前面,不會重複出現。
- 最多記錄 8 篇(修改
LIMIT),比較舊的會自動刪掉。 - 清單不會列出「正在看的這一篇」。
- 標題用
textContent放進去,而不是innerHTML,避免奇怪的內容被當成 HTML 執行;也會檢查網址是不是自己網站的。 - 無痕模式或瀏覽器封鎖儲存空間時,程式會安靜略過,不會出錯。
舊版的 JavaScript 還要處理 IE6、IE7 不支援 localStorage 的問題,現在的瀏覽器都支援,程式可以精簡很多。
之後修改了 view-history.js,記得把步驟一裡的版本號 '1.0' 改成 '1.1',訪客的瀏覽器才會重新下載新的檔案。
步驟三:放上顯示清單的位置
在想顯示清單的地方,放一個 id 是 view-history 的容器:
<div id="view-history">
<h3>最近看過的文章</h3>
</div>- 區塊小工具或區塊主題:在側邊欄,或「外觀 → 編輯器」的單篇文章範本裡,加入「自訂 HTML」區塊,貼上這段。
- 傳統主題:也可以直接放在子主題的
single.php或sidebar.php裡。
還沒有任何紀錄時(例如第一次來的訪客),整個區塊會自動隱藏,不會出現一個空的標題。
步驟四:加上樣式
下面的樣式會把每篇文章做成一顆一顆的標籤按鈕。CSS 可以放在「外觀 → 自訂 → 附加 CSS」(區塊主題在網站編輯器的樣式設定裡也有「附加 CSS」),或子主題的 style.css:
#view-history ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
#view-history a {
display: block;
padding: 6px 12px;
border-radius: 8px;
background: #f1f1f1;
color: #232323;
text-decoration: none;
}
#view-history a:hover {
background: #0099cb;
color: #fff;
}想改成一行一篇的直式清單,把 display: flex;、flex-wrap: wrap;、gap: 8px; 三行拿掉就好。
常見問題
為什麼我看了文章,清單卻沒有出現?
- 清單不會列出正在看的那一篇,至少要看過兩篇,在第二篇才會看到第一篇。
- 確認容器的
id是view-history,而且頁面有載入view-history.js(按 F12 打開開發者工具,在「Network」分頁搜尋檔名)。 - 有用快取或合併 JavaScript 的外掛的話,清除快取再試一次。
可以讓會員在不同裝置看到同樣的紀錄嗎?
localStorage 只存在瀏覽器裡,沒辦法跨裝置。要讓會員換手機也看得到,就必須把紀錄存到資料庫(例如使用者的個人資料欄位),那是另一種做法,也要多考慮資料量與隱私。
延伸閱讀
- WordPress 顯示文章閱讀次數:不用外掛的程式碼寫法(含快取網站解法)
- WordPress 顯示相關文章:不用外掛,依分類與標籤撈出相關文章
- WordPress 隨機文章:orderby rand 寫法與效能、快取問題
讓讀者更容易找到下一篇想看的文章,是提高網站停留時間的小細節。正威網路行銷是 25 年經驗的基隆網頁設計工作室,提供客製化網頁設計與 RWD 響應式網站,歡迎來聊聊。







發表留言
還沒有人留言,歡迎第一個分享你的想法或問題。留言經審核後才會顯示,Email 不會公開。