125naroom / デザインするところ(会社)です。

【jQuery】プラグインを使わずにモーダルを作ってみる

【jQuery】プラグインを使わずにモーダルを作ってみる
きのこさん
プラグインを使わずにモーダルってできますか?
きのこさん
ふむふむ、やってみましょうー

1.プラグインを使わないモーダルのサンプル

スマホでも問題ない。
IEでも問題ない。
完成ですー。

See the Pen
【jQuery】プラグインを使わずにモーダルを作ってみる
by 125naroom (@125naroom)
on CodePen.

コードはこちら

HTML

<div class="s_section gutter">
  <a href="#modal-a" data-modal-org-target="#modal-a" class="_a">モーダルへ</a>
</div>

<div class="a-modal-org" role="dialog" id="modal-a">
  <div class="a-modal-org__inner">
    <div class="a-modal-org__inner2">
      <div class="a-modal-org__inner3">
        <div class="a-modal-org__body">
          <div class="a-modal-org__scroll gutter">
            ここに内容
          </div>
        </div>
      </div>
      <div class="a-modal-org__close"><a class="a-modal-org__btn" href="#"><span class="a-modal-org__btn-inner"></span><span class="a-display-off">閉じる</span></a></div>
    </div>
  </div>
</div>
<div class="a-modal-org-overlay" tabindex="0"></div>

CSS

/*=========
project - modal-org
=========*/

.a-modal-org {
	display: none;
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	z-index: 190002;
}
.a-modal-org__inner {
	display: flex;
	align-items: center;
	margin: 0 auto;
	height: 100%;
}
@media print, screen and (min-width: 737px) {
	.a-modal-org__inner {
		width: 80vw;
		max-width: 900px;
	}
}
@media screen and (max-width: 736px) {
	.a-modal-org__inner {
		width: 85vw;
	}
}
.a-modal-org__inner2 {
	position: relative;
	width: 100%;
}
.a-modal-org__inner3 {
	background-color: #ffffff;
}
@media print, screen and (min-width: 769px) {
	.a-modal-org__inner3 {
		max-height: 80vh;
		border-radius: 12px;
		border: 1.5rem solid #fff;
	}
}
@media print, screen and (max-width: 768px) {
	.a-modal-org__inner3 {
		max-height: 70vh;
		border-radius: 6px;
		border: 1.125rem solid #fff;
	}
}
.a-modal-org__body {
	position: relative;
	overflow: hidden;
	box-sizing: border-box;
}
.a-modal-org__scroll.is-scroll {
	overflow-y: scroll;
}
@media print, screen and (min-width: 769px) {
	.a-modal-org__scroll.is-scroll {
		padding-right: 0.5rem;
	}
}
.a-modal-org__close {
	position: absolute;
	top: 0;
	right: 0;
	transform: translate(50%, -50%);
}
.a-modal-org__btn {
	box-sizing: border-box;
	background-color: #333333;
	border-radius: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
}
.a-modal-org__btn-inner {
	display: inline-block;
	box-sizing: border-box;
	width: 20px;
	height: 20px;
	position: relative;
}
.a-modal-org__btn-inner::before, .a-modal-org__btn-inner::after {
	position: absolute;
	content: "";
	margin: auto;
	box-sizing: border-box;
	vertical-align: middle;
}
.a-modal-org__btn-inner::before {
	border-top: 2px solid #ffffff;
	width: 20px;
	height: 0;
	top: 0;
	bottom: 0;
	left: 0;
	transform: rotate(45deg);
}
.a-modal-org__btn-inner::after {
	border-left: 2px solid #ffffff;
	width: 0;
	height: 20px;
	top: 0;
	bottom: 0;
	left: 9px;
	transform: rotate(45deg);
}

/*=========
utility - display-off
=========*/

.a-display-off {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	border: 0;
	overflow: hidden;
	padding: 0;
	clip: rect(0, 0, 0, 0);
}

/*=========
layout - a-modal-org-overlay
=========*/

.a-modal-org-overlay {
	display: none;
	z-index: 190001;
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100vh;
	background-color: rgba(0, 0, 0, 0.7);
}

