JavaScriptサンプルコードです。JavaScriptも参照のこと。
history.pushState()とpopstate()は、履歴に対して新しい項目を追加したり、その追加した項目を取り出すことのできる機能。
シングルページアプリケーションを実装している際に、たとえばタブの切り替えに応じて、履歴を追加・取り出ししたい時などに使う。
たとえば、タブがクリックされた時は、
function TabClick(tabname) {
history.pushState(tabname, null, "index.html");
ChangeTab(tabname);
}
function ChangeTab(tabname) {
document.getElementById('tab_top').style.display = 'none';
document.getElementById('tab_profile').style.display = 'none';
document.getElementById('tab_novel').style.display = 'none';
document.getElementById('tab_diary').style.display = 'none';
document.getElementById('tab_links').style.display = 'none';
document.getElementById('tab_intro').style.display = 'none';
document.getElementById(tabname).style.display = 'block';
document.getElementById(tabname + '_li').style.background = 'white';
if (tabname != 'tab_top') {
document.getElementById('tab_top_li').style.background = '#e3e3e3';
}
if (tabname != 'tab_profile') {
document.getElementById('tab_profile_li').style.background = '#e3e3e3';
}
if (tabname != 'tab_novel') {
document.getElementById('tab_novel_li').style.background = '#e3e3e3';
}
if (tabname != 'tab_diary') {
document.getElementById('tab_diary_li').style.background = '#e3e3e3';
}
if (tabname != 'tab_links') {
document.getElementById('tab_links_li').style.background = '#e3e3e3';
}
if (tabname != 'tab_intro') {
document.getElementById('tab_intro_li').style.background = '#e3e3e3';
}
}
のようにし、クリックされた項目を履歴に追加する。
popstateイベントは、戻るや進むボタンがクリックされた時に呼び出される。そのため、以下のようにすれば、戻るや進むボタンがクリックされた時にタブを移動できる。
window.addEventListener('popstate', e => {
if (e.state === null) {
return;
} else {
ChangeTab(e.state);
}
});
e.stateはhistory.pushState()で挿入した内容(tabname)が取り出される。自分で追加したのではない場合にはnullが返る。
これで、ページのロード時に指定のタブを表示するために、以下のようにする。
<script type="text/javascript">
ChangeTab('tab_top');
</script>
ただし、僕がこれを実装していた時、上のコードだとバグがある。e.stateがnullである時は、ページの最初に何もpushState()せずにページが表示された時点へと戻った時と、別のサイトからこのページに戻ってきた時を示すが(ここらへんのことは、ブラウザでデバッグしながら自分なりに分かったことであり、詳細まで分かっていないので、もし間違っていたら申し訳ない)、このような時に上のコードでは上手く履歴が戻ってくれない。
次のように、ロード時にChangeTab()ではなくTabClick()を実行すると、この問題は解決する。
<script type="text/javascript">
TabClick('tab_top');
</script>
だが、この場合、ページのロード時にpushState()を行うため、別のページから戻ってきた時に進むの履歴が消えてしまう。また、最初のページのロード時に必要のない履歴がひとつ加わってしまう。
僕はいろいろと試した結果、結局このバグを直すことができなかったため、このシングルページアプリケーションを実装するのは諦めた。
ちなみに、HTMLは以下のようになる。
<div class="tab_menu">
<ul class="tab_list">
<li id="tab_top_li"><a href="#tab_top" class="tab_top" onclick="TabClick('tab_top'); return false;">トップ</a></li>
<li id="tab_profile_li"><a href="#tab_profile" class="tab_profile" onclick="TabClick('tab_profile'); return false;">自己紹介</a></li>
<li id="tab_novel_li"><a href="#tab_novel" class="tab_novel" onclick="TabClick('tab_novel'); return false;">作品</a></li>
<li id="tab_diary_li"><a href="#tab_diary" class="tab_diary" onclick="TabClick('tab_diary'); return false;">日記</a></li>
<li id="tab_links_li"><a href="#tab_links" class="tab_links" onclick="TabClick('tab_links'); return false;">世界観</a></li>
<li id="tab_intro_li"><a href="#tab_intro" class="tab_intro" onclick="TabClick('tab_intro'); return false;">略歴</a></li>
</ul>
</div>
<div class="box">
<div id="tab_top">
<h3>トップ</h3>
<p>(ここにトップページを記述する)</p>
</div>
<div id="tab_profile">
<h3>自己紹介</h3>
<p>(ここに自己紹介を記述する)</p>
</div>
<div id="tab_novel">
<h3>作品</h3>
<p>(ここに作品を記述する)</p>
</div>
<div id="tab_diary">
<h3>日記</h3>
<p>(ここに日記を記述する)</p>
</div>
<div id="tab_links">
<h3>世界観</h3>
<p>(ここに世界観を記述する)</p>
</div>
<div id="tab_intro">
<h3>略歴</h3>
<p>(ここに略歴を記述する)</p>
</div>
</div>
また、CSSは以下のようになる。
.tab_menu { }
.tab_list {
margin: 0;
padding: 0;
}
.tab_list li {
list-style-type:none;
list-style-image:none;
display: inline-block;
margin: 0;
margin-top: 5px;
margin-right: 5px;
padding: 3px 5px 3px 5px;
width: auto;
border-top: 2px solid #aaaaaa;
border-right: 1px solid #cccccc;
border-left: 1px solid #cccccc;
background: #e3e3e3;
}
.tab_list A:link,
.tab_list A:visited {
text-decoration: none;
font-weight: bold;
}
#tab_top_li {
background: white;
}
.box {
border-top: 1px solid #cccccc;
}
.box h3 {
font-size: 120%;
background: #eeeeee;
border: 1px solid #cccccc;
color: #444444;
padding: 5px 10px 5px 10px;
margin: 20px 0 20px 0;
}
後日注記:このようにタブごとにページを切り替えるページを作る時は、シングルページアプリケーションにするのではなく、きちんとページをタブごとに分けましょう。それがもっとも素直で正しい解決方法です。また、シングルページアプリケーションにする際には、そもそもタブごとに切り替えるような構造のサイトにしないことです。Ajaxを用いたサービスは、普通戻るや進むボタンは使いません。
以下はヒストリーAPIの参考文献。
タブ切り替えのコードは以下が参考になります。
以下はJavaScriptで僕が書いたFizzBuzz。
for (var i = 1; i <= 100; i++) {
if (i % 15 == 0) {
process.stdout.write('FizzBuzz, ');
} else if (i % 3 == 0) {
process.stdout.write('Fizz, ');
} else if (i % 5 == 0) {
process.stdout.write('Buzz, ');
} else {
process.stdout.write(i + ', ');
}
}
process.stdout.write('終了.\n');
実行結果:
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49, Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, FizzBuzz, 61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz, 71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79, Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89, FizzBuzz, 91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz, 終了.
JavaScriptにおいて、改行なしで画面に出力するにはprocess.stdout.write()を使う。
FizzBuzzの詳細についてはC言語サンプルコードを参照のこと。
2023.05.17