JavaScriptを調べる準備
非エンジニアがコードをいきなり書きだすのは無理なので、JSにやってほしいことを整理する。
JSに自動でやってほしいこと
h2タグを目次にする前提とする。
- ページ内のh2を全て取得
- 取得したh2の数に応じて実行
- 目次にするli要素を生成
- 生成した目次のli要素に、h2から取得したテキストを追加する
- h2に重複しないid属性を追加
- 目次のli要素の子となるa要素を生成
- 生成したa要素のhref属性に、h2要素に追加したid属性を追加
- li要素の子要素にa要素を挿入
- ページ内のul要素に生成したli要素を挿入
必要なコードを調べる
今のところエンジニアを目指してはいないので、必要そうなものだけを調べる。
ページ内のh2を取得
document.querySelectorAll()を使う。
該当する要素を全て取得して、配列っぽいオブジェクトを返す。ここで注意なのは配列ではないということ。
console.logをみるとNodeListと出力される。
また、document.getElementsByTagName()を使っても取得できる。
こちらも配列っぽいオブジェクトを返すが、こちらはHTMLCollectionとなっている。
NodeListとHTMLCollectionの違い
ドキュメントを見ると、document.querySelectorAll()で取得したNodeListは静的 (ライブではない) 、HTMLCollectionは動的(生きたもの)とある。
取得したh2の数だけ繰り返す
ページ内のh2の取得方法によって少し変わる部分もある。
NodeListもHTMLCollectionも配列風のオブジェクトだが、「NodeListはArrayとは異なりますが、forEach() メソッドで処理を反復適用することは可能」とのこと。
HTMLCollectionの方はforEach() メソッドを使えない。
NodelistはforEach() が使えるが、IE11でサポートされていないので、使われることが少なかったかもしれない。今はIE11に配慮する必要がなくなったので、今は気にする必要はなさそう。
NodeListにもHTMLCollectionにもプロパティにlengthがあるので、数を取得してfor文で処理を繰り返せば配列風なのにどっちが配列のfor each。
またArray.prototype.forEach.call(obj, …)を使えば、どちらでも使え、IE11でも動作する。
目次のli要素の生成
document.createElement()を使って生成する。
h2にid属性を設定する
element.idかelement.setAttributeを使って、h2にidを設定する。
今回はelement.idを使った。
h2のテキストを取得して・目次のli要素に追加する
NodeのプロパティtextContentを使って取得・追加する。
混同しやすいプロパティにinnerTextやinnerHTMLがあるらしいので、それぞれのプロパティを見ておく。
また、そもそものHTMLのDOMを理解するために、The HTML DOM APIopen_in_newのプロパティやメソッドの継承などを見ながら調べた。親要素に子要素を挿入する
今回は繰り返し処理で順番に追加するので、末尾に追加するNode.appendChild()メソッドを使った。
繰り返し処理でリアルDOMに要素を1つずつ挿入すると、都度ブラウザの再フローが起きるので、いったんDocumentFragmentに挿入して、すべて完了したらDocumentFragmentをリアルDOMに挿入する。DocumentFragmentは、document.createDocumentFragment()メソッドを使って生成する。
コードを書く(サンプルコード紹介)
調べた内容で書いたサンプルコードです。
外部JSの場合はdefer属性を使って読み込むなどで、実行タイミングを調整してください。
おわりに
このブログでは目次を固定表示しているので、カレント機能やスクロール率の表示なども追加した。
まずやりたいことの整理ができれば、非エンジニアでも調べるのは簡単だった。
ドキュメントを読んでおくと、ちょっとずつできることが増えて応用がきくので、ソースをコピーするよりオススメです。