jQuery

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
$(function(){

	// ============
	// a処理を一旦リセット
	// ============
	$('a[data-modal-org-target]').off('click');

	// ============
	// U a-modal-org
	// ============
	if($('.a-modal-org').length){
		var overlay = $('.a-modal-org-overlay');

		$('[data-modal-org-target]').each(function (){
			var modalTarget = $($(this).attr('data-modal-org-target'));
			var modalTargetinner3 = modalTarget.find('.a-modal-org__inner3');
			var modalTargetHead = modalTarget.find('.a-modal-org__head');
			var modalTargetBody = modalTarget.find('.a-modal-org__body');
			var modalTargetFoot = modalTarget.find('.a-modal-org__foot');
			var modalTargetScroll = modalTarget.find('.a-modal-org__scroll');
			var modalTargetBtn = modalTarget.find('.a-modal-org__btn');

			if(modalTarget){

				$(this).on('click',function(){
					modal_org_open();
					if(overlay){
						overlay.on('click focusin',function(){
							modal_org_close();
							return false;
						});
					}
					return false;
				});

				modalTarget.on('click',function(){
					modal_org_close();
				});
				modalTargetinner3.on('click',function(){
					event.stopPropagation();
				});
				modalTargetBtn.on('click',function(){
					modal_org_close();
					return false;
				});

			}

			// モーダルの高さ監視
			function modal_org_height(){
				if (modalTarget.is(':visible')) {
					modalTargetBody.css("max-height", "none");
					modalTargetScroll.removeClass('is-scroll').css("max-height", "none");
					var outerHeight = modalTargetinner3.innerHeight();
					if(modalTargetHead.length) outerHeight = outerHeight - modalTargetHead.outerHeight();
					if(modalTargetFoot.length) outerHeight = outerHeight - modalTargetFoot.outerHeight();
					modalTargetBody.css("max-height", outerHeight);
					var scrollHeight = Math.floor(modalTargetScroll.outerHeight());
					if(outerHeight < scrollHeight){
						modalTargetScroll.addClass('is-scroll').css("max-height", outerHeight);
					}
				}
			}
			$(window).on('resize',function(){
				modal_org_height();
			});

			function modal_org_open(){
				scroll_lock();
				// モーダルチェック
				modalTarget.css('opacity','0').show();
				modalTargetScroll.scrollTop(0);
				modal_org_height();
				modalTarget.hide().css('opacity','1');
				// モーダル表示
				modalTarget.fadeIn(600, function(){
					$(this).attr('aria-expanded', 'true');
				});
				modalTarget.attr('tabindex',1).focus().attr('tabindex','');
				// if(overlay) overlay.fadeIn(200);
				if(overlay) a_modal_org_overlay(true, 200);			// a-modal-org-overlay用
			}
			function modal_org_close(){
				scroll_lock_off();
				modalTarget.attr('aria-expanded', 'false').fadeOut();
				// if(overlay) overlay.off().fadeOut();
				if(overlay) a_modal_org_overlay(false, 'fast');			// a-modal-org-overlay用
			}

		});
	}

	// ============
	// U scroll_lock
	// ============
	var uA = navigator.userAgent;
	var os = osCh();								//iOS,ipadOS,Android,Mac,Win
	function osCh(){
		if (uA.indexOf('iPhone') > -1 || uA.indexOf('iPod') > -1 || uA.indexOf('iPad') > -1) return "iOS";
		if (uA.indexOf('Android') > -1) return "Android";
		if (uA.indexOf("Mac") != -1 && !('ontouchend' in document)) return "Mac";
		if (uA.indexOf("Mac") != -1 && 'ontouchend' in document) return "ipadOS";			// safari only
		if (uA.indexOf("Win") != -1) return "Win";
		return "unknown";
	}
	var scrollPosition;
	function scroll_lock(){
		if( os == "iOS" || os == "ipadOS" ){
			scrollPosition = $(window).scrollTop();
			$('body').addClass('is-scroll-lock').css({'top': -scrollPosition});
		}else{
			var clientWidth = $(window).innerWidth();
			var noScrollBarWidth = $(window).outerWidth();
			$('body').css({'overflow': 'hidden'});
			diff = noScrollBarWidth - clientWidth;
			if(diff > 0){
				$('body').css({'padding-right': diff + 'px'});
				// if(headerFixedItem && $('[data-header-fixed="true"]').length) headerFixedItem.css({'padding-right': diff + 'px'});
			};
		}
	}
	function scroll_lock_off(){
		if( os == "iOS" || os == "ipadOS" ){
			$('body').removeClass('is-scroll-lock').css({'top': 0});
			window.scrollTo( 0 , scrollPosition );
		}else{
			$('body').css({'overflow': 'auto'});
			$('body').css({'padding-right': '0'});
			// if(headerFixedItem) headerFixedItem.css({'padding-right': 0});
		}
	}

	// ============
	// U a-modal-org-overlay
	// ============
	function a_modal_org_overlay(flag,speed){
		var overlay = $('.a-modal-org-overlay');
		if(overlay.length){
			if(flag){
				overlay.stop().fadeIn(speed);
			}else{
				overlay.stop().off().fadeOut(speed);
			}
		}
	}

});

