JavaScript/CSS:禁用滚动条但保留滚动位置

24

我正在使用Jquery对话框在页面上打开一个弹出框窗口。 当我打开对话框时,我希望禁用一般页面滚动。 为了做到这一点,我正在进行以下操作:

$('body').css({overflow:'hidden'});
当对话框打开时,会:
$('body').css({overflow:'auto'});

当对话框关闭时。

这个方法可以工作,但是当滚动条被移除时,后面的内容会向右移动,结果不太好看。

我尝试了另一种方法,创建一个名为“noscroll”的CSS类,如下所示:

body.noscroll
{
    position: fixed; 
    overflow-y: scroll;
    width: 100%;
}

那么,与先前的JS代码不同,我在打开/关闭对话框时向body添加和删除了这个类。

现在,滚动条可以正常工作,后面的内容不会向右移动,但是通过这种方法,背景中的内容回到了顶部。

因此,方法1使内容向右移动,而方法2使内容回到顶部。

有谁知道解决此问题的方法吗?即当对话框打开时,背景中的内容不出现滚动条,禁用滚动时也不会移动...?


1
你是否考虑过在对话框后面实现一个绝对定位的100% div覆盖层,以阻止页面上的所有交互? - dSquared
好思路,我会尝试并发布我的结果! - Pierre
你找到这个问题的解决方案了吗?你能帮我吗?谢谢!抱歉我的英语不太好。 - Thanh Nguyen
不一定,尝试一下我用过的和别人建议的,看看哪个对你最有效... - Pierre
6个回答

11

我已经制作了一个相当简单的解决方案示例。

http://jsfiddle.net/6eyJm/1/

你的弹出框应该在一个框里面。

<div id="shadow">
<div id="popup">
    <a id='close' href="#">Close</a>
</div>

然后将这些CSS放在根div上

#shadow{
    display: none;
    position: fixed;
    top:0;
    bottom: 0;
    width: 100%;
    height:100%;
    background-color: rgba(0,0,0,0.6);
}

固定位置非常重要,因为您不希望看到白色边框,它将占用整个窗口宽度而不是文档主体。

然后有一个小的JS技巧

$('#open').click(function(e){
    e.preventDefault()
    $('body').width($('body').width());
    $('body').css('overflow', 'hidden');
    $('#shadow').css('display', 'block');
})
$('#close').click(function(e){
    e.preventDefault()
    $('body, #shadow').removeAttr('style')
})

这里的目标是在移除滚动条之前获取页面的宽度,您的内容将不会移动。

希望能对您有所帮助!

抱歉我的英语不好,这不是我的母语。


8
当我将“打开”按钮放在列表中间时,点击它会禁止页面滚动,但会回到页面顶部。 - Thanh Nguyen
5
还是不行。打开时,滚动位置保持不变,但关闭后又回到了顶部。我的代码在这里:http://jsfiddle.net/ZG8FU/ - Thanh Nguyen
这个解决方案仍然无法阻止箭头键和pageup/pagedown/end/home滚动原始内容。这个问题在许多其他帖子中得到了探讨:一个在这里,还有另一个在这里;还有一些较小的帖子,可能找不到解决方案。据我所知,没有一个是完整的。 - Thomas
1
@TheatreOfSouls 锚点链接会将页面滚动到顶部。阻止链接的默认行为:http://jsfiddle.net/zujv0g36/. - Karl-André Gagnon
1
7年前的帖子救了我的一天 :) 非常感谢您的想法,非常有效,老实说我并没有看出你的英语有什么问题 :D - Emil Borconi
显示剩余5条评论

9

记住偏移量可以使你的弹出窗口保持在原地;

JSfiddle

HTML

<div id="popupholder">
    <button id="close">Close me</button>
</div>


asd <br />asd <br />asd <br />asd <br />asd <br />
<button class="open">Popup</button>
<br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd<br />
<button class="open">Popup</button>
<br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />
<button class="open">Popup</button>
<br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd<br />
<button class="open">Popup</button>
<br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />asd <br />

CSS

html, body {
    margin: 0px;
}

#popupholder {
    width: 100%;
    height: 100%;
    position: absolute;
    display: box;
    display: -webkit-box;
    display: -moz-box;
    background-color: rgba(0,0,0,0.75);
    display: none;
}

#close {
    display: block;
    height: 20px;
    margin: 75px auto 0px auto;
}

JavaScript

$(document).ready(function() {
    $('.open').click(function() {
        // Block scrolling
        $('body').css('overflow', 'hidden');

        // Show popup
        var offset = window.pageYOffset;
        //console.log(offset);
        $('#popupholder').css({
            'display': 'block',
            'top': offset + 'px'
        });
    });

    $('#close').click(function() {
        // Enable scrolling
        $('body').css('overflow', 'auto');

        // Hide popup
        $('#popupholder').css('display', 'none');
    });
});

为了安全起见,您可以在#popupholder上添加非常高的z-index,但这与问题无关。

感谢您抽出时间编写代码,但我想禁用滚动条,而不是像 Facebook 弹出窗口一样隐藏它。 - Thanh Nguyen
你也可以将 overflow: hidden; 添加到 #popupholder 中。否则,你可以查看类似于 WebKit 的 ::-webkit-scrollbar { display: none; } 样式的变体。它将访问 Shadow DOM 并完全隐藏滚动条。 - user1467267

