【javascript】Q&Aの目次を自動で作ろう【jQuery不要】

NiiMoのWeb担当、たくまです。入社報告以来の2度めのブログですが、こんなの書くの初めてなので、書き始めてから5日くらい経ってしまいました…

で、この度 NiiMo のサイトに「表札のお悩み Q & A」というページを公開しました。木製の表札を検討しているけど、どのくらいが寿命なの?どんなメンテナンスをすればいいの?といったお客様の疑問に答えるQ&Aです。

Q&Aは定義リスト <dl>, <dt>, <dd> タグで構成するのが定石のようで、今回はそれに倣って作成しています。我々が公開したQAページは、javascript くんが勝手に目次を作ってくれる!というものです。

Webブラウザが実行する javascript にできることはいろいろありますが、HTMLの内容を操作する用途で最も利用されているのではないでしょうか。つまり javascript くんなら、HTMLの内容を解析して自動で目次だって作れる、という理屈になります。

「先頭に目次を置きたい」そう思ったとき、別々のIDを作ってページ内リンクを何十個も手作業で貼りたいですか?載せる質問が増えるたびに?いやいやシンプルにめんどくさいです。楽がしたいですよね。

というわけで、こちらの記事を参考に、アレンジさせていただきました。今回はそのスクリプトを解説する記事です。20行にも満たないソースコードなので、ぜひ目を通してみてください。

ちゃんと理解したら、カスタマイズができます。あなたのQAサイトにはもちろん、参考元のようにブログ等の記事にも応用できるものです。あと私が推す最大の利点ですが、誰かが書いた javascript のコードが、少しわかるようになっているかもしれません。

想定している対象読者は、

  • HTMLとCSSでWebページのデザインはできるけど、プログラムのことはよくわからない
  • スクリプトは今までコピペで済ませてきた
  • javascript は何から勉強すればいいかわからない

ぐらいの方々です。そんなあなた、この記事でわかった気になることから始めませんか?

結局なにができたの?

出来上がったものを先に載せてしまいます。

で、実際に動くサンプルがこちら。

See the Pen zmVNav by Takuma Sato (@takuma-nimo) on CodePen.

javascript 恐怖症のそこのあなたも大丈夫、ちゃんと(ざっくりですけど)解説していきますから。

CSSが無いので味気ないものですが、よくあるQAサイトはこんなHTML構成で作られています。

div の中には目次なんてないのに、Resultにはちゃんとできていますね。これは javascript くんが id=”toc” の中身を作ってくれたからです。

中身を細かく見ていく前に、「何をしているのか?」ざっくり説明しましょう。

何をしているのか?

ではまず手順を3行で言い表してみます。大丈夫、javascript のコードの意味はあとで説明します。まずは日本語での理解が、ソフトウェアを読み解く第一歩です。

  1. 質問リストをピックアップする
  2. 質問すべてに id属性を追加する
  3. id へのリンクを <div> の中に追加する(<ul><li><a>形式)

いかがですか?手順がイメージできたでしょうか。

ソフトウェアというものは、イメージから全てが始まります。設計もコーディングも、頭の中で何が起こるのかイメージできなければ成り立ちません。このイメージが間違っていたり足りない場合に、現れるのがバグです。

イメージできた人もできない人も、考えてみてください。あなたならこれっぽっちのHTMLから目次を作るとき、どんな手順を踏むでしょうか。おそらく、こんなことを考えるんじゃないでしょうか。

  • 目次はファイル内リンクだから、各<dt>に id 属性が無いといけないな。でもHTMLには id なんて付いてない……付けなきゃ
  • 質問 と同じ数だけ <a> が必要になるな
  • id は一意でないといけなかったな。何を id にすればいいんだろう?質問文そのものでいいか。

javascript がHTMLを操作できることを知っているなら、これぐらいは思いつくかもしれません。もし手順を思いついたなら、javascript のことがわからなくてもプログラムの半分はできているということになります。

それでは次はソースコードを見ていきます。

ソースコード解説

ここからはあなたのイメージ力が勝負です。早速1行目を見てみます。

これはおまじないみたいなもので、ブラウザがHTMLを読み込んだら一度だけ 「…」を実行しますよ、という意味です。

あとはこの中に、上記で挙げた手順があるだけなのです。

質問リストをピックアップする

これは2行目で完結しています。要は、目次にしたい箇所をCSSセレクタで指定しているだけです。

  • var は新しい変数の宣言です。
  • = は代入(右の内容を左に入れる)です。
  • document.querySelectorAll() は、 document(HTML全体)の中からCSSセレクタに一致するものを全て選んでいます。

HTMLを見てください。CSSセレクタ dl.qa-list>dt に一致する dt が3つありますね。つまり、 dt_list という新しい変数に<dt>が3つ入りました。図にするとこうです。

