在開發 Ruby on Rails 專案時,經常需要建立支援多層級分類的動態導航選單。本文介紹如何使用深度優先遍歷演算法,從資料庫中讀取分類資料並自動生成可摺疊展開的 HTML 選單結構。
分類資料儲存在 categories 表中,包含三個核心欄位:
id (integer) - 主鍵name (string) - 分類名稱parentid (integer) - 父分類 ID,頂層分類的 parentid 為 0在 Controller 的 index 動作中,首先查詢所有頂層分類(parentid=0),然後遞迴呼叫私有方法 buildmenu 來處理子分類。程式碼使用實例變數 @htmlmenu 累積 HTML 字串,最終在視圖中直接輸出。
def index
@htmlmenu = ""
@htmlmenu += "<ul>"
@root = Category.find(:all, :conditions => ['parentid=0'])
@root.each do |item|
if Category.find_by_parentid(item.id)
@htmlmenu += "<li><a href='#ChildMenu#{item.id}' onclick=\"DoMenu('ChildMenu#{item.id}')\">"
else
@htmlmenu += "<li><a href='/categories/#{item.id}'>"
end
@htmlmenu += item.name
@htmlmenu += "</a>"
buildmenu(item)
@htmlmenu += "</li>"
end
@htmlmenu += "</ul>"
end
private
def buildmenu(category)
@children = Category.find_all_by_parentid(category.id)
if @children.size != 0
@htmlmenu += "<ul id='ChildMenu#{category.id}' class='collapsed'>"
@children.each do |item|
if Category.find_all_by_parentid(item.id).size != 0
@htmlmenu += "<li><a href='#ChildMenu#{item.id}' onclick=\"DoMenu('ChildMenu#{item.id}')\">"
else
@htmlmenu += "<li><a href='/categories/#{item.id}'>"
end
@htmlmenu += item.name
@htmlmenu += "</a>"
buildmenu(item)
@htmlmenu += "</li>"
end
@htmlmenu += "</ul>"
end
end
搭配簡單的 JavaScript 與 CSS 即可實現摺疊展開效果:
<script type="text/javascript">
function DoMenu(emid) {
var obj = document.getElementById(emid);
obj.className = (obj.className.toLowerCase() == "expanded" ? "collapsed" : "expanded");
}
</script>
<style>
ul.collapsed { display: none; }
</style>
includes 或一次性載入所有分類並在記憶體中建立樹狀結構。config/initializers),但需注意新增分類後需重啟服務才能生效。此遞迴遍歷邏輯不僅適用於導航選單,同樣可用於檔案系統瀏覽、組織架構圖等任何樹狀資料的 HTML 生成場景。