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 %}