3
这是目前我得到的最佳解决方案。相信我,我尝试了所有其他方法,这是我想出来的最好、最简单的方法。它在Windows设备上表现出色,可以将页面从右侧推出以留出系统滚动条的空间,并且在IOS设备上也很好用,在浏览器中不需要为其滚动条腾出空间。因此,使用此方法,您无需在右侧添加填充,以使页面在使用CSS隐藏正文或HTML的溢出时不会闪烁。

如果您认真思考,这个解决方案非常简单。其思路是在弹出窗口打开时给window.scrollTop()设置完全相同的位置。同时,当窗口大小改变时更改该位置(因为此时滚动位置也会改变)。

那么,我们开始吧...

首先,让我们创建一个变量,称之为stopWindowScroll,用于指示弹出窗口是否已打开。如果我们不这样做,您的页面将出现未定义变量的错误,并将其设置为0-表示未激活。

$(document).ready(function(){
    stopWindowScroll = 0;
});

现在让我们创建打开弹出窗口的函数,这个函数可以是代码中触发任何弹出窗口插件或自定义弹出窗口的函数。 在这种情况下,它将是一个简单的自定义弹出窗口,具有简单的“单击文档”功能。
$(document).on('click','.open-popup', function(){
    // Saving the scroll position once opening the popup.
    stopWindowScrollPosition = $(window).scrollTop();
    // Setting the stopWindowScroll to 1 to know the popup is open.
    stopWindowScroll = 1;
    // Displaying your popup.
    $('.your-popup').fadeIn(300);
});

所以接下来我们要创建关闭弹出窗口的函数,我再重复一遍,这个函数可以是你已经创建或正在使用的任何函数或插件。重要的是,我们需要这两个函数将 stopWindowScroll 变量设置为 1 或 0,以便知道它是开着还是关闭着。
$(document).on('click','.open-popup', function(){
    // Setting the stopWindowScroll to 0 to know the popup is closed.
    stopWindowScroll = 0;
    // Hiding your popup
    $('.your-popup').fadeOut(300);
});

那么我们就创建一个 window.scroll 函数,以便在上面提到的 stopWindowScroll 被设置为 1(激活状态)时,可以防止滚动。

$(window).scroll(function(){
    if(stopWindowScroll == 1) {
         // Giving the window scrollTop() function the position on which
         // the popup was opened, this way it will stay in its place.
         $(window).scrollTop(stopWindowScrollPosition);
    }
});

就这样。这不需要CSS才能工作,除了页面上自己的样式。这对我来说非常好用,希望能帮助到你和其他人。

以下是JSFiddle上的一个可行示例:

JS Fiddle 示例

如果有帮助,请告诉我。祝好。


看起来不错!你能更新一下你的 Fiddle 链接吗?因为它无法使用。 - Pierre
更新了 JS Fiddle 的 URL。 - Architect

3
我遇到了同样的问题,这个代码库帮助了我:no-scroll

1

我使用这两个函数来实现相同的目的:

function enableBodyScroll() {
  if (document.readyState === 'complete') {
    document.body.style.position = '';
    document.body.style.overflowY = '';

    if (document.body.style.marginTop) {
      const scrollTop = -parseInt(document.body.style.marginTop, 10);
      document.body.style.marginTop = '';
      window.scrollTo(window.pageXOffset, scrollTop);
    }
  } else {
    window.addEventListener('load', enableBodyScroll);
  }
}

function disableBodyScroll({ savePosition = false } = {}) {
  if (document.readyState === 'complete') {
    if (document.body.scrollHeight > window.innerHeight) {
      if (savePosition) document.body.style.marginTop = `-${window.pageYOffset}px`;
      document.body.style.position = 'fixed';
      document.body.style.overflowY = 'scroll';
    }
  } else {
    window.addEventListener('load', () => disableBodyScroll({ savePosition }));
  }
}

工作原理:

  1. 当您想要禁用滚动并保存当前位置时,请运行 disableBodyScroll({ savePosition: true })

  2. 该函数检查页面是否已加载(因为用户可能在加载过程中触发对话框打开)。

  3. 如果页面已加载,则通过在 body 上设置 margin-top 来保存当前滚动位置,然后将其设置为 position: fixed; overflow-y: scroll 以移除滚动条。

  4. 如果页面尚未加载,则添加事件侦听器以在页面加载时运行 (3.)。

要启用滚动,一切都相同,但是该函数会删除样式而不是设置它们。

代码来源:https://github.com/funbox/diamonds/blob/master/lib/body-scroll.ts


1
你可以计算溢出隐藏前后主体宽度的差异,并将其应用为主体的padding-right。
var bodyStartW = $("body").width();
$("body").css("overflow-y" , "hidden");
var bodyEndW = $("body").width();
var bodyMarginL = bodyEndW - bodyStartW;
$("body").css("padding-right" , bodyMarginL);

在Safari中,你可以使用相同的技巧来处理“html”标签,使用margin-right而不是padding-right。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接