サンプルページはこちら

2.プラグインを使わないモーダルのサンプル(同じページに複数設置)

『data-modal-org-target』と『id』の紐付けさえ間違えなければ簡単ですねー。

See the Pen
【jQuery】プラグインを使わずにモーダルを作ってみる、複数も可能
by 125naroom (@125naroom)
on CodePen.

コードはこちら

HTML

<div class="s_section gutter">
  <a href="#modal-a" data-modal-org-target="#modal-a" class="_a">モーダルAへ</a>
  <a href="#modal-b" data-modal-org-target="#modal-b" class="_a">モーダルBへ</a>
  <a href="#modal-c" data-modal-org-target="#modal-c" class="_a">モーダルCへ</a>
</div>

<div class="a-modal-org" role="dialog" id="modal-a">
  <div class="a-modal-org__inner">
    <div class="a-modal-org__inner2">
      <div class="a-modal-org__inner3">
        <div class="a-modal-org__body">
          <div class="a-modal-org__scroll">
            モーダルAの内容
          </div>
        </div>
      </div>
      <div class="a-modal-org__close"><a class="a-modal-org__btn" href="#"><span class="a-modal-org__btn-inner"></span><span class="a-display-off">閉じる</span></a></div>
    </div>
  </div>
</div>

<div class="a-modal-org" role="dialog" id="modal-b">
  <div class="a-modal-org__inner">
    <div class="a-modal-org__inner2">
      <div class="a-modal-org__inner3">
        <div class="a-modal-org__body">
          <div class="a-modal-org__scroll">
            モーダルBの内容
          </div>
        </div>
      </div>
      <div class="a-modal-org__close"><a class="a-modal-org__btn" href="#"><span class="a-modal-org__btn-inner"></span><span class="a-display-off">閉じる</span></a></div>
    </div>
  </div>
</div>

<div class="a-modal-org" role="dialog" id="modal-c">
  <div class="a-modal-org__inner">
    <div class="a-modal-org__inner2">
      <div class="a-modal-org__inner3">
        <div class="a-modal-org__body">
          <div class="a-modal-org__scroll">
            モーダルCの内容
          </div>
        </div>
      </div>
      <div class="a-modal-org__close"><a class="a-modal-org__btn" href="#"><span class="a-modal-org__btn-inner"></span><span class="a-display-off">閉じる</span></a></div>
    </div>
  </div>
</div>
<div class="a-modal-org-overlay" tabindex="0"></div>

CSS

/*=========
project - modal-org
=========*/

