কম্পিউটার টিউটোরিয়াল

গভীর ডাইভ: জেকিলে মাল্টি-লেভেল সাবনেভিগেশন বিল্ড করা

একটি পূর্ববর্তী পোস্টে আমি আপনাকে দেখিয়েছিলাম জেকিল পেজের প্রতিটি H2-এর জন্য সাবনেভিগেশন লিংক জেনারেট কীভাবে করবেন। এই পোস্টে, আমরা সেটি ফাউন্ডেশন হিসেবে ব্যবহার করে দেখাবো কীভাবে আপনি 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 ডকুমেন্টকে সেকশনে ভাঙার Châu 뭣 একটু পরিশ্কার সমস্যা হচ্ছে, কিছুই নেস্টেড নয়। 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

  # একটি হেডিংকে নেভ লিংক ইনফোর হ্যাশে কনভার্ট করুন
  def to_nav_item(page, heading)
    { "title" => heading.text, "url" => [page.url, heading['id']].join("#") }
  end

  # একটি H2-র অধীনে সকল H3 রিটার্ন করার একটি এনুমেরেটর
  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

মনে হয় এটি আপনার কাছে অনেক কোড একসাথে ফেলা হয়েছে, কিন্তু এটি একটি পূর্ববর্তী পোস্টের ওপর বিল্ট। জেকিল প্লাগিনের স্ট্রাকচার বা 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"}]}, ... ]

এখন বাকি আছে এই জিনিসটি লিকুইড টেমপ্লেট দিয়ে রেন্ডার করা।

সাবনেভ রেন্ডারিং

লিকুইড টেমপ্লেট ব্যবহার করে আর্বিট্রারি গভীর সাব নেভিগেশন রেন্ডার করা আসলে তত কঠিন নয়। ট্রিক হচ্ছে একটি পার্শিয়াল ব্যবহার করা যা নিজে নিজেকে রেন্ডার করে।

আমার লেয়াউটে, আমি পার্শিয়ালটি রেন্ডার করি এবং নেভিগেশন আইটেমের কালেকশন পাস করি।

{% include navigation_item.html collection=page.subnav level=0 %}

পার্শিয়াল এই লেভেলের নেভিগেশন লিংক তৈরি করে, এবং তারপর নিজেকে রেন্ডার করে, চাইল্ডরেনের লিস্ট পাস করে। একটি রিকার্সিভ ফাংশনের মতো, এটি থিওরেটিক্যালি অনন্ত পর্যন্ত চলতে পারে। শুধু মজার জন্য, আমি প্রতিটি সাবনেভ লেভেলের জন্য 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 %}

শেষ!

এটি শেষ আমাদের জেকিলের চমৎকার জগতে সংক্ষিপ্ত ভ্রমণ। আগামী কিছু দিনে রুবি ইন্টারন্যালস নিয়ে একটি সিরিজ আর্টিকেল পাবলিশ করব, তাই টিউন্ড থাকুন!

  1. জাভাস্ক্রিপ্টে মাল্টি-স্টেপ ফর্ম (Multi-step Form) তৈরি করা

  2. অ্যান্ড্রয়েড WebView-এ মাল্টিপল উইন্ডো সাপোর্ট সক্ষম করার পদ্ধতি

  3. CSS3 Multiple Backgrounds (একাধিক ব্যাকগ্রাউন্ড) যোগ করা

  4. MongoDB-এ একাধিক শর্তে অ্যারে আপডেট করার পদ্ধতি