Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

การนำทางย่อยหลายระดับด้วย Jekyll

ในโพสต์ก่อนหน้านี้ ฉันแสดงให้คุณเห็นถึงวิธีสร้างลิงก์การนำทางย่อยสำหรับ 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 โปรดคอยติดตาม!