转载自品略图书馆  http://www.pinlue.com/article/2020/03/2010/4610037571703.html

弹窗也叫modal(模态)在网页设计中是一个常见的功能。实现方法也不会太复杂,可以通过css实现居中(bootstrap的modal是使用靠上边距来实现),然后JS控制点击显示和消失。在弹窗中也许你会发现一个问题,就是当页面有滚动条时,点击弹窗可能需要隐藏滚动条,这时内容会变宽,本文来自于江水提供了三种解决方法,收益匪浅,感谢。

弹窗的实现原理和方法

弹窗通常就是两部分,一部分是半透明的背景遮罩,另一部分就是承载主体内容的区域。当点击某个按钮或者某个地方触发,将隐藏的遮罩和内容通过某种动画效果显示出来。然后点击内容中的关闭或者遮罩区域,就会将遮罩和内容通过某种动画效果隐藏起来。

所以,实现弹窗第一步,就是要写结构和CSS。

弹窗的html 结构和 CSS

既然知道实现方法,一般的结构主要有下面两种(分情况使用):

第一种,将内容块独立出来,再声明一个 .overlay 结构用来做背景遮罩。结构如下:

<div class="box-login"> ...</div><div class="overlay"></div>

这样的话,就可以为 .overlay 结构设置半透明的背景图片或者使用 rgba 的背景,然后为 .box-login 应用绝对居中的布局模式或者使用js 动态计算宽高或者位置。(当然,这种方法的兼容性 IE8+ 特别在乎的,可以使用 JS 来计算位置,以及用半透明的 gif 图片当背景)。

这种结构的优势是,遮罩层可以被复用,任何需要弹窗的场景,都可以直接用 overlay 这个遮罩层,twitter 等用的就是这种,也推荐这种。

具体代码直接看下面的用 jsfiddle 做的Demo

第二种,将内容块包裹进 .overlay 结构中。结构如下:

<div class="overlay box-login"> <div class="section"> ... </div></div>

这种结构更加独立一些与其他结构相区别,用 JS 来控制也比较统一比较简单,也比较方便用 JS 等插件生成。fancybox 就是生成这种结构来实现弹窗。

但是这里需要注意,由于内容块是被包裹在 overlay 层的,所以要实现点击 overlay 层取消弹窗的效果,需要在内容块中阻止单击或者其他有关事件的冒泡,这样在内容块中的操作,就不会触发取消弹窗的效果了。

具体代码如下,或者打开Demo

通常还会为弹窗层中添加一个“关闭”按钮,点击关闭按钮就关掉当前弹窗,一般使用下面代码,因为还需要找图标等,就不再上传图片做 Demo 了:

$(".close-it").on("click",function(event) { $(this).parents(".overlay").fadeOut(200,function(){ $(this).removeAttr("style"); });});

还需要注意一个细节,在用jQuery 做动画的时候,尽量用回调函数清除 jQuery 为其添加的 style 属性,这样可以避免一些意外的问题,流过血的教训。

注意弹窗的内容

其实对于弹窗效果的内容块,初始的时候为其应用 display: none; 是非常简单而合理的,但实际应用还会出现一些意外情况,你需要注意弹窗的内容。

前段时间的项目中,弹窗里面的内容比较复杂,有幻灯片、图表之类的插件,结果出现了问题,幻灯片和图表区域内容会出现错位、变形等情况。后来测试了一下发现了原因,因为这些插件在初始化的时候,需要计算外框或者相关元素的尺寸位置等,如果你为其设置了 display: none; 之后,插件初始化就获取不到正确的数据,就错位或者失效了。

所以使用 display: none; 来隐藏弹窗也是需要分情况讨论的,有些情况下,使用 z-index: -1; 的方式隐藏会更加合理,如果需要做动画,可以设置 opacity 或者当页面加载完之后,相关插件已经获取相关结构的尺寸等之后,使用 setTimeout 函数为其增加 display: none; 相关的类也可以。

提升弹窗的用户体验

上面的弹窗还是比较基础和笼统的,一般来说,根据弹窗内容块的布局,还可以分两类:

弹窗后允许页面滚动

这种方式通常使用 position: absolute; ,可以看看我做的这个Demo。主要用来应对弹窗内容很大很多的情况,超过了屏幕的宽高需要产生滚动条来方便浏览者查看。有一些图片弹窗插件使用这种方式,使用 JS 动态计算弹窗内容块的位置,这样即便是内容块很大,也不会造成信息缺失。

但是居中往往需要 JS 配合或者进行位置、尺寸处理,会稍微麻烦一些。

弹窗后不允许页面滚动

你可以通过为内容块设置 position: fixed; 使其虽然滚动了,但内容块仍然居中显示,给人一种没有滚动的感觉,还是刚刚的Demo 注释掉然后修改一下就可以看到。

也可以为 html、body 标签设置 overflow-y: hidden; 属性,使其滚动条消失,无法滚动。