.a-modal-org {
	display: none;
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	z-index: 190002;
}
.a-modal-org__inner {
	display: flex;
	align-items: center;
	margin: 0 auto;
	height: 100%;
}
@media print, screen and (min-width: 737px) {
	.a-modal-org__inner {
		width: 80vw;
		max-width: 900px;
	}
}
@media screen and (max-width: 736px) {
	.a-modal-org__inner {
		width: 85vw;
	}
}
.a-modal-org__inner2 {
	position: relative;
	width: 100%;
}
.a-modal-org__inner3 {
	background-color: #ffffff;
}
@media print, screen and (min-width: 769px) {
	.a-modal-org__inner3 {
		max-height: 80vh;
		border-radius: 12px;
		border: 1.5rem solid #fff;
	}
}
@media print, screen and (max-width: 768px) {
	.a-modal-org__inner3 {
		max-height: 70vh;
		border-radius: 6px;
		border: 1.125rem solid #fff;
	}
}
.a-modal-org__body {
	position: relative;
	overflow: hidden;
	box-sizing: border-box;
}
.a-modal-org__scroll.is-scroll {
	overflow-y: scroll;
}
@media print, screen and (min-width: 769px) {
	.a-modal-org__scroll.is-scroll {
		padding-right: 0.5rem;
	}
}
.a-modal-org__close {
	position: absolute;
	top: 0;
	right: 0;
	transform: translate(50%, -50%);
}
.a-modal-org__btn {
	box-sizing: border-box;
	background-color: #333333;
	border-radius: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
}
.a-modal-org__btn-inner {
	display: inline-block;
	box-sizing: border-box;
	width: 20px;
	height: 20px;
	position: relative;
}
.a-modal-org__btn-inner::before, .a-modal-org__btn-inner::after {
	position: absolute;
	content: "";
	margin: auto;
	box-sizing: border-box;
	vertical-align: middle;
}
.a-modal-org__btn-inner::before {
	border-top: 2px solid #ffffff;
	width: 20px;
	height: 0;
	top: 0;
	bottom: 0;
	left: 0;
	transform: rotate(45deg);
}
.a-modal-org__btn-inner::after {
	border-left: 2px solid #ffffff;
	width: 0;
	height: 20px;
	top: 0;
	bottom: 0;
	left: 9px;
	transform: rotate(45deg);
}

/*=========
utility - display-off
=========*/

.a-display-off {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	border: 0;
	overflow: hidden;
	padding: 0;
	clip: rect(0, 0, 0, 0);
}

/*=========
layout - a-modal-org-overlay
=========*/

.a-modal-org-overlay {
	display: none;
	z-index: 190001;
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100vh;
	background-color: rgba(0, 0, 0, 0.7);
}

