想用弹窗效果,可以搜索modal、fancebox、shadowbox等关键词,可用的插件很多,功能也十分强大,只要按照它的文档来多半都能实现我们的需求。但它终究不那么自由,前几天想追踪一下用shadowbox展示的youtube视频,明明十分简单的事情却因为shadowbox变得复杂,于是想着能不能写个十分简单的类shadowbox效果?

选择弹窗,一定要注重移动端,这东西在移动端不那么友好。当弹窗内容较多时,有些脚本会使窗口超出屏幕,超出部分无法查看;有的则是在弹窗内部出现滚动条,勉强能看。这方面处理的比较好的是bootstrap modal——直接模拟浏览器的滚动条,所以决定写一个类似bootstrap modal的弹窗。

最终效果

文字少时与普通弹窗没区别

modal-small

内容多的话,可以出现一个浏览器滚动条,当然这个其实是div的滚动条 modal-large

下面来说实现的步骤:

  1. 创建HTML结构,即让你做如上图所示的样子,你会用怎样的markup
  2. 写样式,给结构增加css,变成上图的样子
  3. 写控制脚本,用jquery实现打开和关闭的功能

HTML结构

考虑到内容区域需要居中对齐,所以至少要有一个div来定位和显示背景,再用一个div居中内容,内容区域想分成header、body和footer。

<div class="rs-dialog" id="myModal1">
	<div class="rs-dialog-box">
		<a class="close" href="#">&times;</a>
		<div class="rs-dialog-header">
			<h3>标题</h3>
		</div>
		<div class="rs-dialog-body">
			<p>内容</p>		
		</div>
		<div class="rs-dialog-footer">
			<input type="button" class="close" value="Close" style="float:right">
		</div>
	</div>	
</div>

添加样式

透明背景用background和opacity来实现,也可以选择rgba,只是IE8及以下浏览器不支持。为了让一个position为fixed的div铺满整个窗口,可以将其top、right、left、bottom属性全部设为0。

当内容区域过长时,模拟浏览器纵向滚动条,方法是将body(或HTML)的overflow属性设为hidden,禁止浏览器真正的滚动条出现,然后给弹窗最外层的div设置overflow-y:auto,用这个div的滚动条模拟浏览器滚动条。

弹窗打开关闭时滚动效果用css3 transition来实现。

.dialog-open{
	overflow-y:hidden !important;
}
.rs-overlay{
	background:#000;
	opacity:.5;
	filter: alpha(opacity=50);
	position: fixed;
	z-index: 1000;
	top:0;
	bottom:0;
	left:0;
	right:0;
	display: none;
}
.rs-dialog{
	display: none;
	opacity: 0;
	overflow: hidden;
	position: fixed;
	top:0;
	bottom:0;
	left:0;
	right:0;	
	z-index: 1040;
	-webkit-overflow-scrolling: touch;
	outline: 0;
	/*background: rgba(0,0,0,.5);*/
	-webkit-transition: opacity .15s linear;
	-o-transition: opacity .15s linear;
	transition: opacity .15s linear;
}
.dialog-open .rs-dialog{
	overflow-x:hidden;
	overflow-y:auto;
}
.rs-dialog.in{
	opacity: 1;
}
.rs-dialog .rs-dialog-box {
  -webkit-transform: translate(0, -25%);
  -ms-transform: translate(0, -25%);
  -o-transform: translate(0, -25%);
  transform: translate(0, -25%);
  -webkit-transition: -webkit-transform 0.3s ease-out;
  -o-transition: -o-transform 0.3s ease-out;
  transition: transform 0.3s ease-out;
}
.rs-dialog.in .rs-dialog-box {
  -webkit-transform: translate(0, 0);
  -ms-transform: translate(0, 0);
  -o-transform: translate(0, 0);
  transform: translate(0, 0);
}
.rs-dialog .rs-dialog-box{
	position: relative;
	margin:30px auto;
	width: 600px;
	background-color: #ffffff;
	border-radius:10px;
	border: 1px solid #999999;
	border: 1px solid rgba(0, 0, 0, 0.2);
	-webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
	box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
}
.logged-in .rs-dialog .rs-dialog-box{
	margin-top:60px;
}
.rs-dialog-box a.close{
	position: absolute;
	top: -12px;
	right: -12px;
	width: 25px;
	height: 25px;
	padding: 0;
	line-height: 25px;
	font-size:20px;
	font-family:Arial,sans-serif;
	font-weight:bold;
	text-decoration:none;
	text-align:center;
	text-shadow: 0 1px 0 #ffffff;
	color: #fff;
	background-color:#8b8b8b;
	border:2px solid #fff;	
	border-radius: 25px;	
	box-shadow:0 0 3px 1px #999;
	outline: none;
}
.rs-dialog-box a.close:hover{
	background-color:#444;
}
.rs-dialog-header{
	padding: 20px;
	border-bottom: 1px solid #e5e5e5;
}
.rs-dialog-header h3{
	font-size: 18px;
}
.rs-dialog-body{
	padding: 20px;
	line-height: 1.4
}
.rs-dialog-body p{
	margin-bottom:10px;
}
.rs-dialog-footer{
	padding: 20px;
	border-top:1px solid #e5e5e5;
	overflow: hidden;
}

