---
title: "收合的內容，為什麼按 Ctrl+F 找不到？聊聊 hidden=\"until-found\" - Neil 的作品集"
description: "FAQ、手風琴這類收合起來的內容，用 hidden 藏起來就搜尋不到。hidden=\"until-found\" 讓內容藏著也能被頁內搜尋與深層連結找到，找到時自動展開。這篇用可以直接操作的範例比較四種收合方式，也整理了實測時踩到的三個坑。"
canonical_url: "https://neillin.com/blog/hidden-until-found/"
last_updated: 2026-10-09
meta:
  author: Neil
  keywords: "無障礙網頁, 設計管理, 資料視覺化, UI, UX, 切版"
  "og:description": "FAQ、手風琴這類收合起來的內容，用 hidden 藏起來就搜尋不到。hidden=\"until-found\" 讓內容藏著也能被頁內搜尋與深層連結找到，找到時自動展開。這篇用可以直接操作的範例比較四種收合方式，也整理了實測時踩到的三個坑。"
  "og:image": "https://neillin.com/_og/s/o_dcb009c27eed19c4.png"
  "og:image:height": "600"
  "og:image:type": image/png
  "og:image:width": "1200"
  "og:title": "收合的內容，為什麼按 Ctrl+F 找不到？聊聊 hidden=\"until-found\" - Neil 的作品集"
  "twitter:image": "https://neillin.com/_og/s/o_dcb009c27eed19c4.png"
---

