ในโพสต์ก่อนหน้านี้ ฉันแสดงให้คุณเห็นถึงวิธีสร้างลิงก์การนำทางย่อยสำหรับ H2 แต่ละรายการในหน้า Jekyll ในโพสต์นี้ เราจะสร้างบนพื้นฐานนั้นและแสดงให้คุณเห็นว่าคุณสามารถเพิ่มระดับการนำทางย่อยตาม H3, H4 ฯลฯ ได้อย่างไร
ภาพรวม
ฉันได้แบ่งโปรเจ็กต์นี้ออกเป็นสองขั้นตอน:
- ขั้นแรก เราจะใช้ nokogiri เพื่อดึงส่วนที่กำหนดโดยแท็ก H3 "ภายใน" ของแท็ก H2 ออก
- ต่อไป เราจะใช้เคล็ดลับเจ๋งๆ เพื่อแสดงระดับการนำทางย่อยตามอำเภอใจ เรากำลังจะสร้างเทมเพลตแบบเรียกซ้ำ
ก่อนที่เราจะเริ่มต้น เรามาทำให้ชัดเจนเสียก่อน เมื่อฉันอ้างถึงแท็ก H3 ว่าอยู่ภายใน H2 ฉันไม่ได้หมายความว่าแท็กนั้นซ้อนกันอย่างแท้จริง ฉันหมายถึงสถานการณ์ที่เราเห็นด้านล่างแทน:
<h2>Animals</h2>
<p>Here are some kinds of animals.</p>
<h3>Giraffe</h3>
<p>This section about giraffes logically belongs inside of the section about animals, even though the structure of the Dom doesn't define it as being nested</p>
<h3>Zebra</h3>
<p>Another section that logically belongs under "Animals"</p>
การแบ่งเอกสารเป็นส่วนๆ
ปัญหาที่เห็นได้ชัดที่เราเผชิญเมื่อแบ่งเอกสาร HTML ดังที่กล่าวไว้ด้านบนออกเป็นส่วนๆ คือไม่มีอะไรซ้อนอยู่ เครื่องมือส่วนใหญ่สำหรับการแยกวิเคราะห์ HTML สร้างขึ้นเพื่อทำงานกับการซ้อน
นี่ไม่ใช่ตัวทำลายข้อตกลง แต่มันหมายความว่าเราต้องทำงานเพิ่มขึ้นอีกเล็กน้อย ในตัวอย่างด้านล่าง เราจะพบแต่ละแท็ก H2 จากนั้นจึงสแกนพี่น้องหาแท็ก H3 ด้วยตนเอง
ฉันได้รับแฟนซีและใช้การแจงนับที่กำหนดเอง หากคุณมีคำถามใดๆ เกี่ยวกับสิ่งเหล่านั้น โปรดดูโพสต์บนบล็อกของฉันเกี่ยวกับคำถามเหล่านั้น
require "nokogiri"
class MySubnavGenerator < Jekyll::Generator
def generate(site)
parser = Jekyll::Converters::Markdown.new(site.config)
site.pages.each do |page|
if page.ext == ".md"
doc = Nokogiri::HTML(parser.convert(page['content']))
page.data["subnav"] = doc.css('h2').map do |h2|
to_nav_item(page, h2).tap do |item|
item["children"] = subheadings(h2).map { |h3| to_nav_item(page, h3) }
end
end
end
end
end
# Converts a heading into a hash of the info for a link
def to_nav_item(page, heading)
{
"title" => heading.text,
"url" => [page.url, heading['id']].join("#")
}
end
# Returns an enumerator of all H3s "belonging" to an H2
def subheadings(el)
Enumerator.new do |y|
next_el = el.next_sibling
while next_el && next_el.name != "h2"
if next_el.name == "h3"
y << next_el
end
next_el = next_el.next_sibling
end
end
end
end
ฉันตระหนักดีว่านี่เป็นโค้ดจำนวนมากที่จะโยนใส่คุณ แต่มันสร้างจากงานที่เราทำในโพสต์ก่อนหน้านี้ หากคุณมีคำถามเกี่ยวกับโครงสร้างของปลั๊กอิน Jekyll หรือวิธีที่เราใช้ nokogiri โปรดตรวจสอบบทความนั้น
เมื่อฉันเรียกใช้โค้ดนี้กับไซต์เอกสารของเรา ฉันได้รับแฮชที่มีลักษณะดังนี้:
[{"title"=>"Getting Started",
"url"=>"/lib/java.html#getting-started",
"sub_subnav"=>
[{"title"=>"Download / Maven", "url"=>"/lib/java.html#download-maven"},
{"title"=>"Stand Alone Usage", "url"=>"/lib/java.html#stand-alone-usage"},
{"title"=>"Servlet Usage", "url"=>"/lib/java.html#servlet-usage"},
{"title"=>"Play Usage", "url"=>"/lib/java.html#play-usage"},
{"title"=>"API Usage", "url"=>"/lib/java.html#api-usage"}]},
...
ตอนนี้สิ่งที่เราต้องทำคือหาวิธีแสดงผลสิ่งนี้โดยใช้เทมเพลตของเหลว
การแสดงผล Subnav
จริง ๆ แล้วมันไม่ยากนักที่จะแสดงผลการนำทางย่อยในเชิงลึกโดยใช้เทมเพลตของเหลว เคล็ดลับคือการใช้บางส่วนที่แสดงตัวเอง
ในเลย์เอาต์ของฉัน ฉันแสดงผลบางส่วนและส่งผ่านไปยังคอลเล็กชันรายการการนำทาง
{% include navigation_item.html collection=page.subnav level=0 %}
บางส่วนจะสร้างลิงก์สำหรับการนำทางในระดับนี้ แล้วแสดงผลเอง โดยส่งผ่านรายการย่อย เช่นเดียวกับฟังก์ชันเรียกซ้ำ ในทางทฤษฎีสามารถดำเนินต่อไปได้ตลอดไป ฉันได้เพิ่มโค้ดเล็กน้อยเพื่อให้แต่ละระดับของ subnav มีคลาสเช่น level-1
หรือ level-2
. มีประโยชน์มากสำหรับการจัดสไตล์
{% if include.collection.size > 0 %}
<ul class="nav nav-list level-{{ include.level }}">
{% for item in include.collection %}
{% if item.url == page.url %}
<li class="active">
{% else %}
<li>
{% endif %}
{% if item.subnav.size > 0 %}
<a class="has-subnav" href="{{ item.url }}">
<span class="glyphicon glyphicon-plus"></span>
<span class="glyphicon glyphicon-minus"></span>
{% else %}
<a href="{{ item.url }}">
{% endif %}
{{ item.title }}
</a>
{% assign next_level = include.level | plus: 1 %}
{% include navigation_item.html collection=item.children level=next_level %}
</li>
{% endfor %}
</ul>
{% endif %}
แค่นั้น!
นี่เป็นการสรุปการจู่โจมสั้นๆ ของเราในโลกมหัศจรรย์ของเจคิลล์ ในอีกไม่กี่วันข้างหน้าของการเผยแพร่ชุดบทความเกี่ยวกับ Ruby internals โปรดคอยติดตาม!