@media (max-width: 767px) {
  .rs-dialog .rs-dialog-box {
    width: auto;
    margin: 30px 20px;
  }
}

添加控制脚本

大部分用css实现,所以脚本通过简单的addClass和removeClass就可以控制开关。

还可以增加点击弹窗外部关闭窗口的功能。

jQuery(document).ready(function($){
	$('body').append('<div class="rs-overlay" />');
	$("a[rel='rs-dialog']").each(function(){
		var trigger 	= $(this);
		var rs_dialog 	= $('#' + trigger.data('target'));
		var rs_box 		= rs_dialog.find('.rs-dialog-box');
		var rs_close 	= rs_dialog.find('.close');
		var rs_overlay 	= $('.rs-overlay');
		if( !rs_dialog.length ) return true;
		
		// Open dialog
		trigger.click(function(){
			//Get the scrollbar width and avoid content being pushed
			var w1 = $(window).width();
			$('html').addClass('dialog-open');
			var w2 = $(window).width();
			c = w2-w1 + parseFloat($('body').css('padding-right'));
			if( c > 0 ) $('body').css('padding-right', c + 'px' );

			rs_overlay.fadeIn('fast');
			rs_dialog.show( 'fast', function(){
				rs_dialog.addClass('in');
			});	
			return false;
		});

		// Close dialog when clicking on the close button
		rs_close.click(function(e){			
			rs_dialog.removeClass('in').delay(150).queue(function(){
				rs_dialog.hide().dequeue();	
				rs_overlay.fadeOut('slow');
				$('html').removeClass('dialog-open');
				$('body').css('padding-right', '');		
			});
			return false;
		});

		// Close dialog when clicking outside the dialog
		rs_dialog.click(function(e){
			rs_close.trigger('click');		
		});
		rs_box.click(function(e){
			e.stopPropagation();
		});		
	});
});

防止网页内容偏移

打开弹窗时给body增加overflow:hidden属性,导致滚动条消失,这时会使网页的内容往右移动一下,待弹窗打开滚动条再出现时,又会复原,视觉效果不友好。如果在有滚动条的情况下知道其宽度,给body增加padding-right属性就可以抵消这个便宜效果。

这个方法在于江水博客关于弹窗的文章里找到了,在open dialog的click时间中引用了文章提到的最后一种方法。

触发弹窗

最后,根据上面的脚本,触发弹窗的链接如下

<a href="#" rel="rs-dialog" data-target="myModal">Launch Demo Modal</a>

rel="rs-dialog"表示这是弹窗触发链接

data-target="myModal"表示要打开HTML ID为myModal的弹窗。

源代码下载

WordPress版本需要加入主题使用,html版本直接运行即可

