Trong một bài trước, tôi đã hướng dẫn bạn cách tạo các liên kết phụ cho mỗi H2 trong một trang Jekyll. Trong bài đăng này, chúng tôi sẽ xây dựng dựa trên nền tảng đó và chỉ cho bạn cách bạn có thể thêm các mức điều chỉnh phụ tùy ý dựa trên H3, H4, v.v.
Tổng quan
Tôi đã chia nhỏ dự án này thành một vài bước:
- Đầu tiên, chúng tôi sẽ sử dụng nokogiri để lấy ra các phần được xác định bởi thẻ H3 "bên trong" thẻ H2
- Tiếp theo, chúng tôi sẽ sử dụng một thủ thuật thú vị để hiển thị các mức điều hướng phụ tùy ý. Chúng tôi sẽ tạo một mẫu đệ quy.
Trước khi bắt đầu, hãy làm rõ điều gì đó. Khi tôi đề cập đến thẻ H3 là bên trong của H2, tôi không có nghĩa là nó được lồng vào nhau theo nghĩa đen. Thay vào đó, tôi đang đề cập đến tình huống mà chúng ta thấy bên dưới:
<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>
Chia tài liệu thành nhiều phần
Vấn đề rõ ràng mà chúng ta gặp phải khi chia tài liệu HTML như tài liệu ở trên thành các phần, đó là không có gì được lồng vào nhau. Hầu hết các công cụ để phân tích cú pháp HTML được xây dựng để hoạt động với lồng ghép.
Đây không phải là một công cụ phá vỡ thỏa thuận, nhưng nó có nghĩa là chúng ta phải làm nhiều việc hơn một chút. Trong ví dụ dưới đây, chúng tôi tìm từng thẻ H2 và sau đó quét thủ công các thẻ anh chị em để tìm thẻ H3.
Tôi đã yêu thích và sử dụng một điều tra viên tùy chỉnh. Nếu bạn có bất kỳ câu hỏi nào về chúng, hãy xem bài đăng trên blog của tôi về chúng.
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
Tôi nhận ra rằng đây là một đoạn mã khá rắc rối để ném vào bạn, nhưng nó được xây dựng dựa trên công việc mà chúng tôi đã làm trong một bài viết trước. Nếu bạn có bất kỳ câu hỏi nào về cấu trúc của trình cắm Jekyll hoặc cách chúng tôi đang sử dụng nokogiri, vui lòng xem bài viết đó.
Khi tôi chạy mã này trên trang web tài liệu của chúng tôi, tôi nhận được một hàm băm trông giống như sau:
[{"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"}]},
...
Bây giờ tất cả những gì chúng ta phải làm là tìm ra cách hiển thị thứ này bằng cách sử dụng các mẫu lỏng.
Hiển thị Subnav
Thực sự không khó để hiển thị một điều hướng phụ sâu tùy ý bằng cách sử dụng các mẫu lỏng. Mẹo là sử dụng một phần hiển thị chính nó.
Trong bố cục của mình, tôi hiển thị một phần và chuyển vào bộ sưu tập các mục điều hướng.
{% include navigation_item.html collection=page.subnav level=0 %}
Phần này tạo ra các liên kết cho mức điều hướng này, và sau đó tự hiển thị, chuyển vào danh sách các phần tử con. Cũng giống như một hàm đệ quy, điều này về mặt lý thuyết có thể tồn tại mãi mãi. Chỉ dành cho các cú đá, tôi đã thêm một chút mã để cấp cho mỗi cấp của subnav một lớp như level-1
hoặc level-2
. Điều này thực sự hữu ích cho việc tạo kiểu.
{% 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 %}
Vậy là xong!
Điều này kết thúc chuyến đi ngắn của chúng tôi vào thế giới tuyệt vời của Jekyll. Trong vài ngày tới khi xuất bản một loạt các bài viết về nội bộ của Ruby, vì vậy hãy chú ý theo dõi!