如何检查元素是否在屏幕外

99

我需要用jQuery检查一个DIV元素是否没有移出屏幕。这些元素根据CSS属性可见并显示,但它们可能会被故意放在屏幕外。

position: absolute; 
left: -1000px; 
top: -1000px;

由于元素具有非零高度和宽度,我无法使用jQuery的:visible选择器。

我没有做任何花哨的事情。这种绝对定位方式是我的Ajax框架实现某些小部件的隐藏/显示的方法。


我在这个“重复”的问题上也给出了答案: https://dev59.com/VW435IYBdhLWcg3wigux#5354536 - Tokimon
8个回答

158

取决于你对“屏幕外”这个概念的定义,是指视口内还是页面定义的边界内?

使用Element.getBoundingClientRect(),你可以轻松检测你的元素是否在视口的范围内(即在屏幕上或屏幕外):

jQuery.expr.filters.offscreen = function(el) {
  var rect = el.getBoundingClientRect();
  return (
           (rect.x + rect.width) < 0 
             || (rect.y + rect.height) < 0
             || (rect.x > window.innerWidth || rect.y > window.innerHeight)
         );
};

你可以以多种方式使用它:

// returns all elements that are offscreen
$(':offscreen');

// boolean returned if element is offscreen
$('div').is(':offscreen');

2
我喜欢这个,但是如果您的元素位于可滚动容器内,则会出现问题--位于高度为2屏幕的DIV底部的元素始终会处于“屏幕外”,因为窗口高度小于元素的offsetTop。 - Coderer
@scurker 我无法让它工作。这是我的 SO 问题(带有 jsFiddle):http://stackoverflow.com/questions/26004098/extend-jquery-to-create-custom-filter-re-element-outside-viewport - crashwap
@fekirimalek 你是正确的,我已经更新了示例以解决这个问题。 - scurker
9
请注意,目前(2017年1月)在Chrome浏览器上至少无法使用此方法,因为Element.getBoundingClientRect()具有top,right,bottom和left属性,而不是x和y。 - Asu
1
在函数的条件中加入(rect.x + rect.width > window.innerWidth || rect.y + rect.height > window.innerHeight)怎么样? - Assad Nazar
显示剩余5条评论

17

这里有一个jQuery插件,允许用户测试元素是否在浏览器的可见视口范围内,并考虑浏览器的滚动位置。

$('#element').visible();

您还可以检查部分可见性:

$('#element').visible( true);

其中一个缺点是它只能在垂直定位/滚动时使用,但应该很容易将水平定位加入到其中。


这是一个非常轻量级的插件。我只需添加几分钟就能实现固定导航栏并响应式工作。非常好的建议。 - Nappstir
这个插件非常轻量级且非常容易实现。解决了我在子菜单方面遇到的问题。 - Theo Orphanos

9

嗯……我在这里发现了一些问题,这些问题与每个提出的解决方案有关。

  • 您应该能够选择您是否想要将整个元素显示在屏幕上或是只想显示其任意部分
  • 如果元素比窗口更高/更宽,则所提出的解决方案失败,并且会在某种程度上覆盖浏览器窗口。

以下是我的解决方案,包括jQuery .fn实例函数和expression。我在函数中创建了比我需要的更多变量,但对于复杂的逻辑问题,我喜欢将其分成更小,名称明确的部分。

我正在使用getBoundingClientRect方法,该方法返回相对于视口的元素位置,因此我不需要关心滚动位置

使用方法

$(".some-element").filter(":onscreen").doSomething();
$(".some-element").filter(":entireonscreen").doSomething();
$(".some-element").isOnScreen(); // true / false
$(".some-element").isOnScreen(true); // true / false (partially on screen)
$(".some-element").is(":onscreen"); // true / false (partially on screen)
$(".some-element").is(":entireonscreen"); // true / false 

来源:

