🍣

【jQuery無し】javascriptを使った複数モーダルビュー

2021/07/06に公開

昨今のフロントエンド界隈ではvueやReactなどのフレームワークを使った開発が盛んになっていますが、それでもなお「WordPress」を筆頭に従来のホームページ制作もまだまだ需要があるのが現状です。

この記事では素のjavascriptを使ったモーダルビューの実装を行います。素のjavascriptを使ったDOM操作を行うことは業務上減ってきているかと思いますので、どちらかというと初学者向けということになります。

早速つくる

※追記:2021.07.12
1ページ内に複数のジャンルのモーダルを設置しても機能するよう変更。
(例:ギャラリーのモーダルとスタッフ紹介のモーダルを1ページの中に共存させる)

サンプルは非常にシンプルにしてあります。スタイルなどは各々良い感じにしていただければと思います。
完成の挙動はこちらから。

htmlとCSSを用意

まずは以下のようにボタンと、モーダルで表示させる内容を用意します。

<section data-modal="modal1">
  <button class="modal-trigger" data-target="1">ボタン1</button>
  <button class="modal-trigger" data-target="2">ボタン2</button>
  <button class="modal-trigger" data-target="3">ボタン3</button>
  
  <!--これより下がモーダルで表示させるコンテンツ-->
 
  <div id="1" class="modal" data-content="modal1">
    <div class="close-btn">
      <span></span>
      <span></span>
    </div>
    <p>モーダル1</p>
  </div>
  
  <div id="2" class="modal" data-content="modal1">
    <div class="close-btn">
      <span></span>
      <span></span>
    </div>
    <p>モーダル2</p>
  </div>
  
  
  <div id="3" class="modal" data-content="modal1">
    <div class="close-btn">
      <span></span>
      <span></span>
    </div>
    <p>モーダル3</p>
  </div>
</section>

data属性が、今回モーダルを操作する上で重要になります。

  • data-modal
    全体をラップする(親要素)につけます。insertBefore()をするときに必要

  • data-target
    クリックしたものと表示させるコンテンツの対応に利用します。具体的には、クリックしたdata-target属性の値と同じid名のコンテンツを表示させています。

  • data-content
    insertBefore()するときの挿入場所です。insertBefore()は、親要素となるラッパーの(今回だとsectionタグ)中にある任意の要素を検索して、その検索した要素の前に新たな要素をインサートします。
    親要素が見分けられればなんでもいいのですが、idにしてしまうと1ページの中に1つしか使えなくなってしまいます。
    そこで、data属性を利用します。data属性であれば、値を変更して何個でも親要素を作成することができます。

.modal{
    visibility: hidden;
    opacity: 1;
    transition:all 500ms ease;
    position:relative;
    background:#000;
    color:#fff;
  }
.open-modal + .modal{
    visibility: visible;
    opacity: 1;
}

.close-btn{
     span{
       position:absolute;
       top:0px;
       right:100px;
        border-bottom: 2px #FFF solid;
        width: 40px;
        height: 1px;
        display: block;
        right:0px;
        top:16px;
        transform: rotate(45deg);
        &:first-child{
            transform: rotate(-45deg);
       }
  }
}

簡単に解説をすると、modal-triggerというクラス名のボタンを用意し、data属性を設定します。
modal-triggerをjavscriptで監視し、クリックイベントをトリガーにmodalを表示させます。
data属性は、どのボタンをクリックしたのかの判定に利用します。
data属性の値と、対応するモーダルコンテンツのid属性の値を同じにしておくところがポイントです。

js

あとはjsで操作します。
初めに言葉で説明すると、eventListenerでmodal-triggerを監視し、クリックイベントをトリガーとしてopen-modalというクラスをもった<span>タグを、表示させたいコンテンツの前に挿入します。
モーダルのトリガーとなるボタンやコンテンツは複数あるので、id属性は使えません。
クラスを付与することになるので、querySelectorAll()メソッドを使います。このメソッドは、取得した値をforeachで回せることがポイントです。
クリックしたボタンのdata属性と一致するidを持つコンテンツを表示させるようにします。

閉じる時も同じ原理です。
close-btnのクリックイベントをトリガーに、.open-modalクラスを外します。

var hideModal = function(c){
   c.forEach(function(target){
       target.addEventListener("click", function(){
           var closeModal = document.querySelector('.open-modal');
           closeModal.classList.remove('open-modal');
       });
   });
}

//modalを監視
var modalTrigger = function(c){
   c.forEach(function(target){
       target.addEventListener('click',function(event){
           //data属性でクリックした対象を判定
           var dataTarget = target.getAttribute("data-target");
           var content = document.getElementById(dataTarget);
           //モーダルの親ラッパー
           var parents = document.querySelectorAll('section[data-modal]');
           parents.forEach(function(p){
               //挿入するspanタグ
             console.log(p);
               if(p.dataset.modal == content.dataset.content){
                   var spn = document.createElement('span');
                   spn.className = "open-modal absolute"
                   p.insertBefore(spn, content);
               }
           });
       });
   })
}


window.addEventListener('DOMContentLoaded', function(){
   //modalを監視
   var modalContent = document.querySelectorAll(".modal-trigger");
   modalTrigger(modalContent);

   //modalを閉じる
   var closeBtn = document.querySelectorAll(".close-btn");
   hideModal(closeBtn);
})

jQueryやその他ライブラリを使えばもっと簡単ですが、DOM操作を慣れるにはちょうど良い機能だと思います。
アニメーションなどはCSSでつける方が簡単かと思います。

以上。

Discussion