Detect percent on screen of an element - javascript

I'm trying to detect what % of the element can be seen on the current window.
For example, if the user can only see half the element, return 50. If the user can see the whole element, return 100.
Here's my code so far:
function getPercentOnScreen() {
var $window = $(window),
viewport_top = $window.scrollTop(),
viewport_height = $window.height(),
viewport_bottom = viewport_top + viewport_height,
$elem = $(this),
top = $elem.offset().top,
height = $elem.height(),
bottom = top + height;
return (bottom - viewport_top) / height * 100;
}
But it doesn't seem to be working. Can anyone help me out in achieveing this I seem to be spinning gears.

What you want to get is the amount of pixels that the element extends past the top and bottom of the viewport. Then you can just subtract it from the total height and divide by that height to get the percentage onscreen.
var px_below = Math.max(bottom - viewport_bottom, 0);
var px_above = Math.max(viewport_top - top, 0);
var percent = (height - px_below - px_above) / height;
return percent;
One thing to note is that jQuery's height method won't include padding. You probably want to use .outerHeight for that.

Your $elem = $(this)assignment seems wrong, here function scoping means this refers to the function you're in (ala ~ the function getPercentOnScreen), try referencing by $elem = $('#yourElementId')instead.

if you only want to calculate percent of element then just do this
function getPercentOnScreen(elem) {
$docHeight = $(document).height();
$elemHeight = $(elem).height();
return ($elemHeight/$docHeight)* 100;
}

Related

How can I calculate scroll percentage of a div instead of whole page with JS?

I found the following code used to track the scroll percentage as you scroll down the page.
document.addEventListener('scroll', function(){
var h = document.documentElement,
b = document.body,
st = 'scrollTop',
sh = 'scrollHeight';
var percent = (h[st]||b[st]) / ((h[sh]||b[sh]) - h.clientHeight) * 100;
document.getElementById('scroll').textContent = 'scrolled: ' + percent + '%';
});
That shows the percentage of the whole page. If I only want the scroll percentage of a specific div like this example https://www.thefarmersdog.com/ how the dog bowl scrolls over when you move down the page.
You need to use getBoundingClientRect()
const coordinates = document.getElementById('your-id').getBoundingClientRect();
console.log(coordinates.top); // This is the distance to the top of the screen
Here is a detailed article that you can read if you need more info.
Divide number of pixels that an element's content has been scrolled vertically by the height of an element's content minus an element's offset height and multiply by 100 to get percentage value from 0 to 100. Optionally, round it to get an integer value.
scrollPercent = scrollTop / (scrollHeight - offsetHeight)
const scrollLabelElement = document.getElementById('scroll');
element.addEventListener('scroll', (_event) =>
const percentScrolled = Math.round(element.scrollTop / (element.scrollHeight - element.offsetHeight));
scrollLabelElement.textContent = `Scrolled: ${percentScrolled}%`;
});

Get percent scrolled through an element jquery

