文章の中に長いコードを書いた時とかに、そのコードをデフォルトでは表示しないようにしておいて、「ボタンをクリックしたらコードが表示されて、もう一回クリックしたらまた隠される」みたいな操作をできるようにしたい。
ググると、jQueryを使った方法がいくつか見つかったけど、ライブラリを使わずに作った方が融通が効く気がしたので、ライブラリを使わず作った。
実例
実装
コードは下記のような感じ。
<div>
<div style="text-decoration:underline" onclick="var style = this.parentNode.getElementsByTagName('div')[1].style; style.display=(style.display ==='block' ?'none':'block')">たいとる</div>
<div style="display:none">
<div>
ほげほげ
</div>
<div style="text-decoration:underline" onclick="this.parentNode.style.display='none'">閉じる</div>
</div>
</div>
「たいとる」のところにボタンとして表示される文字列を書いて、「ほげほげ」のところにコンテンツを書く。
隠したり表示したりするもの(「ほげほげ」にあたるもの)がソースコードの場合、<とか>とか"とかをそのまま書くと、HTMLのタグとして解釈されてしまってうまく表示されない。
下記のような方法を使えば、<とか>とか"とかを意識せずに済む。
ただし、${...}
という文字列を含めると、この箇所がうまく表示されない(これは、JavaScriptのテンプレート文字列を使っているため。後述する)。
<div>
<div style="text-decoration:underline" onclick="var style = this.parentNode.getElementsByTagName('div')[1].style; style.display=(style.display ==='block' ?'none':'block')">ここをクリックすると展開</div>
<div style="display:none">
<div>
<pre><code><script type="text/javascript">
var src =
`ここに、<とか>とか"が入った文字列を書くと、htmlタグと解釈されず、ちゃんとそのまま表示される。`;
var scripts=document.getElementsByTagName('script');
scripts[scripts.length-1].parentNode.appendChild(document.createTextNode(src));
</script></code></pre>
</div>
<div style="text-decoration:underline" onclick="this.parentNode.style.display='none'">閉じる</div>
</div>
</div>
実装の詳細: 隠したり表示したりする処理
参考にさせていただいたもの
下記のページを参考にさせていただいた。
http://k-hiura.cocolog-nifty.com/blog/2009/06/htmldiv-512d.html
このページの方法だと、id属性を使って要素を特定してる。
けど、id属性を使うと、1つのHTMLにボタンをいっぱい設置したい時に大変そう。
なので、クリックされた要素(this)からDOMツリーを辿る形で要素を特定する形に変更した。
動作の概要
DOMツリーを辿ることで要素を特定して、style属性をblockにしたりnoneしたりすることで要素を表示したり隠したりしてる。
DOMツリー
DOMツリーの構造は下記。
div
├ div # タイトル(例における、「ここをクリックすると展開」に相当)
└ div
├ div # コンテンツ
└ div # 「閉じる」
DOMツリーの辿り方
タイトルが書かれたdivタグと、「閉じる」のdivタグで、onclick属性を使って、クリックされた時の動作を指定してる。
タイトルが書かれたdivタグのonclick属性での処理
var style = this.parentNode.getElementsByTagName('div')[1].style;
style.display=(style.display ==='block' ?'none':'block');
1行目
クリックされたdivタグ(this)から、parentNodeで親に移動。
getElementsByTagName(‘div’)を使って、子孫のdivタグの配列を取得。
配列の1番目を取ることで、コンテンツと「閉じる」を囲むdivタグに移動(0番目はタイトルを囲むdiv)。
そのdivタグのstyle要素を取得しておく。
2行目
style属性のdisplayがblockならnoneを設定し、noneならblockを設定するようにする。
備考
thisからnextSiblingに移動する方法だとうまくいかなかった。
「閉じる」のdivタグのonclick属性での処理
this.parentNode.style.display='none';
「閉じる」の親(parentNode)が、コンテンツと「閉じる」を囲むdivタグなので、そこに移動。
「閉じる」ボタンは表示を隠すしかしないので、単にnoneに設定するだけ。
備考
最初、「閉じる」のdivタグはbuttonタグとかspanタグにしようと思ったけど、WordPressかテーマかが何かしているのか、buttonタグとかspanタグの親にpタグが挿入されて、うまくいかなかった。
便利だと思う使い方
辞書ツールに、下記のような感じで登録しておく。
読み: たいとるとぐる
<div><div style="text-decoration:underline" onclick="var style = this.parentNode.getElementsByTagName('div')[1].style; style.display=(style.display ==='block' ?'none':'block')">
読み: こんてんつとぐる
</div><div style="display:none"><div>
読み: えんどとぐる
</div><div style="text-decoration:underline" onclick="this.parentNode.style.display='none'">閉じる</div></div></div>
「たいとるとぐる」の変換で出てくる部分を入れて、タイトルを入力。
その後、「こんてんつとぐる」の変換で出てくる部分を入れて、コンテンツを入力。
最後に、「えんどとぐる」で変換して確定。
これで、「ボタンのクリックで、HTMLの一部を隠したり表示したりする」をHTML内に埋め込める。
実装の詳細: <とか>とか”とかを意識しないための処理
コード再掲。
<pre><code><script type="text/javascript">
var src =
`ここに、<とか>とか"が入ったコードを書いたら、そのまま表示される。`;
var scripts=document.getElementsByTagName('script');
scripts[scripts.length-1].parentNode.appendChild(document.createTextNode(src));
</script></code></pre>
preタグの下に、<とか>とか"とかが入ったコードをそのまま書くと、htmlのタグとみなされてしまう。
なので、JavaScriptの文字列として記述(`の中に記述)した上で、その文字列を(preタグの下の)codeタグに埋め込むようにする。
こうすると、<とか>とか"とかをそのまま書いても問題ない。
scriptタグ内でscriptタグへの参照を取得する方法は、下記のYahoo!知恵袋の記事で完璧に説明されてた。
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1189943841
JavaScriptで、バックティック文字(`)を使って表した文字列は、テンプレート文字列というやつ(今回これを作るために調べてて知った。。。)。
テンプレート文字列を使うと、文字列内での改行をそのまま記述できる。
けど、テンプレート文字列内では、${...}
が特別な意味を持ってしまうので、理想形を目指すなら、テンプレート文字列とは別の方法で、改行とかそのまま記述する方法(ヒアドキュメントの実現方法)を模索した方がいいかもしれない。。。
文字列は、createTextNodeを使ってテキストノードに変換した後、appendChildを使ってcodeタグの子として挿入してる。
追記:
最初、変数をvarでなくconstで宣言してたけど、constだとダメだった。
constを使うと、1つのhtmlに複数のコードを埋め込もうとした時、「すでにsrcという変数が存在しますよ」というエラーが出てしまって、2つ目以降が表示されなくなる。