0%

正威網路行銷工作室,專注於基隆、台北與全台客製化網頁設計,提供專業 RWD 響應式、SEO 搜尋引擎優化、品牌形象設計與網站維護託管服務。

搜尋文章
聯絡資訊
地址基隆市仁二路 220 號 5 樓
追蹤我們

WordPress 顯示「最近看過的文章」:用 localStorage 記錄瀏覽紀錄,不用存資料庫

木桌上顯示程式碼的筆電

讀者看完一篇文章、又點進另一篇,過一陣子想回頭找剛才那篇,卻忘了標題。在側邊欄或文章下方放一個「最近看過的文章」清單,讀者一鍵就能回去,也能讓讀者在網站裡多看幾篇。

這篇用瀏覽器內建的 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 外掛)加入:

PHP
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:

JavaScript
(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 的容器:

HTML
<div id="view-history">
  <h3>最近看過的文章</h3>
</div>
  • 區塊小工具或區塊主題:在側邊欄,或「外觀 → 編輯器」的單篇文章範本裡,加入「自訂 HTML」區塊,貼上這段。
  • 傳統主題:也可以直接放在子主題的 single.php 或 sidebar.php 裡。

還沒有任何紀錄時(例如第一次來的訪客),整個區塊會自動隱藏,不會出現一個空的標題。

步驟四:加上樣式

下面的樣式會把每篇文章做成一顆一顆的標籤按鈕。CSS 可以放在「外觀 → 自訂 → 附加 CSS」(區塊主題在網站編輯器的樣式設定裡也有「附加 CSS」),或子主題的 style.css:

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 只存在瀏覽器裡,沒辦法跨裝置。要讓會員換手機也看得到,就必須把紀錄存到資料庫(例如使用者的個人資料欄位),那是另一種做法,也要多考慮資料量與隱私。

延伸閱讀

讓讀者更容易找到下一篇想看的文章,是提高網站停留時間的小細節。正威網路行銷是 25 年經驗的基隆網頁設計工作室,提供客製化網頁設計與 RWD 響應式網站,歡迎來聊聊。

分享這篇文章

返回文章列表更多「Wordpress代碼教學」文章

發表留言

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

讓我們一起打造
您的專屬網站!