$.fn.isOnScreen = function(partial){

    //let's be sure we're checking only one element (in case function is called on set)
    var t = $(this).first();

    //we're using getBoundingClientRect to get position of element relative to viewport
    //so we dont need to care about scroll position
    var box = t[0].getBoundingClientRect();

    //let's save window size
    var win = {
        h : $(window).height(),
        w : $(window).width()
    };

    //now we check against edges of element

    //firstly we check one axis
    //for example we check if left edge of element is between left and right edge of scree (still might be above/below)
    var topEdgeInRange = box.top >= 0 && box.top <= win.h;
    var bottomEdgeInRange = box.bottom >= 0 && box.bottom <= win.h;

    var leftEdgeInRange = box.left >= 0 && box.left <= win.w;
    var rightEdgeInRange = box.right >= 0 && box.right <= win.w;


    //here we check if element is bigger then window and 'covers' the screen in given axis
    var coverScreenHorizontally = box.left <= 0 && box.right >= win.w;
    var coverScreenVertically = box.top <= 0 && box.bottom >= win.h;

    //now we check 2nd axis
    var topEdgeInScreen = topEdgeInRange && ( leftEdgeInRange || rightEdgeInRange || coverScreenHorizontally );
    var bottomEdgeInScreen = bottomEdgeInRange && ( leftEdgeInRange || rightEdgeInRange || coverScreenHorizontally );

    var leftEdgeInScreen = leftEdgeInRange && ( topEdgeInRange || bottomEdgeInRange || coverScreenVertically );
    var rightEdgeInScreen = rightEdgeInRange && ( topEdgeInRange || bottomEdgeInRange || coverScreenVertically );

    //now knowing presence of each edge on screen, we check if element is partially or entirely present on screen
    var isPartiallyOnScreen = topEdgeInScreen || bottomEdgeInScreen || leftEdgeInScreen || rightEdgeInScreen;
    var isEntirelyOnScreen = topEdgeInScreen && bottomEdgeInScreen && leftEdgeInScreen && rightEdgeInScreen;

    return partial ? isPartiallyOnScreen : isEntirelyOnScreen;

};

$.expr.filters.onscreen = function(elem) {
  return $(elem).isOnScreen(true);
};

$.expr.filters.entireonscreen = function(elem) {
  return $(elem).isOnScreen(true);
};

1
当我尝试在那里使用你的函数时,我得到了make_footer_down.js:8未捕获的类型错误:无法读取未定义的属性“getBoundingClientRect”。 - Ludvig W
嗨Lurr...我尝试使用$("#divId").isOnScreen(true);其中'divId'不在屏幕上,它返回了'false';所以,我觉得它有效。 - Anirban

9
无需插件即可检查是否超出视窗。
var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0)
var d = $(document).scrollTop();

$.each($("div"),function(){
    p = $(this).position();
    //vertical
    if (p.top > h + d || p.top > h - d){
        console.log($(this))
    }
    //horizontal
    if (p.left < 0 - $(this).width() || p.left > w){
        console.log($(this))
    }
});

1
如果你使用$(this).offset而不是$this.position,这个方法会起作用。现在它只是计算与其父元素之间的距离,而父元素可能是一个完美地包裹它的div。 - Ryan Shillington

8

虽然原帖要求使用 JQuery 解决方案,但他们也在问题中添加了 JavaScript 标签,因此可以添加这个更现代的解决方案:

TLDR: 使用 IntersectionObserver API

这里有两个沙盒示例,我都使用原生 JavaScript 和 React 制作了代码样例:

  1. 原生 JavaScript 沙盒示例
  2. React 沙盒示例 (使用 react-intersection-observer NPM 模块)

稍长一些的答案: 这个问题被提出已经超过10年了,那时使用 JQuery 的解决方案是合理的,但今天我们不再需要 JQuery 来满足这种需求了:

Intersection Observer API 提供了一种观察一个目标元素与祖先元素或顶级文档视口交叉变化的方法。

它非常容易使用。首先创建一个观察器,传递一个回调函数以及一个可选的选项对象,例如在这个问题中,我们需要将阈值选项的值设置为1,以确保元素完全位于视口内部。如果不是,则会知道它是“离屏”的:

const doable = () => {
  //do something
}
const observer = new IntersectionObserver(doable, { threshold: 1 });

现在调用observe() 方法,并将要观察的元素作为参数传递给它:
observer.observe(observee)

1

1
  • Get the distance from the top of the given element
  • Add the height of the same given element. This will tell you the total number from the top of the screen to the end of the given element.
  • Then all you have to do is subtract that from total document height

    jQuery(function () {
        var documentHeight = jQuery(document).height();
        var element = jQuery('#you-element');
        var distanceFromBottom = documentHeight - (element.position().top + element.outerHeight(true));
        alert(distanceFromBottom)
    });
    

-2
你可以使用$(div).position()来检查div的位置,并检查左和上边距属性是否小于0:
if($(div).position().left < 0 && $(div).position().top < 0){
    alert("off screen");
}

3
如果该div不在视图之外,它的值会是什么? - dude

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