通常来说,一般都要使用 hidden 来隐藏滚动条禁止滚动,然后使用 absolute 或者 fixed 就看你自己的需求来选择。但是,对 html 或 body 设置 overflow-y: hidden; 之后,由于滚动条没有了,宽度变宽,页面内容会整体偏移一点,虽然只是一点,但是很明显。这简直太影响用户体验了!!

去掉滚动条但是避免页面内容偏移

知乎上前段时间也提到了这个问题:如何禁止浏览器滚动条滚动,但是又不让它消失?。其中比较简单方便的就是TQ 学长的回答,但是实际测试并不完美,因为 chrome 浏览器的滚动条是 15px 像素宽,而 firefox 浏览器是 17px 像素。如果统一设置成 17px 的话,chrome 等浏览器中显然还会偏移 2px ,虽然尽力完善了,但还是有点小偏移,受不了。

既然不同浏览器里面滚动条宽度不同,那可否先用 JS 检测操作系统和浏览器,然后再根据判断的浏览器等设置不同的偏移量?大体思路是对的,但是方法是错的。检测操作系统和浏览器,要判断的情况太多,代码太过于复杂。于是这个问题就暂时放在这里了。

偶然刷 twitter 的时候,触发弹窗效果的时候,偶然看到了他们也是采用这种方式隐藏滚动条并且保证不会偏移,经过测试各个浏览器中效果完美。于是分析了一下他们的代码,下面是他们的函数:

function ScrollbarWidth() { this.calculateScrollbarWidth = function() { if ($("#scrollbar-width").length > 0) return; var a = $("<div class="modal-measure-scrollbar"/>").prependTo($("body")), b = $("<div class="inner"/>").appendTo(a), c = a.width() - b.width(); a.remove(), $("head").append("<style id="scrollbar-width"> .compensate-for-scrollbar, .modal-enabled, .modal-enabled .global-nav-inner, .profile-editing, .profile-editing .global-nav-inner, .overlay-enabled, .overlay-enabled .global-nav-inner, .grid-enabled, .grid-enabled .global-nav-inner, .gallery-enabled, .gallery-enabled .global-nav-inner { margin-right: " + c + "px } </style>") }}

精简一下主要代码就是

var a = $("<div class="modal-measure-scrollbar"/>").prependTo($("body")),b = $("<div class="inner"/>").appendTo(a),c = a.width() - b.width();a.remove();$("head").append("<style id="scrollbar-width"> .compensate-for-scrollbar{ margin-right:" + c + "px } </style>");

大体意思就是新建了一个包裹的结构,然后两个宽度相减得到滚动条的宽度,然后输出到 head 中。当有弹窗发生之后,就为 html 标签赋予相应的类来 margin-right 一下,避免滚动条消失引起的内容偏移。

Great!这才是正解,显然这两个结构需要赋予一定的 CSS,目的要使 .modal-measure-scrollbar 产生滚动条,而 .inner 是不包括滚动条的全宽,他们的差值正好就是滚动条的宽度!

twitter 中对它们应用的样式如下:

.modal-measure-scrollbar{ position: absolute; height: 100px; width: 100px; top: -300px; left: -300px; overflow: scroll; z-index: 1000; overflow-y: scroll;}.modal-measure-scrollbar .inner{ height: 200px;}

不需要解释了吧。

后来又测试了 fancybox 插件,它的方法更加简单通用,具体代码如下:

w1 = $(window).width();H.addClass("fancybox-lock-test");w2 = $(window).width();H.removeClass("fancybox-lock-test");$("<style type="text/css">.fancybox-margin{margin-right:" + (w2 - w1) + "px;}</style>").appendTo("head");

其中 H 变量是 $("html") ,大意就是先检测现有宽度,然后再为 html 增加一个类,再检测,然后复原去掉类得到滚动条宽度。大体想一下也可以知道 .fancybox-lock-test 类的代码是这样的:

.fancybox-lock-test { overflow-y: hidden !important;}

这种方法更加巧妙简单,不需要创建临时的 DOM 结构,所以强烈推荐在项目中使用这种方式判断。

此外,还有第三种方法,是来自 QQ 空间的。QQ 空间的相册之类的,也是采用弹窗的,查看了一下他们的实现方式,就是使用的固定的 17px 的偏移值,在其他浏览器中也是 17px 像素。但是使用中很难看到内容的偏移,原因是背景遮罩层太黑了,透明度不高,所以很多细节就忽略掉了。如果你的背景遮罩不是很透明,显然也就不需要处理这了~

扩展阅读

推荐前端博客 对于检测滚动条提供的原生 JS 实现方法:计算滚动条的宽度

方法一:添加一个元素,强制滚动,计算offsetWidth和clientWidth之差,然后删除

function getScrollbarWidth() { var oP = document.createElement("p"), styles = { width: "100px", height: "100px", overflowY: "scroll" }, i, scrollbarWidth; for (i in styles) oP.style[i] = styles[i]; document.body.appendChild(oP); scrollbarWidth = oP.offsetWidth - oP.clientWidth; oP.remove(); return scrollbarWidth;}

方法二:添加一个元素,计算clientWidth,强制滚动,再次计算clientWidth,比较得出滚动条宽度

