How many items are visible in the viewport

The page has a div (brown rectangle). The page is taller than the viewport (orange rectangle), so it can be scrolled, which means that the div can only be displayed partially or not at all.

element visibility

What is the easiest algorithm to say how many% div visible in the viewport?

(To simplify the task, the div always fits horizontally into the viewport, therefore, only the Y axis should be taken into account in the calculations).

+10
source share
5 answers

See another example in the script: 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> 
+18
source

After playing a little, I think I found perhaps the easiest way to do this: I basically determine how much the element extends to the viewing area (no matter which direction), and based on this it can be easily calculated how many of them it is seen.

 // 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 small illustration to help you understand how this works:

enter image description here

+3
source

Here is a snippet illustrating how you can calculate this.

I put the% values ​​in the fields for readability, and it even kind of "follows" in the view ^^:

script 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> 
+2
source

I searched Google and this thread was the first on the list. The reason he surpassed again is because others like me in the search for how to do this check have quick information about a more modern solution: IntersectionObserver.

https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

0
source

Chrome now supports the Intersection Observer API:
https://developer.mozilla.org/en-US/docs/Web/API/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); }); 

};

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

};

0
source

All Articles