How much of an element is visible in viewport

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>

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:

enter image description here


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>