---
# System prepended metadata

title: HTML / 元件 / tree viewer
tags: [HTML, summary, details, file, icon, frontend, HTML5, frontend / HTML, tree, viewer, free, folder]

---

HTML / 元件 / tree viewer
===
###### tags: `frontend / HTML`
###### tags: `frontend`, `HTML`, `HTML5`, `tree`, `viewer`, `details`, `summary`, `folder`, `file`, `icon`, `free`

<br>

[TOC]

<br>


## 專業級 TreeViewr
- https://github.com/SlinkyProject/slurm-operator/commit/431d7de00cf07bd3259e43f72b9a984f016bfd12
    ![image](https://hackmd.io/_uploads/rJb2klgjll.png)

<br>

## 箭頭範例 - 文字樣式
![](https://hackmd.io/_uploads/rJ8beRY_gg.png =30%x)

- ### code
    ```html=
    <html>

    <style>
        details {
          margin-left: 1em; /* 基本縮排 */
        }

        details[open] > summary::before {
          content: "▼ "; /* 展開狀態的 icon */
          font-size: 0.8em;
        }

        details > summary::before {
          content: "▶ "; /* 收合狀態的 icon */
          font-size: 0.8em;
        }

        summary {
          cursor: pointer;
          list-style: none; /* 移除預設符號 */
        }

        summary::-webkit-details-marker {
          display: none; /* 移除 Chrome 預設箭頭 */
        }

        /* 針對不同層級加大縮排 */
        details details {
          margin-left: 1.5em;
        }
    </style>

    <body>
    <details open>
        <summary>旅遊</summary>
        <details close>
            <summary>機場用語</summary>
        </details>
        <details close>
            <summary>飯店用語</summary>
            <details open>
                <summary>櫃台用語</summary>
            </details>
            <details open>
                <summary>餐廳用語</summary>
            </details>
        </details>
    </details>

    </body>
    </html>
    ```
- ### 重點說明

    1. **縮排**：用 `margin-left` 依層級加大，`details details` 可以選到第二層再加縮排。
    2. **icon**：用 `summary::before` 加文字或 SVG，並用 `details[open]` 判斷展開狀態切換。
    3. **移除預設箭頭**：
       * `summary::-webkit-details-marker { display: none; }` 可隱藏 Chrome 預設箭頭。
    4. **可改成 SVG 圖示**：如果不想用 "▶ / ▼"，可以用背景圖片或內嵌 `<svg>`。

<br>

---

<br>

## 箭頭範例 - svg 圖片樣式
![](https://hackmd.io/_uploads/r1a-KAYOel.png =30%x)
- ### code
    ```html=
    <html>

    <style>
        details {
            margin-left: 1em; /* 第一層縮排 */
        }

        summary {
            cursor: pointer;
            list-style: none; /* 移除預設符號 */
            padding-left: 1.2em; /* 預留背景圖位置 */
            background-repeat: no-repeat;
            background-position: 0 50%; /**/
            background-size: 1em 1em;
        }

        /* 收合狀態的箭頭 (右向) */
        summary {
            background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><polygon points='2,1 8,5 2,9' fill='%23333'/></svg>");
        }

        /* 展開狀態的箭頭 (下向) */
        details[open] > summary {
            background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><polygon points='1,2 9,2 5,8' fill='%23333'/></svg>");
        }
    </style>

    <body>
    <details open>
        <summary>旅遊</summary>
        <details close>
            <summary>機場用語</summary>
        </details>
        <details close>
            <summary>飯店用語</summary>
            <details open>
                <summary>櫃台用語</summary>
            </details>
            <details open>
                <summary>餐廳用語</summary>
            </details>
        </details>
    </details>

    </body>
    </html>
    ```
    - `background-position: 0 50%;`
        這是設定**背景圖片的位置**，它有兩個值：
        1. **`0`** → **水平位置**
           * 表示背景圖片的左邊對齊元素的最左邊（`0`% = 左邊界），也可以寫 `left`，效果一樣。
           * `100`% = 右邊界），也可以寫 `right`，效果一樣。
        2. **`50%`** → **垂直位置**
           * 表示背景圖片的中間點對齊元素的垂直中間（`50%` = 垂直置中）。
           * 也可以寫 `center`，效果一樣。

        🔍 **簡單理解**：
        - 把背景圖片放在元素**最左邊**，並且**垂直置中**，所以不管文字多高，icon 都會在中間。
        - 如果改成 `background-position: 5px center;`，它就會往右移 5px 再垂直置中。
- ### 重點特色
    1. **背景圖是 SVG Data URI**
       * 好處：不用額外檔案，顏色（`fill='%23333'`）可以直接改成自己要的十六進位色碼。
       * SVG 右箭頭與下箭頭是兩個不同的 Data URI。
    2. **移除預設箭頭**
       * 不用 `summary::-webkit-details-marker`，因為背景圖本身就蓋掉預設箭頭了。


---

<br>

## 箭頭範例 - 使用 border 樣式
![](https://hackmd.io/_uploads/H13k0AFdgg.png =45%x)
- ### code
    ```html=
    <html>
    <style>
        /* 樹狀結構容器 */
        .tree details {
            padding-left: 1.2em; /* 縮排空間 */
            position: relative;
        }

        /* 標題行樣式 */
        .tree summary {
            cursor: pointer;
            list-style: none;
            display: flex;
            align-items: center;
            font-family: Arial, sans-serif;
        }

        /* 移除預設箭頭 */
        .tree summary::-webkit-details-marker {
            display: none;
        }

        /* 自訂箭頭圖示（初始為收合狀態） */
        .tree summary::before {
            content: "";
            display: inline-block;
            width: 0.5em;
            height: 0.5em;
            border-right: 2px solid #555;
            border-bottom: 2px solid #555;
            transform: rotate(-45deg); /* ▶ 方向 */
            margin-left: 0.5em;
            margin-right: 0.5em;
            transition: transform 0.2s ease;
        }

        /* 展開狀態旋轉箭頭 */
        .tree details[open] > summary::before {
            transform: rotate(45deg); /* ▼ 方向 */
        }

        /* 第二層開始額外縮排 */
        .tree details details {
            padding-left: 1.5em;
        }

        /* 顏色與 hover 效果（可改） */
        .tree summary:hover {
            background-color: #f0f0f0;
        }
    </style>

    <body>
    <div class="tree">
        <details open>
            <summary>旅遊</summary>
            <details close>
                <summary>機場用語</summary>
            </details>
            <details close>
                <summary>飯店用語</summary>
                <details open>
                    <summary>櫃台用語</summary>
                </details>
                <details open>
                    <summary>餐廳用語</summary>
                </details>
            </details>
        </details>
    </div>
    </body>
    </html>
    ```
    - `display: flex; align-items: center;`
        1. **水平排列**：箭頭（`::before`）和文字會在同一行，不會因為換行或 baseline 不齊而跳位。
        2. **垂直置中**（`align-items: center;`）：確保箭頭與文字的中線對齊，看起來比較整齊。

- ### 重點特色
    1. **多層縮排**：每一層 `<details>` 都會自動加左邊縮排。
    2. **旋轉箭頭**：用 `::before` 畫一個小箭頭，收合時指向右邊，展開時旋轉指向下方。
    3. **可無限層級**：無論幾層 `<details>` 都會自動套用縮排與箭頭效果。
    4. **hover 高亮**：滑鼠移到項目上會加背景色（可自行調整）。

---

<br>

## icon 範例 - 樹狀檢視器(Tree Viewer)
![image](https://hackmd.io/_uploads/By_DVhcuxg.png =50%x)

- ### code
    ```html=
    <html>
    <style>
    .tree {
      font-family: Arial, system-ui, -apple-system, "Noto Sans TC", sans-serif;
      line-height: 1.6;
    }

    /* 每層資料夾的縮排 */
    .tree details {
      padding-left: 1.2em;
    }
    .tree details details {
      padding-left: 1.5em;
    }

    /* summary 樣式 */
    .tree summary {
      cursor: pointer;
      list-style: none;
      display: flex;
      align-items: center;
      gap: 6px;
      border-radius: 6px;
      padding: 2px 4px;
    }
    .tree summary:hover { background: #f3f4f6; }

    /* 移除預設箭頭 */
    .tree summary::-webkit-details-marker { display: none; }

    /* 資料夾 icon */
    .tree details > summary .icon::after { content: "📁"; }
    .tree details[open] > summary .icon::after { content: "📂"; }

    /* 檔案 icon */
    .tree .file::before { content: "📄"; margin-right: 6px; }

    /* 檔案列縮排與 hover 效果 */
    .tree .file {
      display: flex;
      align-items: center;
      padding: 2px 4px 2px calc(1.2em + 2px); /* 與資料夾 icon 對齊 */
      border-radius: 6px;
    }
    .tree .file:hover { background: #f9fafb; }
    </style>

    <div class="tree">
      <details open>
        <summary><span class="icon"></span>旅遊</summary>

        <details open>
          <summary><span class="icon"></span>機場用語</summary>
          <div class="file">值機常用句.pdf</div>
          <div class="file">安檢問答.md</div>
        </details>

        <details>
          <summary><span class="icon"></span>飯店用語</summary>

          <details open>
            <summary><span class="icon"></span>櫃台用語</summary>
            <div class="file">入住/退房句型.docx</div>
          </details>

          <details open>
            <summary><span class="icon"></span>餐廳用語</summary>
            <div class="file">點餐流程.txt</div>
            <div class="file">過敏原詢問.md</div>
          </details>

        </details>
      </details>
    </div>
    </body>
    </html>
    ```
- ### 重點
    * **沒有箭頭**：完全移除箭頭相關 CSS，只用資料夾圖示來提示展開 / 收起狀態。
    * **資料夾圖示會變化**：
      * 收起時 📁
      * 展開時 📂
    * **檔案圖示**：使用 📄 並自動縮排到與資料夾文字對齊。
    * **支援多層巢狀**：每多一層 `<details>` 就會自動縮排。

<br>

## 討論

### `details[]` vs `details`
- ### Question
    ```
    .tree details[] > summary .icon::before {
        content: "📁"
    }
    .tree details > summary .icon::before { 
        content: "📁"
    }
    ```
    前後差在哪裡?
- ### gpt-5 (2025/08/13)
    你寫的兩個選擇器
    ```css
    .tree details[] > summary .icon::before { ... }
    ```
    和
    ```css
    .tree details > summary .icon::before { ... }
    ```
    最大的差別在 **`details[]`** 這個部分。

    ---

    ### 1. `.tree details > summary .icon::before`

    這是**正常有效**的選擇器，意思是：

    > 在 `.tree` 元素裡，選擇 **任何 `<details>`**，其**直接子元素 `<summary>`** 裡的 `.icon` 元素，然後套用 `::before` 內容。

    範例結構匹配：

    ```html
    <div class="tree">
      <details>
        <summary>
          <span class="icon"></span>
        </summary>
      </details>
    </div>
    ```

    這裡 `.icon::before` 就會被套用。

    ---

    ### 2. `.tree details[] > summary .icon::before`

    `details[]` 在 CSS 選擇器語法中，**`[]` 是屬性選擇器**的語法，**裡面必須要有屬性條件**，像：

    * `details[open]` → 選展開的 `<details>`
    * `details[title]` → 選有 `title` 屬性的 `<details>`
    * `details[data-id="123"]` → 選有特定屬性值的 `<details>`

    如果你什麼都不寫成 `details[]`，這在 CSS 裡是**不合法的選擇器**，瀏覽器會忽略這條規則，等於沒作用。

    ---

    ### 3. 正確的展開 / 收起資料夾寫法

    如果你要針對展開狀態換 icon，就應該寫成：

    ```css
    /* 收起資料夾 */
    .tree details > summary .icon::before {
      content: "📁";
    }

    /* 展開資料夾（有 open 屬性） */
    .tree details[open] > summary .icon::before {
      content: "📂";
    }
    ```

    這樣在 `<details>` 展開時（`open` 屬性存在）就會自動切換。

<br>

{%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}