Simple Dialog WordPress版本 已下载 620 次
Simple Dialog HTML版 已下载 731 次

插件版

如果想知道如何将这段脚本编程WordPress插件,请看参考文章《写一个WordPress弹窗插件》

17条留言

  1. 站长你好,
    请问能不能使用本文的弹窗代码实现这样的效果:给首页文章添加弹窗效果,当点击一篇文章链接的时候,出来一个弹窗,但弹窗的内容是动态获取相应文章的内容,胆鉴于是弹窗,让弹窗里面呈现的只是文章页的content部分,而不显示头部、脚部等无用部分。
    静候指教,谢谢。

    1. 当然可以啊,点击文章链接时通过ajax异步获取内容,在弹窗显示前将内容置入div.rs-dialog-body。

      获取内容的方法是利用jquery的load(http://api.jquery.com/load/)

  2. 您好,请教下这弹窗显示数据:
    后台列表:
    详情1
    详情2
    ……..
    详情n
    想实现不同id标签点击弹窗获取相应数据
    请问使用wordpress后台自带的ajax怎么样实现
    admin-ajax.php
    恳请帮忙实现下!万分感谢!

    1. 如果你是问wordpress里如何使用ajax,可以看文档https://codex.wordpress.org/AJAX_in_Plugins

      如果你的详情链接直接链向你想显示的页面,可以用ajax load方法获取内容,看文档http://api.jquery.com/load/

      1. var ajax_url = ‘admin-ajax.php;
        $.ajax({
        url:ajax_url, //ajax地址
        type:’GET’,
        success: function(a) {
        $(‘body’).empty().html(a) //成功时
        }

        }

        我用这不成功。GET不到值

        1. ajax的javascript部分负责将数据传递给server, server上还有有段脚本处理数据才行,因此ni你的代码不完整,而且ajax_url应该是admin_url( ‘admin-ajax.php’ )(php代码)

          我给你说的第二种方法不涉及php,说白了就是让javascript帮你读一下链接,把内容传回来,截取一部分显示,跟无限加载文章的原理一样。

  3. 博主,有什么办法使内容区域能够支持短代码显示吗?试了 add_filter( ‘rs-dialog-box’, ‘do_shortcode’ ); 不行。本人文盲,请教授,谢谢

    1. 内容区域是指显示文章编辑器里内容的区域吗?那个本来就支持短代码不需要更改。
      如果短代码原样显示出来,先检查代码写的是否正确。

      1. 不是文章的,是rs-dialog-body里面的内容区能不能实现支持短代码显示?有什么方法吗?

        1. 那你需要对输出的内容运行do_shortcode函数,就像这样
          do_shortcode( ‘需要支持shortcode的内容’ );

  4. 好久不见。

    关于弹窗滚动条这里,高度高导致滚动条的出现,会出现偏振的问题,自荐一篇文章,可以改善:http://yujiangshui.com/review-how-to-make-popup-mask-effect/#去掉滚动条但是避免页面内容偏移

    1. 非常感谢你的留言,我有注意到偏振问题单碍于前端水平有限没解决了,研究bootstrap官方modal例子时,发现在谷歌和firefox下没偏振,但IE11下会向左偏,原因是bootstrap开启了IE滚动条的autohiding模式。
      我实验了你twitter和fancybox代码,当IE是autohiding时检测到的滚动条宽度是0,fancybox那段真的很巧妙,正好适合我的代码使用。

      position:absolute的方法虽然没滚动条的事,但弹窗的滚动距离有时会远大于弹窗高度,所以我更倾向于bootstrap的方式,不知道为何bootstrap的例子没能正确检测IE滚动条宽度。

      看了你的文章我觉得我的代码还能继续简化,好文章!

      1. 赞。

        autohiding 是指 -ms-autohiding-scrollbar 这个属性么?好久没接触 IE 有关的东西了,我等看下去。

发表评论

电子邮件地址不会被公开。 必填项已用*标注