JavaScriptの.querySelectorAll()
を使って、よくブログの記事の冒頭に設置される「目次」をネイティブなJavaScriptで生成して、それをモーダルの中に表示させてみます。
.querySelectorAll()
だけではないですが、そんなスニペットです。
jQueryのライブラリ依存もなく、カスタマイズも比較的簡単なので、順に解説していきます。
.querySelectorAll()
JavaScriptの .querySelectorAll()
は、CSS のセレクタ形式で条件を指定して要素を取得するメソッドです。
document.querySelectorAll(ここにCSSのセレクタ)
classや idといった簡単なものから、タグとclassを組み合わせた複雑なものまで指定することができる、便利なメソッドです。
使い勝手の良いメソッドです。
目次が表示されるモーダルのサンプル
早速サンプルです。
以下のボタンをクリックすると、モーダルが現れその中に目次が表示されます。
目次はこのページの h
タグを取得していて、各目次・モーダル外・バツのいずれかをクリックすることで、モーダルが閉じます。
目次の装飾は全てCSSです。
サンプルのコード
HTMLは、大きく分けて「モーダルを表示させるボタン」と「モーダルの中身」の2つに分かれます。目次は、JavaScriptで出力する <ul id="tocBlock"></ul>
のタグをモーダルの中に記述します。
<div class="container">
<button class="modal-toggle btn-example" data-modal="modalOne">目次のモーダル</button>
</div>
<!-- modalInner -->
<div id="modalOne" class="modal">
<div class="modal-content">
<div class="modal-top">
<span class="modal-close">×</span>
</div>
<div class="modal-container">
<ul id="tocBlock"></ul>
</div>
</div>
</div>
<!-- modalInner -->
ちなみに、buttonタグじゃなく他のタグに class="modal-toggle" data-modal="modalOne"
を付けてもモーダルが発火するので、汎用性は高いです。
JavaScriptの全体感で言うと、上から順に「モーダル」「目次」「目次クリック時の動作」の3つに分かれます。
//modal
const modalBtns = document.querySelectorAll(".modal-toggle");
modalBtns.forEach(function (btn) {
btn.onclick = function () {
var modal = btn.getAttribute('data-modal');
document.getElementById(modal).style.display = "block";
};
});
const closeBtns = document.querySelectorAll(".modal-close");
closeBtns.forEach(function (btn) {
btn.onclick = function () {
var modal = btn.closest('.modal');
modal.style.display = "none";
};
});
window.onclick = function (event) {
if (event.target.className === "modal") {
event.target.style.display = "none";
}
};
//toc
document.addEventListener('DOMContentLoaded', function() {
htmlTableOfContents();
} );
function htmlTableOfContents( documentRef ) {
var documentRef = documentRef || document;
var toc = documentRef.getElementById("tocBlock");
var headings = [].slice.call(documentRef.body.querySelectorAll('.entry-content h1, .entry-content h2, .entry-content h3')); /* ここで目次で取得するタグを指定 */
headings.forEach(function (heading, index) {
var ref = "toc" + index;
if ( heading.hasAttribute( "id" ) )
ref = heading.getAttribute( "id" );
else
heading.setAttribute( "id", ref );
var link = documentRef.createElement( "a" );
link.setAttribute( "href", "#"+ ref );
link.classList.add('linkTarget');
link.textContent = heading.textContent;
link.setAttribute( "onclick", "displayno()" );
var div = documentRef.createElement( "li" );
div.setAttribute( "class", heading.tagName.toLowerCase() );
div.appendChild( link );
toc.appendChild( div );
});
}
try {
module.exports = htmlTableOfContents;
} catch (e) {
};
//event
function displayno() {
document.getElementById("modalOne").style.display = "none";
};
最後に、モーダル内にある目次をクリックした時にモーダルが非表示になるよう、クリックのイベントハンドラをHTMLに出力させて、その為の関数を記述しています。
CSS
CSSは、使用しているタグが多少多いのでそれに比例して多めです。
/* modal */
.modal {
display: none;
position: fixed;
z-index: 8887;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.7);
transition: all 1s ease-in-out;
}
.modal-content {
background: #FFF;
overflow-y: auto;
padding: 20px 25px;
width: 90%;
max-width: 500px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
max-height: 400px;
animation: show 0.6s linear 0s;
filter: drop-shadow(0px 2px 6px #777);
}
.modal-top {
display: inline-block;
position: absolute;
right: 5px;
top: 5px;
}
.modal-close {
color: #FF0000;
text-decoration: none;
font-size: 2rem;
line-height: 1;
padding: 0 8px;
}
.modal-close:hover, .modal-close:focus {
text-decoration: none;
cursor: pointer;
}
.modal-title {
color: #FFF;
}
@keyframes show{
from{
opacity: 0;
}
to{
opacity: 1;
}
}
/* toc */
ul#tocBlock {
border: none;
margin: 0;
list-style: none;
padding: 0;
counter-reset: li;
}
ul#tocBlock li {
padding: 0 0 0 32px;
font-size: 1.1rem;
margin: 8px 0;
line-height: 1.6;
position: relative;
}
ul#tocBlock li:first-child {
margin-top: 0;
}
ul#tocBlock li:last-child {
margin-bottom: 0;
}
ul#tocBlock li.h2:before {
position: absolute;
top: 50%;
left: 0;
font-weight: bold;
color: #f0db3f;
counter-increment: li;
content: counter(li);
background: #313131;
border-radius: 9999px;
transform: translateY(-50%);
width: 22px;
height: 22px;
display: flex;
align-items: center;
justify-content: center;
line-height: 22px;
font-size: 0.85rem;
}
li.h3:before {
position: relative;
content: '\f105';
display: inline-block;
width: 14px;
height: 28px;
line-height: 26px;
font-size: 20px;
color: #f0db3f;
vertical-align: middle;
margin-right: 7px;
font-family: "Font Awesome 5 Free";
font-weight: 900;
}
ul#tocBlock li a {
padding: 5px 0;
display: inline-block;
color: #313131;
transition: all .3s ease;
position: relative;
}
ul#tocBlock li a:after {
content: "";
width: 0;
height: 2px;
background: #e6d340;
bottom: 3px;
left: 0;
position: absolute;
transition: all .3s ease;
}
ul#tocBlock li a:hover:after {
width: 100%;
}
目次のデザインはこのページにあわせて作っているので、設置する場合はカスタマイズして使ってください。