function getScrollbarWidth() { var oP = document.createElement("p"), styles = { width: "100px", height: "100px" }, i, clientWidth1, clientWidth2, scrollbarWidth; for (i in styles) oP.style[i] = styles[i]; document.body.appendChild(oP); clientWidth1 = oP.clientWidth; oP.style.overflowY = "scroll"; clientWidth2 = oP.clientWidth; scrollbarWidth = clientWidth1 - clientWidth2; oP.remove(); return scrollbarWidth;}

浅谈弹窗Modal实现和有滚动条偏移解决方法相关推荐

  1. 弹窗Modal实现和有滚动条偏移解决方法。

    转自:http://www.pinlue.com/article/2020/03/2010/4610037571703.html

  2. 浅谈5G通信面临的电磁兼容挑战及解决方法

    1.背景 目前,国际上对于5G移动通信的需求已经迫在眉睫.按照通信行业的预期,5G应当实现比4G快十倍以上的传输速率,即5G的传输速率可实现1 Gb/s.这就意味着用5G传输一部1GB大小的高清电影仅 ...

  3. 浅谈以太坊智能合约的设计模式与升级方法

    浅谈以太坊智能合约的设计模式与升级方法 1. 最佳实践 2. 实用设计案例 2.1 控制器合约与数据合约: 1->1 2.2 控制器合约与数据合约: 1->N 2.3 控制器合约与数据合约 ...

  4. 浅谈PHP语音里的流程控制及实现方法

    脚本由语句构成,语句靠流程控制实现功能,这一节主要介绍了几个关键字的使用. elseif elseif和else if的行为完全一样,如果用冒号来定义if/elseif条件,那就不能用两个单词的els ...

  5. python打开文件夹中的tiff_浅谈python下tiff图像的读取和保存方法

    对比测试 scipy.misc和 PIL.Image和 libtiff.TIFF三个库 输入: 1. (读取矩阵) 读入uint8.uint16.float32的lena.tif 2. (生成矩阵) ...

  6. 浅谈淘宝客运营的一些思路和方法

    浅谈淘宝客运营的一些思路和方法 我在之前的文章<如何吸引淘宝客推广你的商品>中提到:值得注意的是淘宝客这种推广方式可能不太适合入门级的淘宝店家,初级店家可以通过爆款等方式在市场上证明自身的 ...

  7. python np array归一化_浅谈利用numpy对矩阵进行归一化处理的方法

    浅谈利用numpy对矩阵进行归一化处理的方法 本文不讲归一化原理,只介绍实现(事实上看了代码就会懂原理),代码如下: def Normalize(data): m = np.mean(data) mx ...

  8. git每次都弹窗提示输入用户名和密码的解决方法

    每次进行git操作时总是要输入用户名密码很麻烦,解决方法是:加上用户本地凭证,命令如下 git config --global credential.helper store 然后再进行git操作,比 ...

  9. 浅谈批处理获取管理员运行权限的几种方法

    很多用了Win10版本系统的人都会发现,Windows对程序的运行权限是控制得更加严格了,即使你将UAC控制放至最低,如果没有特别赋予外来程序管理员运行权限的话,很多程序都会运行出错,包括很多用于系统 ...

最新文章

  1. python用渐变色画圆_利用python控制Autocad:pyautocad方式
  2. 组成新数python_大数相加 简单实现 Python 版本
  3. rub、sass和compass的安装
  4. 阿里Java高考卷来了,你能得几分?
  5. 【JSON】JSON入门详解(二)
  6. 54 - 算法 - 动态规划问题 连续子序列和最大
  7. ~~单调队列(数据结构)(附题目)
  8. MTRR内存类型范围寄存器
  9. GJB用于试验的计算机软件,GJB9001C-2017版标准培训课件.ppt
  10. Matlab之数据归一化函数——mapminmax()
  11. 网络安全笔记-网络设备专场(路由器、交换机、防火墙)
  12. Maven第9篇:多环境构建
  13. 法国在华企业名单,坚决抵制!
  14. echars、象棋、飞机大战、五子棋
  15. 我感觉被骗了,微信内测 “大小号” 功能,同一手机号可注册两个微信
  16. 技能提升之word中如何优雅的输入根号3
  17. 【模电】NPN和PNP三极管的区别(一)
  18. 大数据分析哪款软件好用?如何进行大数据分析
  19. easyui(快速入门)
  20. label标签 的使用与作用

热门文章

  1. 小学五年级计算机工作计划,2017小学五年级新学期工作计划
  2. jsp源码商城系统Myeclipse开发mysql数据库servlet开发java编程计算机网页项目
  3. java aqs源码_Java并发系列-AQS源码学习
  4. 2017年7月24日 星期一 --出埃及记 Exodus 28:15
  5. 如何将视频修改成html,如何利用h5将视频设置为背景
  6. java简历工作描述怎么写,帮你解决95%以上的问题!
  7. 基于目的转发和通用转发区别
  8. cfg桩设备型号_武威CFG桩设备-施工
  9. 给axios的请求拦截器中配置token
  10. 查看oracle 分区表大小,ORACLE查询每个表占用空间大小的方法