Detect when elements within a scrollable div are out of view

前端 未结 8 1806
我寻月下人不归
我寻月下人不归 2020-12-24 13:15

I need to find a good solution to the following problem. I see a lot of people asking about tracking if an element is in or outside of view Port for the page or browser wind

相关标签:
8条回答
  • 2020-12-24 13:27

    Here is a pure javascript solution.

    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
        };
    };
    
    0 讨论(0)
  • 2020-12-24 13:27

    You can try this

    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);
    }
    

    eg:

    isScrolledIntoView('#button')
    
    0 讨论(0)
  • 2020-12-24 13:37

    Here's a pure javascript version of the accepted answer without relying on jQuery and with some fixes to the partial in view detection and support for out of view on top.

    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);
    }
    

    And as a bonus, this function ensures the element is in view if it's not (partial or full):

    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);
        }
    }
    
    0 讨论(0)
  • 2020-12-24 13:37

    I made a jquery plugin with the last answer:

    (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);
    
    0 讨论(0)
  • 2020-12-24 13:37

    Played around with it for my purposes. Here is my solution (vanilla)

    Menu is the container, el is the active element.

    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);
        }
    
    0 讨论(0)
  • 2020-12-24 13:39

    i had the same problem before, i have ended up with the following function.the first parameter is for the element to check, the second is to check if the element is partially in-view.it is for vertical check only, you can extend it to check for horizontal scroll.

    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 ;
    }
    

    check it on jsFiddle .

    0 讨论(0)
提交回复
热议问题