jQuery

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
$(function(){

	// ============
	// a処理を一旦リセット
	// ============
	$('a[data-modal-org-target]').off('click');

	// ============
	// U a-modal-org
	// ============
	if($('.a-modal-org').length){
		var overlay = $('.a-modal-org-overlay');

		$('[data-modal-org-target]').each(function (){
			var modalTarget = $($(this).attr('data-modal-org-target'));
			var modalTargetinner3 = modalTarget.find('.a-modal-org__inner3');
			var modalTargetHead = modalTarget.find('.a-modal-org__head');
			var modalTargetBody = modalTarget.find('.a-modal-org__body');
			var modalTargetFoot = modalTarget.find('.a-modal-org__foot');
			var modalTargetScroll = modalTarget.find('.a-modal-org__scroll');
			var modalTargetBtn = modalTarget.find('.a-modal-org__btn');

			if(modalTarget){

				$(this).on('click',function(){
					modal_org_open();
					if(overlay){
						overlay.on('click focusin',function(){
							modal_org_close();
							return false;
						});
					}
					return false;
				});

				modalTarget.on('click',function(){
					modal_org_close();
				});
				modalTargetinner3.on('click',function(){
					event.stopPropagation();
				});
				modalTargetBtn.on('click',function(){
					modal_org_close();
					return false;
				});

			}

			// モーダルの高さ監視
			function modal_org_height(){
				if (modalTarget.is(':visible')) {
					modalTargetBody.css("max-height", "none");
					modalTargetScroll.removeClass('is-scroll').css("max-height", "none");
					var outerHeight = modalTargetinner3.innerHeight();
					if(modalTargetHead.length) outerHeight = outerHeight - modalTargetHead.outerHeight();
					if(modalTargetFoot.length) outerHeight = outerHeight - modalTargetFoot.outerHeight();
					modalTargetBody.css("max-height", outerHeight);
					var scrollHeight = Math.floor(modalTargetScroll.outerHeight());
					if(outerHeight < scrollHeight){
						modalTargetScroll.addClass('is-scroll').css("max-height", outerHeight);
					}
				}
			}
			$(window).on('resize',function(){
				modal_org_height();
			});

			function modal_org_open(){
				scroll_lock();
				// モーダルチェック
				modalTarget.css('opacity','0').show();
				modalTargetScroll.scrollTop(0);
				modal_org_height();
				modalTarget.hide().css('opacity','1');
				// モーダル表示
				modalTarget.fadeIn(600, function(){
					$(this).attr('aria-expanded', 'true');
				});
				modalTarget.attr('tabindex',1).focus().attr('tabindex','');
				// if(overlay) overlay.fadeIn(200);
				if(overlay) a_modal_org_overlay(true, 200);			// a-modal-org-overlay用
			}
			function modal_org_close(){
				scroll_lock_off();
				modalTarget.attr('aria-expanded', 'false').fadeOut();
				// if(overlay) overlay.off().fadeOut();
				if(overlay) a_modal_org_overlay(false, 'fast');			// a-modal-org-overlay用
			}

		});
	}

	// ============
	// U scroll_lock
	// ============
	var uA = navigator.userAgent;
	var os = osCh();								//iOS,ipadOS,Android,Mac,Win
	function osCh(){
		if (uA.indexOf('iPhone') > -1 || uA.indexOf('iPod') > -1 || uA.indexOf('iPad') > -1) return "iOS";
		if (uA.indexOf('Android') > -1) return "Android";
		if (uA.indexOf("Mac") != -1 && !('ontouchend' in document)) return "Mac";
		if (uA.indexOf("Mac") != -1 && 'ontouchend' in document) return "ipadOS";			// safari only
		if (uA.indexOf("Win") != -1) return "Win";
		return "unknown";
	}
	var scrollPosition;
	function scroll_lock(){
		if( os == "iOS" || os == "ipadOS" ){
			scrollPosition = $(window).scrollTop();
			$('body').addClass('is-scroll-lock').css({'top': -scrollPosition});
		}else{
			var clientWidth = $(window).innerWidth();
			var noScrollBarWidth = $(window).outerWidth();
			$('body').css({'overflow': 'hidden'});
			diff = noScrollBarWidth - clientWidth;
			if(diff > 0){
				$('body').css({'padding-right': diff + 'px'});
				// if(headerFixedItem && $('[data-header-fixed="true"]').length) headerFixedItem.css({'padding-right': diff + 'px'});
			};
		}
	}
	function scroll_lock_off(){
		if( os == "iOS" || os == "ipadOS" ){
			$('body').removeClass('is-scroll-lock').css({'top': 0});
			window.scrollTo( 0 , scrollPosition );
		}else{
			$('body').css({'overflow': 'auto'});
			$('body').css({'padding-right': '0'});
			// if(headerFixedItem) headerFixedItem.css({'padding-right': 0});
		}
	}

	// ============
	// U a-modal-org-overlay
	// ============
	function a_modal_org_overlay(flag,speed){
		var overlay = $('.a-modal-org-overlay');
		if(overlay.length){
			if(flag){
				overlay.stop().fadeIn(speed);
			}else{
				overlay.stop().off().fadeOut(speed);
			}
		}
	}

});

サンプルページはこちら

メモ

jQueryのことで何かわからないことがあればjQueryの日本語リファレンスサイトがあるので一度チェックしてみるといろいろ解決できたりしますよ。

jQuery日本語リファレンス

jQuery(英語版)

さいごに

きのこさん
わー、これこれー!
きのこさん
デバイス対応が難しかったですねー
さ、おやつにしましょうー

関連記事

【jQuery】モーダルプラグイン『Remodal』の実装サンプル集

おすすめ

Googleさんのおすすめ

Googleさんのおすすめ

デザインの記事

Aubloom オーブルーム – RAWSILK SHAMPOO ローシルクシャンプー
ちょっと思い出しただけ
【Google Chrome】ショートカットキーまとめ(Windows or Mac)