How much of an element is visible in viewport

前端 未结 5 1604
天命终不由人
天命终不由人 2020-12-29 09:24

There\'s a div (brown rectangle) on the page. The page is higher than the viewport (orange rectangle) so it can be scrolled, which means that the div

相关标签:
5条回答
  • 2020-12-29 09:54

    After playing around a bit I think I've found perhaps the simplest way to do it: I basically determine how much the element extends over the viewport (doesn't matter in which direction) and based on this it can easily be calculated how much of it is visible.

    // When the page is completely loaded.
    $(document).ready(function() {
    
      // Returns in percentages how much can be seen vertically
      // of an element in the current viewport.
      $.fn.pvisible = function() {
        var eTop = this.offset().top;
        var eBottom = eTop + this.height();
        var wTop = $(window).scrollTop();
        var wBottom = wTop + $(window).height();
        var totalH = Math.max(eBottom, wBottom) - Math.min(eTop, wTop);
        var wComp = totalH - $(window).height();
        var eIn = this.height() - wComp;
        return (eIn <= 0 ? 0 : eIn / this.height() * 100);
      }
    
      // If the page is scrolled.
      $(window).scroll(function() {
        // Setting the opacity of the divs.
        $("div").each(function() {
          $(this).css("opacity", Math.round($(this).pvisible()) / 100);
        });
      });
    
    });
    html,
    body {
      width: 100%;
      height: 100%;
    }
    body {
      background-color: rgba(255, 191, 127, 1);
    }
    div {
      width: 60%;
      height: 30%;
      margin: 5% auto;
      background-color: rgba(175, 153, 131, 1);
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>

    A little illustration to help understand how it works:

    0 讨论(0)
  • 2020-12-29 10:01

    Please note that the Intersection Observer API is available since then, made specifically for this purpose.

    0 讨论(0)
  • 2020-12-29 10:05

    Chrome now supports Intersection Observer API

    Example (TypeScript):

    export const elementVisibleInPercent = (element: HTMLElement) => {
        return new Promise((resolve, reject) => {
            const observer = new IntersectionObserver((entries: IntersectionObserverEntry[]) => {
                entries.forEach((entry: IntersectionObserverEntry) => {
                    resolve(Math.floor(entry.intersectionRatio * 100));
                    clearTimeout(timeout);
                    observer.disconnect();
                });
            });
    
            observer.observe(element);
            // Probably not needed, but in case something goes wrong.
            const timeout = setTimeout(() => {
                reject();
            }, 500);
        });
    };
    
    const example = document.getElementById('example');
    const percentageVisible = elementVisibleInPercent(example);
    

    Example (JavaScript):

    export const elementVisibleInPercent = (element) => {
        return new Promise((resolve, reject) => {
            const observer = new IntersectionObserver(entries => {
                entries.forEach(entry => {
                    resolve(Math.floor(entry.intersectionRatio * 100));
                    clearTimeout(timeout);
                    observer.disconnect();
                });
            });
    
            observer.observe(element);
            // Probably not needed, but in case something goes wrong.
            const timeout = setTimeout(() => {
                reject();
            }, 500);
        });
    };
    
    const example = document.getElementById('example');
    const percentageVisible = elementVisibleInPercent(example);
    
    0 讨论(0)
  • 2020-12-29 10:09

    See one more example in fiddle: https://jsfiddle.net/1hfxom6h/3/

    /*jslint browser: true*/
    /*global jQuery, window, document*/
    (function ($) {
        'use strict';
        var results = {};
    
        function display() {
            var resultString = '';
    
            $.each(results, function (key) {
                resultString += '(' + key + ': ' + Math.round(results[key]) + '%)';
            });
    
            $('p').text(resultString);
        }
    
        function calculateVisibilityForDiv(div$) {
            var windowHeight = $(window).height(),
                docScroll = $(document).scrollTop(),
                divPosition = div$.offset().top,
                divHeight = div$.height(),
                hiddenBefore = docScroll - divPosition,
                hiddenAfter = (divPosition + divHeight) - (docScroll + windowHeight);
    
            if ((docScroll > divPosition + divHeight) || (divPosition > docScroll + windowHeight)) {
                return 0;
            } else {
                var result = 100;
    
                if (hiddenBefore > 0) {
                    result -= (hiddenBefore * 100) / divHeight;
                }
    
                if (hiddenAfter > 0) {
                    result -= (hiddenAfter * 100) / divHeight;
                }
    
                return result;
            }
        }
    
        function calculateAndDisplayForAllDivs() {
            $('div').each(function () {
                var div$ = $(this);
                results[div$.attr('id')] = calculateVisibilityForDiv(div$);
            });
    
            display();
        }
    
        $(document).scroll(function () {
            calculateAndDisplayForAllDivs();
        });
    
        $(document).ready(function () {
            calculateAndDisplayForAllDivs();
        });
    }(jQuery));
    div {
        height:200px;
        width:300px;
    
        border-width:1px;
        border-style:solid;
    }
    p {
        position: fixed;
        left:320px;
        top:4px;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div id="div1">div1</div>
    <div id="div2">div2</div>
    <div id="div3">div3</div>
    <div id="div4">div4</div>
    <p id="result"></p>

    0 讨论(0)
  • 2020-12-29 10:10

    Here's a snippet illustrating how you can calculate this.

    I've put the % values in the boxes for readability, and it even kinda "follows" the viewport ^^ :

    Fiddle version

    function listVisibleBoxes() {
    
      var results = [];
    
      $("section").each(function () {
    
        var screenTop = document.documentElement.scrollTop;
        var screenBottom = document.documentElement.scrollTop + $(window).height();
        var boxTop = $(this).offset().top;
        var boxHeight = $(this).height();
        var boxBottom = boxTop + boxHeight;
    
        if(boxTop > screenTop) {
          if(boxBottom < screenBottom) {
            //full box
            results.push(this.id + "-100%");
            $(this).html("100%").css({ "line-height": "50vh" });
          } else if(boxTop < screenBottom) {
            //partial (bottom)
            var percent = Math.round((screenBottom - boxTop) / boxHeight * 100) + "%";
            var lineHeight = Math.round((screenBottom - boxTop) / boxHeight * 50) + "vh";
            results.push(this.id + "-" + percent);
            $(this).html(percent).css({ "line-height": lineHeight });
          }
        } else if(boxBottom > screenTop) {
          //partial (top)
          var percent = Math.round((boxBottom - screenTop) / boxHeight * 100) + "%";
          var lineHeight = 100 - Math.round((boxBottom - screenTop) / boxHeight * 50) + "vh";
          results.push(this.id + "-" + percent);
          $(this).html(percent).css({ "line-height": lineHeight });
        }
      });
    
      $("#data").html(results.join(" | "));
    
    }
    
    $(function () {
    
      listVisibleBoxes();
    
      $(window).on("scroll", function() {
        listVisibleBoxes();
      });
    
    });
    body {
      background-color: rgba(255, 191, 127, 1);
      font-family: Arial, sans-serif;
    }
    
    section {
      background-color: rgba(175, 153, 131, 1);
      height: 50vh;
      font-size: 5vh;
      line-height: 50vh;
      margin: 10vh auto;
      overflow: hidden;
      text-align: center;
      width: 50vw;
    }
    
    #data {
      background-color: rgba(255, 255, 255, .5);
      left: 0;
      padding: .5em;
      position: fixed;
      top: 0;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <section id="one"></section>
    <section id="two"></section>
    <section id="three"></section>
    <section id="four"></section>
    <section id="five"></section>
    <section id="six"></section>
    
    <div id="data">data here</div>

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