> Index of every page on this site: [llms.txt](https://neillin.com/llms.txt)

:::

# 收合的內容，為什麼按 Ctrl+F 找不到？聊聊 hidden="until-found"

2026年10月9日  

[前端技術](https://neillin.com/blog?category=frontend "查看「前端技術」分類的所有文章")HTMLCSS前端開發無障礙瀏覽器支援

  

> 話說在前頭：這篇是我練習 `hidden="until-found"` 的筆記，文中的實測是在 Chromium 上做的，其他瀏覽器的支援狀況變動很快，有講錯的地方歡迎指正。

## 前言

在我的 [現代 CSS 與 HTML 技巧整理清單](https://neillin.com/blog/css-techniques-checklist)裡，`hidden="until-found"` 也是一格還沒打勾的。上一篇寫 [popover 的 auto、manual、hint](https://neillin.com/blog/popover-auto-manual-hint) 時提到它，說它「降級比較安全」，這次就來好好驗證一下。

先講它要解決的問題。網站上收合起來的內容到處都是：常見問答、手風琴、長文裡的「展開更多」。使用者想找某個資訊時，很自然會按 Ctrl+F（Mac 是 ⌘+F）搜尋，結果瀏覽器告訴他「找不到」。

但內容明明就在頁面上，只是被收合了。對使用者來說，搜尋不到就等於這個資訊不存在。

先看一下你的瀏覽器支援度，後面的範例會用到：

你的瀏覽器支援度

- `hidden="until-found"`偵測中（需要 JavaScript）

## 先動手：四種收合方式，哪個搜得到？

這次一樣先玩再說。下面四個區塊用四種不同的方式收合，每個區塊上方都標了一個要搜尋的關鍵字（關鍵字只藏在各自的區塊裡）。請按 Ctrl+F 一個一個搜尋看看：

互動範例：按 Ctrl+F（Mac 是 ⌘+F）搜尋各區塊標示的關鍵字

### hidden

`<div hidden>`

要搜尋的關鍵字：

狀態：收合中

### height: 0

`height: 0; overflow: hidden`

要搜尋的關鍵字：

這段用高度 0 藏起來，裡面有企鵝。

狀態：收合中

### hidden="until-found"

`<div hidden="until-found">`

要搜尋的關鍵字：

這段用 until-found 藏起來，裡面有長頸鹿。

狀態：收合中

### until-found ＋ 常見 reset

`[hidden] { display: none !important }`

要搜尋的關鍵字：

這段用 until-found 藏起來，但被 reset 蓋掉了，裡面有無尾熊。

狀態：收合中

此處為互動範例，請在網頁上操作。

預期你會看到：

| 收合方式 | 搜得到嗎 | 搜到之後 |
| --- | --- | --- |
| `hidden` | 找不到 | — |
| `height: 0; overflow: hidden` | 找得到 | 瀏覽器說有一筆，但畫面上什麼都看不到，使用者會更困惑 |
| `hidden="until-found"` | 找得到 | **自動展開**，並捲到那段文字 |
| `until-found` ＋ 常見的 reset | 找不到 | reset 把它打回原形了，後面會講 |

如果你看到的結果不一樣，很可能是瀏覽器支援度的差異，也歡迎告訴我。

## 它是怎麼運作的？

寫法很單純，把 `hidden` 的值改成 `until-found` 就好：

```
<div hidden="until-found">
  收合起來，但搜得到的內容
</div>
```

背後的機制是這樣：

1. 瀏覽器不是用 `display: none` 藏它，而是用 `content-visibility: hidden`。內容不顯示，但文字仍然參與頁內搜尋。
2. 搜尋命中、或是網址的 `#id` 指向裡面的元素時，瀏覽器會先觸發一個 **`beforematch` 事件**，接著**自己移除 `hidden` 屬性**，再捲到那個位置。
3. 收合中的內容不在無障礙樹裡，裡面的按鈕也 Tab 不到。我在 Chromium 實測過，這點跟一般的 `hidden` 一樣，不用擔心報讀軟體唸到看不見的東西。

也就是說，它只負責「被找到時展開」。使用者自己點按鈕展開、收合，還是要你寫。

## 常見問答範例：事件、深層連結與 `aria-expanded`

下面是一個用 `until-found` 做的常見問答。每個答案收合時都是 `hidden="until-found"`，事件紀錄會列出 `beforematch` 什麼時候觸發：

互動範例：常見問答

收到 beforematch 時同步 aria-expanded（取消勾選看看錯誤示範）

###

收到商品後 7 天內都可以申請退貨，商品需保持完整包裝。

###

可以，結帳時在發票欄位填入 8 碼統一編號即可。

###

單筆訂單滿 1,000 元免運費，未滿則酌收 80 元物流處理費。

深層連結： 直接連到第 3 題答案裡的段落

事件紀錄（最新在最上面）

1. 還沒有事件。試試 Ctrl+F 搜尋答案裡的字，或點上面的連結。

此處為互動範例，請在網頁上操作。

可以這樣玩：

1. 按 Ctrl+F 搜尋「免運費」。文章這裡也有這個詞，多按一次 Enter 跳到下一筆，第 3 題就會自己展開。
2. 按「全部收合」，再點範例裡的深層連結，一樣會展開並捲過去。這代表 FAQ 可以把單一問題的網址分享出去，對方打開就直接看到答案。
3. 取消勾選「同步 aria-expanded」，再搜尋一次。

第 3 步就是這次最想提醒的無障礙問題：**瀏覽器只會移除 `hidden`，不會幫你改按鈕的 `aria-expanded`**。內容已經展開了，按鈕卻還說自己是 `false`，報讀軟體的使用者聽到的就是錯的狀態。

所以一定要監聽 `beforematch`，把狀態同步回來：

```
panel.addEventListener("beforematch", () => {
  button.setAttribute("aria-expanded", "true");
});
```

## 坑一：`el.hidden = !el.hidden` 會悄悄把它變回一般的 `hidden`

這個坑是我寫範例時實測出來的。很多人切換顯示的寫法是這樣：

```
button.addEventListener("click", () => {
  panel.hidden = !panel.hidden;
});
```

問題在於，收合時 `panel.hidden` 讀出來是**字串 `"until-found"`**，不是 `true`。整個流程會變成：

1. 一開始是 `hidden="until-found"`， `!panel.hidden` 是 `false`，所以展開，沒問題。
2. 再按一次收合， `!panel.hidden` 是 `true`，屬性變成 `hidden=""`，**也就是一般的 hidden**。

點開再收合一次之後，這段內容就再也搜不到了，而且畫面上完全看不出差別。比較安全的寫法是用自己的狀態決定，收合時明確寫回 `until-found`：

```
let open = false;

button.addEventListener("click", () => {
  open = !open;
  button.setAttribute("aria-expanded", String(open));
  if (open) {
    panel.removeAttribute("hidden");
  } else {
    panel.setAttribute("hidden", "until-found");
  }
});
```

## 坑二：CSS reset 裡的 `[hidden]` 會讓它失效

很多 CSS reset 或專案裡都有這一行，為了確保 `hidden` 一定藏得住：

```
[hidden] {
  display: none !important;
}
```

但 `hidden="until-found"` 也符合 `[hidden]` 這個選擇器，於是被強制套上 `display: none`。沒有被渲染的內容，頁內搜尋就找不到了，也就是上面對照範例的最後一個區塊。

有趣的是，我實測發現 `#id` 的深層連結在這種情況下還是會展開，所以只測連結很容易以為沒問題。修正方式是把 `until-found` 排除掉：

```
[hidden]:not([hidden="until-found" i]) {
  display: none !important;
}
```

順帶兩個小細節：

- **收合時盒子還在**： `content-visibility: hidden` 只是不畫內容，元素本身的 padding、邊框、背景都還在。面板如果有 padding，收合時記得拿掉，不然會留下一條空框。
- **Web Component 要注意 Shadow DOM**： `beforematch` 會冒泡，但不會穿出 Shadow DOM，監聽器要掛在 shadow root 裡面的元素上。

## 坑三：收合時順手加了 `inert`，就找不到了

有些元件收合時，除了把內容藏起來，還會加上 `inert`，確保鍵盤和報讀軟體碰不到裡面。我自己的 UI Kit 裡，手風琴和樹狀選單就是這樣寫的，導入 `until-found` 時就卡在這裡。

問題是，`inert` 的內容會被排除在頁內搜尋之外，HTML 規範就是這樣定義的。所以 `hidden="until-found"` 再加上 `inert`，Ctrl+F 一樣找不到，等於白做。實際導入時，搜尋找不到內容；連結雖然會展開面板，裡面卻無法操作（見下一段）。

我另外在 Chromium 141 和 153 測了 `#id` 深層連結：面板雖然會展開，但 `inert` 沒有被拿掉，裡面的東西照樣無法聚焦，報讀軟體也讀不到，變成看得到卻用不了。

其實用了 `until-found` 就不需要 `inert`。我實測收合中的內容本來就不在 Tab 順序裡，也不在無障礙樹裡，`inert` 想做到的事它已經做了：

```
<!-- 不要這樣 -->
<div hidden="until-found" inert>…</div>

<!-- 這樣就好 -->
<div hidden="until-found">…</div>
```

如果既有元件是用 `inert` 來收合，改用 `until-found` 時記得把收合和展開兩邊的 `inert` 都拿掉。

## 不支援的時候會怎樣？

這次的降級是安全的。不認得 `until-found` 的瀏覽器會把它當成一般的 `hidden`：內容一樣藏著，只是搜尋不到，什麼都不會壞。跟 `popover="hint"` 不支援時退化成 `manual`、連關都關不掉相比，可以放心地當成漸進增強來用。

偵測方式：

```
const supportsUntilFound = "onbeforematch" in HTMLElement.prototype;
```

`until-found` 在 Chromium 系瀏覽器很早就支援了，Firefox 和 Safari 的狀況變動比較快，上線前請以 [caniuse](https://caniuse.com "另開新視窗") 為準。

## 那 `<details>` 呢？

如果只是單純的展開收合，原生的 `<details>` 其實也會在頁內搜尋命中時自動展開（Chromium 系瀏覽器支援），而且連按鈕和狀態都不用自己做。

我的選擇方式是：

- **單純的「展開更多」**：先考慮 `<details>`，最省事。
- **需要標題階層、自訂結構的手風琴**（例如標題包按鈕、控制 `aria-controls` 的寫法），或是不方便改成 `<details>` 的既有元件：用 `hidden="until-found"`。

## 結論

整理成幾句話：

- 收合不等於消失。用 `hidden` 藏起來的內容，對搜尋來說就是不存在。
- `hidden="until-found"` 讓內容藏著也找得到，找到時自動展開，深層連結也適用。
- 記得監聽 `beforematch` 同步 `aria-expanded`，切換時別用 `el.hidden = !el.hidden`，也檢查一下 CSS reset 有沒有把它蓋掉。
- 收合時不要再加 `inert`，不然一樣搜尋不到； `until-found` 本身就會把內容排除在 Tab 順序和無障礙樹之外。
- 不支援時會退回一般的 `hidden`，降級安全，可以放心用。

這個屬性解決的，是使用者「明明知道有、卻找不到」的那種挫折感。做無障礙久了會發現，很多問題不是功能做不到，而是資訊被藏到使用者碰不到的地方，這就是一個很好的例子。

清單上又可以多打一個勾了。你的網站裡有多少收合起來的內容呢？歡迎一起討論！

## 延伸閱讀

- [Popover 的 auto、manual、hint 到底差在哪？做個可以玩的範例來看看](https://neillin.com/blog/popover-auto-manual-hint/)
- [現代 CSS 與 HTML 技巧整理清單：115 個特性、支援度與實驗優先序](https://neillin.com/blog/css-techniques-checklist/)

## 相關連結

- [MDN — hidden 全域屬性](https://developer.mozilla.org/zh-TW/docs/Web/HTML/Global_attributes/hidden "另開新視窗")
- [MDN — beforematch 事件](https://developer.mozilla.org/en-US/docs/Web/API/Element/beforematch_event "另開新視窗")
- [Can I use（瀏覽器支援度查詢）](https://caniuse.com "另開新視窗")

```json
{"@context":"https://schema.org","@graph":[{"@id":"https://neillin.com/#website","@type":"WebSite","inLanguage":"zh-Hant-TW","name":"Neil 的作品集","url":"https://neillin.com/","workTranslation":{"@id":"https://neillin.com/en/#website"}},{"@id":"https://neillin.com/blog/hidden-until-found/#webpage","@type":"WebPage","description":"FAQ、手風琴這類收合起來的內容，用 hidden 藏起來就搜尋不到。hidden=\"until-found\" 讓內容藏著也能被頁內搜尋與深層連結找到，找到時自動展開。這篇用可以直接操作的範例比較四種收合方式，也整理了實測時踩到的三個坑。","name":"收合的內容，為什麼按 Ctrl+F 找不到？聊聊 hidden=\"until-found\"","url":"https://neillin.com/blog/hidden-until-found/","isPartOf":{"@id":"https://neillin.com/#website"},"potentialAction":[{"@type":"ReadAction","target":["https://neillin.com/blog/hidden-until-found/"]}]},{"@id":"https://neillin.com/#person","@type":"Person","jobTitle":"UI/UX 設計師","name":"Neil Lin","url":"https://neillin.com/","sameAs":["https://github.com/Neil-Lin"]},{"@id":"https://neillin.com/blog/hidden-until-found/#/schema/blog-posting/1","@type":"BlogPosting","dateModified":"2026-10-09","datePublished":"2026-10-09","description":"FAQ、手風琴這類收合起來的內容，用 hidden 藏起來就搜尋不到。hidden=\"until-found\" 讓內容藏著也能被頁內搜尋與深層連結找到，找到時自動展開。這篇用可以直接操作的範例比較四種收合方式，也整理了實測時踩到的三個坑。","headline":"收合的內容，為什麼按 Ctrl+F 找不到？聊聊 hidden=\"until-found\"","inLanguage":"zh-Hant-TW","keywords":"HTML, CSS, 前端開發, 無障礙, 瀏覽器支援","url":"https://neillin.com/blog/hidden-until-found/","author":{"@id":"https://neillin.com/#person"},"mainEntityOfPage":{"@type":"WebPage","@id":"https://neillin.com/blog/hidden-until-found/"},"publisher":{"@id":"https://neillin.com/#person"}},{"@id":"https://neillin.com/blog/hidden-until-found/#/schema/breadcrumb-list/1","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"回首頁","item":"https://neillin.com/"},{"@type":"ListItem","position":2,"name":"部落格","item":"https://neillin.com/blog/"},{"@type":"ListItem","position":3,"name":"收合的內容，為什麼按 Ctrl+F 找不到？聊聊 hidden=\"until-found\"","item":"https://neillin.com/blog/hidden-until-found/"}]}]}
```

## Sitemap

See the full [sitemap](https://neillin.com/sitemap.md) for all pages.
