jquery 检测可滚动div中的元素何时不在视图中

8ljdwjyq  于 2023-06-29  发布在  jQuery
关注(0)|答案(9)|浏览(103)

我需要找到一个很好的解决以下问题的方法。我看到很多人询问跟踪元素是否在页面或浏览器窗口的视图端口内或外。我需要能够复制这个动作,但在一个DIV的滚动,与溢出:滚动为例。有没有人知道一个很好的例子,为这个具体的行动?
先谢谢你了。

6yt4nkrj

6yt4nkrj1#

为了我的目的摆弄它。这是我的解决方案(香草)
Menu是容器,el是活动元素。

const isVisible = (menu, el) => {
      const menuHeight = menu.offsetHeight;
      const menuScrollOffset = menu.scrollTop;
    
      const elemTop = el.offsetTop - menu.offsetTop;
      const elemBottom = elemTop + el.offsetHeight;
      return (elemTop >= menuScrollOffset &&
     elemBottom <= menuScrollOffset + menuHeight);
    }
xxe27gdn

xxe27gdn2#

这是一个纯JavaScript的解决方案。

function elementIsVisible(element, container, partial) {
    var contHeight = container.offsetHeight,
    elemTop = offset(element).top - offset(container).top,
    elemBottom = elemTop + element.offsetHeight;
    return (elemTop >= 0 && elemBottom <= contHeight) || 
    (partial && ((elemTop < 0 && elemBottom > 0 ) || (elemTop > 0 && elemTop <= contHeight)))
}

// checks window
function isWindow( obj ) {
    return obj != null && obj === obj.window;
}

// returns corresponding window
function getWindow( elem ) {
    return isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
}

// taken from jquery
// @returns {{top: number, left: number}} 
function offset( elem ) {

    var docElem, win,
        box = { top: 0, left: 0 },
        doc = elem && elem.ownerDocument;

    docElem = doc.documentElement;

    if ( typeof elem.getBoundingClientRect !== typeof undefined ) {
        box = elem.getBoundingClientRect();
    }
    win = getWindow( doc );
    return {
        top: box.top + win.pageYOffset - docElem.clientTop,
        left: box.left + win.pageXOffset - docElem.clientLeft
    };
};
zbwhf8kr

zbwhf8kr3#

以下是使用相对较新的Intersection Observer API(自2019年以来已在主要浏览器中支持)的解决方案:https://caniuse.com/intersectionobserver)。
这几乎处理了我在这个问题中看到的所有边缘情况。(它唯一没有捕捉到的边缘情况是绝对定位的元素遮挡了滚动元素。

async function isElementInScrollView(element) {
    return new Promise((resolve) => {
        const observer = new IntersectionObserver(
            (entries, observerItself) => {
                observerItself.disconnect();
                resolve(entries[0].intersectionRatio === 1);
            }
        );
        observer.observe(element);
    });
}

用法:

const isInView = await isElementInScrollView(document.querySelector('#my-element'));

您可以修改函数实现的intersectionRatio === 1部分,以更改元素必须如何“显示”,以便此函数返回true。(intersectionRatio是介于0.01.0之间的值。)请参阅intersectionRatio文档:https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry/intersectionRatio
下面是一个例子:

async function isElementInScrollView(element) {
  return new Promise((resolve) => {
    const observer = new IntersectionObserver((entries, observerItself) => {
      observerItself.disconnect();
      resolve(entries[0].intersectionRatio === 1);
    });
    observer.observe(element);
  });
}

async function detect() {
  const allChildren = Array.from(document.querySelectorAll(".child"));

  const results = await Promise.all(
    allChildren.map(async(child) => {
      return await isElementInScrollView(child);
    })
  );

  printResults(results);
}

function printResults(results) {
  document.querySelector('.results').innerHTML = results.join('<br>')
}

detect();
.restricted {
  padding: 32px;
  padding-right: 64px;
  border: 1px solid black;
  max-height: 100px;
  width: 100px;
  overflow: auto;
  display: inline-flex;
  flex-direction: column;
  gap: 8px;
}

.child {
  padding: 8px;
  border: 1px solid red;
}

.results {
  display: inline-block;
}

body>* {
  vertical-align: top;
}
<div class="restricted">
  <div class="child">Child</div>
  <div class="child">Child</div>
  <div class="child">Child</div>
  <div class="child">Child</div>
  <div class="child">Child</div>
  <div class="child">Child</div>
  <div class="child">Child</div>
  <div class="child">Child</div>
  <div class="child">Child</div>
</div>

<button onclick="detect()">Detect</button>
<div class="results"></div>
ttygqcqt

ttygqcqt4#

我用最后一个答案做了一个jquery插件:

(function($) {
    $.fn.reallyVisible = function(opt) {

        var options = $.extend({
            cssChanges:[
                { name : 'visibility', states : ['hidden','visible'] }
            ],
            childrenClass:'mentioners2',
            partialview : true
        }, opt);

        var container = $(this);
        var contHeight;
        var contTop;
        var contBottom;
        var _this = this;
        var _children;

        this.checkInView = function(elem,partial){

            var elemTop = $(elem).offset().top - container.offset().top;
            var elemBottom = elemTop + $(elem).height();

            var isTotal = (elemTop >= 0 && elemBottom <=contHeight);
            var isPart = ((elemTop < 0 && elemBottom > 0 ) || (elemTop > 0 && elemTop <= container.height())) && partial ;

            return  isTotal  || isPart ;
        }

        this.bind('restoreProperties',function(){
            $.each(_children,function(i,elem){
                $.each(options.cssChanges,function(i,_property){
                    $(elem).css(_property.name,_property.states[1]);        
                });
            });
            _children = null;
        });

        return this.each(function(){
            contHeight = container.height();
            contTop = container.scrollTop();
            contBottom = contTop + contHeight ;

            _children = container.children("."+options.childrenClass);

            $.each(_children,function(i,elem){
                var res = _this.checkInView(elem,options.partialview);
                if(  !res ){
                    $.each(options.cssChanges,function(i,_property){
                        $(elem).css(_property.name,_property.states[0]);        
                    });
                }
            });

        }); 
    }

})(jQuery);
waxmsbnn

waxmsbnn5#

你可以试试这个

function isScrolledIntoView(elem) {
    var docViewTop = $(window).scrollTop();
    var docViewBottom = docViewTop + window.innerHeight;
    var el = $(elem);
    var elemTop = el.offset().top;
    var elemBottom = elemTop + el.height();
    var elemDisplayNotNone = el.css("display") !== "none";

    return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop) && elemDisplayNotNone);
}

