2023年6月28日水曜日

【Blogger】ラベルを階層化する


階層構造に、改装!w こんにちは、あすらとです。

Bloggerでは記事に対してラベルを付与できますが、このラベルの機能はあまりリッチではありません。そこで、当BlogではBloggerのブログアーカイブのHTML構造を参考にし、比較的自然なラベルの階層化機能を自前実装しました。本日はこのラベル階層化の手順について解説します。

先に完成後のスクリーンショットを以下に載せます(まあPCなら横に表示されているとは思いますが念の為)。



ご自身のブログに同様の機能を実装したいと感じた方は是非最後までご覧ください。



まずBloggerはラベルの階層化機能を提供しておりませんので、HTMLを自分で編集する必要があります。「テーマ」→「カスタマイズの横の下三角」→「HTMLを編集」とクリックしHTMLの編集画面を開きます。


続いて、編集画面で「id='Label1'」で検索し、ラベルの機能を探します。(検索にない場合は元の画面に戻り、「レイアウト」からラベルガジェットを追加してください。)おそらく以下のように表示されていると思います。



最後に<b:widget...から次の</b:widget>まで以下のコードに差し替えて終了です。


  <b:widget id='Label1' locked='false' title='カテゴリ別' type='Label' version='1'>
    <b:widget-settings>
      <b:widget-setting name='sorting'>ALPHA</b:widget-setting>
      <b:widget-setting name='display'>LIST</b:widget-setting>
      <b:widget-setting name='selectedLabelsList'/>
      <b:widget-setting name='showType'>ALL</b:widget-setting>
      <b:widget-setting name='showFreqNumbers'>false</b:widget-setting>
    </b:widget-settings>
    <b:includable id='main'>
  <b:if cond='data:title != &quot;&quot;'>
    <h2><data:title/></h2>
  </b:if>
  <div expr:class='&quot;widget-content &quot; + data:display + &quot;-label-widget-content&quot;'>
    <ul class='hierarchy' id='rootNode'>
      <b:loop values='data:labels' var='label'>
        <li class='labelNode archivedate collapsed' expr:name='data:label.name'>
          <b:include name='myToggle'/>
          <a class='post-count-link' expr:href='data:label.url'>
            <data:label.name/>
          </a>
          <span class='post-count' dir='ltr'>(<data:label.count/>)</span>
          <ul class='hierarchy' style='padding: 10px 0 0 10px'/>
        </li>
      </b:loop>
    </ul>
    <ul class='hierarchy' id='templateElement' style='display:none;'>
      <li class='labelNode archivedate collapsed'>
        <b:include name='myToggle'/>
        <a class='post-count-link' href='javascript:void(0)'>LABEL_NAME</a>
        <ul class='hierarchy'/>
      </li>
    </ul>
    <b:include name='quickedit'/>
  </div>
  <style type='text/css'>
li.collapsed &gt; ul.hierarchy {
  display: none;
}
  </style>
  <script type='text/javascript'>
// 深さ優先探索でHTMLタグを形成する関数
function dfs(ul, obj) {
  for (const label of Object.keys(obj)) {
    let {node, children} = obj[label]

    if (!node) {
      const template = document.querySelector(&#39;#templateElement&#39;).innerHTML.replace(&#39;LABEL_NAME&#39;, label)
      node = new DOMParser().parseFromString(template, &#39;text/xml&#39;)
    }
    node.firstElementChild.addEventListener(&#39;click&#39;, () =&gt; onToggled(node.firstElementChild))
    console.log(ul)
    console.log(node)
    ul.appendChild(node)

    if (Object.keys(children).length &gt; 0) {
      dfs(node.lastElementChild, children)
      continue
    }

    node.removeChild(node.lastElementChild)
    node.removeChild(node.firstElementChild)
  }
}
// トグルがクリックされた時の関数
function onToggled(element) {
  element.parentElement.classList.toggle(&#39;collapsed&#39;)
  element.parentElement.classList.toggle(&#39;expanded&#39;)
  element.firstElementChild.classList.toggle(&#39;toggle-open&#39;)
  if (element.parentElement.classList.contains(&#39;expanded&#39;)) {
    element.firstElementChild.innerHTML = &#39;&#9660;&#160;&#39;
  } else { // 縮小する
    element.firstElementChild.innerHTML = &#39;&#9658;&#160;&#39;
  }
}
function main() {
  const treeObj = {} // 木構造を有するデータ
  document.querySelectorAll(&#39;li.labelNode[name]&#39;).forEach(node =&gt; {
    node.parentElement.removeChild(node) // 一旦DOMから消えてもらう

    // ラベルの名前をツリー状になるように登録する
    let obj = treeObj
    node.getAttribute(&#39;name&#39;).split(&#39;_&#39;).forEach((label, i, a) =&gt; {
      if (label in obj === false) {
        obj[label] = {&#39;children&#39;: {}, &#39;node&#39;: null}
      }
      if (i === a.length - 1) {
        obj[label][&#39;node&#39;] = node
      } else {
        obj = obj[label][&#39;children&#39;]
      }
    })
  })
  dfs(document.querySelector(&#39;#rootNode&#39;), treeObj)
}
main()
  </script>
</b:includable>
    <b:includable id='myToggle' var='interval'>
  <a class='myToggle' href='javascript:void(0)'>
    <span class='zippy'>
      &#9658;&#160;
    </span>
  </a>
</b:includable>
  </b:widget>

保存は忘れずに!お疲れ様でした!