質問すべてに id属性を追加する

まず以下の .forEach で、dt_list の中身の数だけ「…」を繰り返します。(繰り返しのことをループと言ったりもします。)

dt_list は中身が3つなので、「…」が3回繰り返されますね。繰り返されるたびに、dt_obj と num は変わっていきます。(dt_obj と num の名前はここで好きに決めることができます。)

dt_obj は「質問1の<dt>」「質問2の<dt>」「質問3の<dt>」という順番に切り替わり、 num は数字の「0」「1」「2」となります。

今、初回ループ実行中という前提で9行目を見てみます。

  • 「+」は足し算の他に、文字の連結という意味があります。num は数字ですが + の左側が文字なので、文字の連結です。
  • javascript では、 ○○.id でその id 属性にアクセスできます。

初回ループなので dt_obj は「質問1の<dt>」、 num は「0」です。つまり「質問1<dt>の id 属性に “qa-entry-0” という文字を入れる」という意味になります。

同様に、2回目3回目のループで質問2と3には “qa-entry-1” “qa-entry-2” という id が付くことになります。

(ちなみに参考元の記事では「#日本語」というふうに日本語の id名を使っていましたが、私にはブラウザが想定外の解釈をする不安があったので連番で id を作っています)

id へのリンクを <div> の中に追加する

とうとう手順の最後まで来ました。今回は目次を次の形式で作りたいと思います。

さて、この「リンクテキスト123」をどうするか決めていませんでした。ここには質問文をそのまま持ってもってきたいですよね。

QAの目次ですから、「〇〇は××ですか?」というリンクテキストをクリックさせたいのです。

そこまで考えついたら、ループの中でやるべきことは以下の3つになります。

  • こんな<a>を新しく作る
    • リンクテキストは質問文
    • リンク先は <dt> の id
  • <a> が中に入った <li> を新しく作る

これで、ループが終わった時に <li> が3つ出来上がりそうです。

<a> と <li> を新しく作る

ループ先頭にある下記の2行が、一時的な a タグと li タグを生成している部分です。

注意したいのは、これらはこの段階ではHTMLのどこにも存在しないという点です。javascript くんが勝手に持っているだけです。じゃあいつHTMLに反映されるのかと言うと、a や li を実在する div などの要素に紐づけた時です。

<a> の中身を作っていく

では、まずこの a にリンクテキストを入れてみましょう。今までと同様に、初回ループという前提です。

  • .textContent は、タグの中身を文字として評価したものです

初回ループなので dt_obj は「質問1の<dt>」です。したがって、この1行は「<dt></dt>で挟まれた『? 質問1』という文字を <a></a>の間に代入する」ということをしています。

次に a にリンクを設定していきます。ここまで読んだあなたには、何をしているか想像つくと思います。これで目的の <a> タグができましたね。

  • javascript では 〇〇.href でアンカータグの href 属性にアクセスできます。

<li> の中身を作っていく

今度は <li> に中身を作ります。とは言っても、<li></li> に挟まれるのは先程作った <a> だけですから、次の一行で完了します。

  • ○○.appendChild(x) で、x を○○の子要素に追加することができます。

<ul> を作っていく

忘れるところでした。さっきの li は ul の子要素にしないといけませんね。注意しないといけないのは、 ul ひとつの中に li を3つ入れるという点です。

では試しにこの点を気にせず、 a や li と同じようにループの中で ul を宣言してみます。一部、実際のコードではなくコメントで記述を省略しています。

これだと、このように<ul>が3つ出来てしまいます。

なにこれ思ってたのと違う…本当にやりたいのはこうです。

どうすればいいのでしょうか?そもそも3回繰り返されるループの中で新しい ul を作っているので、3つできるのはあたりまえです。でも、ループの中で ul に li を入れないといけない…答えはこうです。

これで、3つの li がひとつの ul の子要素になりますね。最後にこの ul を、目次となる <div> の子要素にしたら完成です。この<div>は実体があるので、ここで初めて ul や li や a が見た目として追加されます。

  • document.getElementById(‘○○’) で、〇〇という id を持ったタグにアクセスできます。
  • 余談ですが、1行でこう書くこともできます。 document.getElementById('toc').appendChild(ul);

最後に

ではここまで読んだあなたへ。最初のソースコードをコメント付きでもう一度見てみましょう。

どうでしょう。何が起こるか、イメージできていますか?イメージできたあなたは、このコードを応用していろんな目次を自動で作ることができるようになっているはずです。

今回は<dt>へのサイト内リンクを作りました。<dt>が増えれば、それに応じて目次の項目も増えます。でも別に<h2>でも良いし、<img>だって良いことに気づくはずです。

長くなりましたが、今回はこのへんで。Web担当者やデザイナーさん、javascript アレルギーの方の助けに、少しでもなれば幸いです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です