例如:

isScrolledIntoView('#button')
aiqt4smr

aiqt4smr6#

这里是一个纯JavaScript版本的公认答案,不依赖于jQuery,并对部分视图检测和顶部视图外支持进行了一些修复。

function checkInView(container, element, partial) {

    //Get container properties
    let cTop = container.scrollTop;
    let cBottom = cTop + container.clientHeight;

    //Get element properties
    let eTop = element.offsetTop;
    let eBottom = eTop + element.clientHeight;

    //Check if in view    
    let isTotal = (eTop >= cTop && eBottom <= cBottom);
    let isPartial = partial && (
      (eTop < cTop && eBottom > cTop) ||
      (eBottom > cBottom && eTop < cBottom)
    );

    //Return outcome
    return  (isTotal  || isPartial);
}

作为奖励,该函数确保元素在视图中,如果它不是(部分或全部):

function ensureInView(container, element) {

    //Determine container top and bottom
    let cTop = container.scrollTop;
    let cBottom = cTop + container.clientHeight;

    //Determine element top and bottom
    let eTop = element.offsetTop;
    let eBottom = eTop + element.clientHeight;

    //Check if out of view
    if (eTop < cTop) {
      container.scrollTop -= (cTop - eTop);
    }
    else if (eBottom > cBottom) {
      container.scrollTop += (eBottom - cBottom);
    }
}
pb3s4cty

pb3s4cty7#

我以前也遇到过同样的问题,我最终得到了下面的函数。第一个参数是要检查的元素,第二个是检查元素是否部分in-view.it只用于垂直检查,你可以扩展它来检查水平滚动。

function checkInView(elem,partial)
{
    var container = $(".scrollable");
    var contHeight = container.height();
    var contTop = container.scrollTop();
    var contBottom = contTop + contHeight ;

    var elemTop = $(elem).offset().top - container.offset().top;
    var elemBottom = elemTop + $(elem).height();

    var isTotal = (elemTop >= 0 && elemBottom <=contHeight);
    var isPart = ((elemTop < 0 && elemBottom > 0 ) || (elemTop > 0 && elemTop <= container.height())) && partial ;

    return  isTotal  || isPart ;
}

jsFiddle上查看。

e7arh2l6

e7arh2l68#

基于最佳答案。而不是仅仅告诉你一个元素是否部分可见。我添加了一点额外的内容,以便您可以传入一个百分比(0-100),告诉您元素是否超过x%可见。

function (container, element, partial) {
    var cTop = container.scrollTop;
    var cBottom = cTop + container.clientHeight;
    var eTop = element.offsetTop;
    var eBottom = eTop + element.clientHeight;
    var isTotal = (eTop >= cTop && eBottom <= cBottom);
    var isPartial;

    if (partial === true) {
        isPartial = (eTop < cTop && eBottom > cTop) || (eBottom > cBottom && eTop < cBottom);
    } else if(typeof partial === "number"){
        if (eTop < cTop && eBottom > cTop) {
            isPartial = ((eBottom - cTop) * 100) / element.clientHeight > partial;
        } else if (eBottom > cBottom && eTop < cBottom){ 
            isPartial = ((cBottom - eTop) * 100) / element.clientHeight > partial;
        }
    }
    return (isTotal || isPartial);
}
yrefmtwq

yrefmtwq9#

我能够通过对所发布的纯JavaScript版本做一个小改动来实现这一点

function checkInView(container, element, partial) {

    //Get container properties
    let cTop = container.scrollTop;
    let cBottom = cTop + container.clientHeight;

    //Get element properties
    let eTop = element.offsetTop - container.offsetTop; // change here
    let eBottom = eTop + element.clientHeight;

    //Check if in view    
    let isTotal = (eTop >= cTop && eBottom <= cBottom);
    let isPartial = partial && (
      (eTop < cTop && eBottom > cTop) ||
      (eBottom > cBottom && eTop < cBottom)
    );

    //Return outcome
    return  (isTotal  || isPartial);
  }

相关问题