How Much Of An Element Is Visible In Viewport
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 might only partially show up or
Solution 1:
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>
Solution 2:
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 ^^ :
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>
Solution 3:
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:
Solution 4:
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);
Solution 5:
Please note that the Intersection Observer API is available since then, made specifically for this purpose.
Post a Comment for "How Much Of An Element Is Visible In Viewport"