I can't seem to figure out how to calculate this value
While scrolling down a page, I would like to make a function that return the percertage of 'scrolled through'.
So my 0 would be when the element is on the cusp of being shown (it's at the bottom of the window, 0 pixels shown) and 100 being when the element is completely passed (the element is over the top of the window, 0 pixels shown).
I would need that to do parralax with custom animations, to make an animation that starts when the element is shown, and animates through until the element is gone.
EDIT: All Paralax plugins I see seem to force you in premade animations. I'd like to animate my own thing, so thats why I need that percent value.
After asking the question, I didn't just sit on my hands and continued working on this. This seems to do what I wish to do.
function ScrollPercent(jQEl){
var currY = $('html').scrollTop();
var elH = $(jQEl).height();
var elTop = $(jQEl).offset();
elTop = elTop.top;
var fullH = $('html').height();
var zero = elTop-elH;
var hundred = elTop+$( window ).height();
var scrollPercent = (currY-zero)/(hundred-zero);
return scrollPercent;
}
I've never used jquery. But I can help you with this function.
function ScrollPercent(selector) {
var currY = document.documentElement.scrollTop;
var elH = document.querySelector(selector).offsetHeight;
var elTop = document.querySelector(selector).offsetTop;
var fullH = document.documentElement.scrollHeight;
var zero = elTop - elH;
var hundred = elTop + window.innerHeight;
var scrollPercent = (currY - zero) / (hundred - zero);
return scrollPercent;
}

Fill window with divs. Div pixel height displayed incorrectly in google chrome. Width works

I want to fill the window size with divs. For a specified div size in px, the screen will be filled as much as it can be, leaving a remainder edge amount of px on the side and bottom. This remainder amount is then divided by the number of cells in the row (or column) and that is then added to the height (or width) of each cell in the row (or column).
For the width this works perfectly but when the same logic is applied to the height, it breaks. Both width and height work in firefox.
Screenshot: http://i.imgur.com/mpDCM0G.png
JSfiddle of making the divs: https://jsfiddle.net/xb82c4zt/
Live: http://conwaygameoflife.heroku.com/
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var size = 100;
// Calculate the number of cells we can fit in the width
//and height (there will be extra space)
w = Math.floor(windowWidth / size);
h = Math.floor(windowHeight / size);
// Calculate the extra space
var widthDiff = windowWidth % size;
var heightDiff = windowHeight % size;
// Add the needed amount of height and width to each cell to fill the window
var widthSize = size + widthDiff / w;
var heightSize = size + heightDiff / h;
// Begin to alter the DOM
var parentDiv = document.createElement('div');
parentDiv.className = 'grid';
for(var y = 0; y < h; y++) {
for(var x = 0; x < w; x++) {
var cellDiv = document.createElement('div')
cellDiv.className = 'cellDiv'
cellDiv.style.height = heightSize + 'px';
cellDiv.style.width = widthSize + 'px';
parentDiv.appendChild(cellDiv)
}
}
document.body.appendChild(parentDiv)
In Chrome (and probably other browsers), height and width pixel values are truncated! See this stackoverflow answer with the related jsFiddle
Precentage values are truncated too, but not as severely. So, to solve this you can convert pixels to percentages as I did in this jsFiddle.
The main thing I added was:
var widthPercent = widthSize / windowWidth * 100;
var heightPercent = heightSize / windowHeight * 100;
Because we're using percentages now, the parent container must have width/height:
parentDiv.style.height = windowHeight + 'px';
parentDiv.style.width = windowWidth + 'px';
And changed the loop to:
for(var x = 0; x < w*h; x++) {
var cellDiv = document.createElement('div');
cellDiv.className = 'cellDiv';
cellDiv.style.height = heightPercent + '%';
cellDiv.style.width = widthPercent + '%';
parentDiv.appendChild(cellDiv)
}
Now this doesn't always work in chrome perfectly. However, it does make it perfect in some cases... basically depends on when (and how drastic) the truncation of percentages is.
After further reflection, it looks like percentages get resolved to fractional pixel values as well... which still get truncated in Chrome. So, let's make our math better, and figure out the biggest non-fractional pixel value we can use... it's actually really easy. See here
Basically, we just floor the values, then center the grid so that we can make it look nice.
edit: wasn't very happy with this answer, so screwed with it some more. Added a function that found the closest multiple of window size and made it so that it would prefer that number. Makes it work in most screen sizes, and has a fallback to the percentage method if it doesn't perfectly work. See here. However, because it relies on a recursive (naive) algorithm to find the closest multiple, it's really easy to screw your browser performance. Limiting to only 5-10 pixels of search space helps. The gist of it:
function closestMultiple(width, size, n, limit) {
if(n > limit) {
return {m: width/size, s:size};
}
if((width % (size+n)) == 0) {
return {m: width / (size+n), s: size+n};
} else if((width % (size-n)) == 0) {
return {m: width / (size-n), s: size-n};
}
return closestMultiple(width, size, n+1, limit);
}
It's very naive and ignores things like "an odd width will never be divisible by an even number"... so there's a ton of room for improvement. Check out this discussion and this discussion for more on this.

How to center a div vertically between two div's? jQuery?

I would say that I'm great with HTML and CSS, it's when I start to touch JavaScript that I struggle; I can understand some of it but I couldn't write it for my life! I'm trying to learn though, any ways; I've put together this jQuery script with the goal to vertically center a div between a relative positioned top div, and an absolute positioned bottom div. It's not working out for me, lol.
Because I'm trying to learn how to work with jQuery and create my own scripts, I'd like to try and get this working. However, if there is a better way to accomplish my goal, I would greatly appreciate your input in that way as well!
<script type="text/javascript">
$(document).ready(function() {
$(window).ready(function(){
vertical_height()
});
$(window).resize(function(){
vertical_height()
});
function vertical_height(){
var doc_height = $(document).height();
var head_height = $(".headcontent").height();
var mid_height = $(".midcontent").height();
var foot_height = $(".footer").height();
var pos_height = Math.round(head_height+foot_height);
var neg_height = Math.round((head_height-foot_height)/2);
var fin_height = Math.round(doc_height-(pos_height-neg_height));
$('.midcontent').css({
"marginTop","-"+fin_height+"px",
"marginBottom","-"+fin_height+"px"
}
}
});
</script>
.headcontent is the top div.
.midcontent is the middle div that I'd like to center.
.footer is the bottom div.
Here's your code, tidied into something that will run :
$(function() {
function vertical_height() {
var doc_height = $(document).height();
var head_height = $(".headcontent").height();
//var mid_height = $(".midcontent").height();//not used
var foot_height = $(".footer").height();
var pos_height = head_height + foot_height;
var neg_height = (head_height - foot_height) / 2;
var fin_height = doc_height - (pos_height - neg_height);
$('.midcontent').css({
"marginTop": "-" + fin_height + "px",
"marginBottom": "-" + fin_height + "px"
});
}
$(window).resize(vertical_height).trigger('resize');
});
Now you can concentrate on getting the algorithm right.
As it stands, combining and simplifying expressions, I get :
var fin_height = doc_height - head_height*3/2 - foot_height*3/2;
which doesn't look right to me but I could be wrong.
EDIT
To avoid overlap, try this version :
var $$ = {}; //jQuery object cache.
$$.w = $(window);
$$.h = $(".headcontent");
$$.m = $(".midcontent");
$$.f = $(".footer");
function vertical_height() {
var w = $$.w.height(),
h = $$.h.outerHeight(true),
m = $$.m.outerHeight(),
f = $$.f.outerHeight(true),
fin_height = Math.max(0, (w - h - m - f) / 2);
$$.m.css('marginTop', Math.floor(fin_height)).css('marginBottom', Math.ceil(fin_height));
$$.h.text(fin_height);
};
$(window).on('resize', vertical_height).trigger('resize');
DEMO
Notes
position:absolute removed from the footer's CSS
jQuery objects now cached in $$ to give the resize handler less work to do.
Calculation now based on window height not document height.
The three divs now measured with .outerHeight(false) to include vertical padding and borders.
Overlap is prevented by Math.max(0, ...), which ensures fin-height can't go negative.
The statement $$.h.text(fin_height); is for debugging and can be removed.
EDIT 2
The following code will "debounce" the resize event.
Replace :
$(window).on('resize', vertical_height).trigger('resize');
with :
var do_resize;
$(window).on('resize', function(){
clearTimeout(do_resize);
do_resize = setTimeout(vertical_height, 100);
}).trigger('resize');
Just a little tip when calculating something, never round until you get to your final number.
h = Header Height
f = Footer Height
m = Middle Height
d = Document Height
s = Height of the space above or below the div
Here we assume the height of the document is equal to that of all the other elements combined. Since there is a space above and below the middle div that is equal we have two spaces.
d = h + f + m + 2s
d - h - m -f = 2s
(d - h - m - f) / 2 = s
Let's put this into some javascript
$(document).ready(function() {
$(window).resize(vertical_height());
function vertical_height() {
var doc_height = $(document).height();
var head_height = $(".headcontent").height();
var mid_height = $(".midcontent").height();
var foot_height = $(".footer").height();
var fin_height = Math.round((doc_height - head_height - mid_height - foot_height) / 2);
$('.midcontent').css("margin-top", fin_height)
}
});
For the css we only need to add a top margin as this will push it away from the header because I am assuming the absolutely positioned footer will be stuck to the bottom.
Here is a working fiddle: http://jsfiddle.net/nBUnt/14/

offsetHeight and offsetWidth calculating incorrectly on first onclick event, not second

I have written the following script to display a hidden element, then fix it's position to the center of the page.
function popUp(id,type) {
var popUpBox = document.getElementById(id);
popUpBox.style.position = "fixed";
popUpBox.style.display = "block";
popUpBox.style.zIndex = "6";
popUpBox.style.top = "50%";
popUpBox.style.left = "50%";
var height = popUpBox.offsetHeight;
var width = popUpBox.offsetWidth;
var marginTop = (height / 2) * -1;
var marginLeft = (width / 2) * -1;
popUpBox.style.marginTop = marginTop + "px";
popUpBox.style.marginLeft = marginLeft + "px";
}
When this function is called by an onclick event, the offsetHeight and offsetWidth are calculated incorrectly, thus not centering the element correctly. If I click the onclick element a second time, the offsetHeight and offsetWidth calculate correctly.
I have tried changing the order in every way I can imagine, and this is driving me crazy! Any help is very much appreciated!
I am guessing your height and width are not defined on the parent. See this fiddle where it works fine. Boy I'm smart. http://jsfiddle.net/mrtsherman/SdTEf/1/
Old Answer
I think this can be done a lot more simply. You are setting the top and left properties to 50%. This will place the fixed element slight off from the center. I think you are then trying to pull it back into the correct position using negative margins. Instead - just calculate the correct top/left values from the start and don't worry about margin. Here is a jQuery solution, but it can be easily adapted to plain js. I also think your current code won't work if the window has been scrolled at all.
//this code will center the following element on the screen
$('#elementid').click(function() {
$(this).css('position','fixed');
$(this).css('top', (($(window).height() - $(this).outerHeight()) / 2) + $(window).scrollTop() + 'px');
$(this).css('left', (($(window).width() - $(this).outerWidth()) / 2) + $(window).scrollLeft() + 'px');
});

Categories