javascript detect reach bottom mobile [duplicate] - javascript

How can I find out what percentage of the vertical scrollbar a user has moved through at any given point?
It's easy enough to trap the onscroll event to fire when the user scrolls down the page, but how do I find out within that event how far they have scrolled? In this case, the percentage particularly is what's important. I'm not particularly worried about a solution for IE6.
Do any of the major frameworks (Dojo, jQuery, Prototype, Mootools) expose this in a simple cross-browser compatible way?

Oct 2016: Fixed. Parentheses in jsbin demo were missing from answer. Oops.
Chrome, Firefox, IE9+. Live Demo on jsbin
var h = document.documentElement,
b = document.body,
st = 'scrollTop',
sh = 'scrollHeight';
var percent = (h[st]||b[st]) / ((h[sh]||b[sh]) - h.clientHeight) * 100;
As function:
function getScrollPercent() {
var h = document.documentElement,
b = document.body,
st = 'scrollTop',
sh = 'scrollHeight';
return (h[st]||b[st]) / ((h[sh]||b[sh]) - h.clientHeight) * 100;
}
If you prefer jQuery (original answer):
$(window).on('scroll', function(){
var s = $(window).scrollTop(),
d = $(document).height(),
c = $(window).height();
var scrollPercent = (s / (d - c)) * 100;
console.clear();
console.log(scrollPercent);
})
html{ height:100%; }
body{ height:300%; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

I think I found a good solution that doesn't depend on any library:
/**
* Get current browser viewpane heigtht
*/
function _get_window_height() {
return window.innerHeight ||
document.documentElement.clientHeight ||
document.body.clientHeight || 0;
}
/**
* Get current absolute window scroll position
*/
function _get_window_Yscroll() {
return window.pageYOffset ||
document.body.scrollTop ||
document.documentElement.scrollTop || 0;
}
/**
* Get current absolute document height
*/
function _get_doc_height() {
return Math.max(
document.body.scrollHeight || 0,
document.documentElement.scrollHeight || 0,
document.body.offsetHeight || 0,
document.documentElement.offsetHeight || 0,
document.body.clientHeight || 0,
document.documentElement.clientHeight || 0
);
}
/**
* Get current vertical scroll percentage
*/
function _get_scroll_percentage() {
return (
(_get_window_Yscroll() + _get_window_height()) / _get_doc_height()
) * 100;
}

This should do the trick, no libraries required:
function currentScrollPercentage()
{
return ((document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight) * 100);
}

These worked for me perfectly in Chrome 19.0, FF12, IE9:
function getElementScrollScale(domElement){
return domElement.scrollTop / (domElement.scrollHeight - domElement.clientHeight);
}
function setElementScrollScale(domElement,scale){
domElement.scrollTop = (domElement.scrollHeight - domElement.clientHeight) * scale;
}

A Typescript implementation.
function getScrollPercent(event: Event): number {
const {target} = event;
const {documentElement, body} = target as Document;
const {scrollTop: documentElementScrollTop, scrollHeight: documentElementScrollHeight, clientHeight} = documentElement;
const {scrollTop: bodyScrollTop, scrollHeight: bodyScrollHeight} = body;
const percent = (documentElementScrollTop || bodyScrollTop) / ((documentElementScrollHeight || bodyScrollHeight) - clientHeight) * 100;
return Math.ceil(percent);
}

If you're using Dojo, you can do the following:
var vp = dijit.getViewport();
return (vp.t / (document.documentElement.scrollHeight - vp.h));
Which will return a value between 0 and 1.

This question has been here for a long time, I know, but I stumbled onto it while trying to solve the same problem. Here is how I solved it, in jQuery:
First, I wrapped the thing I wanted to scroll in a div (not semantic, but it helps). Then set the overflow and height on the wrapper.
<div class="content-wrapper" style="overflow: scroll; height:100px">
<div class="content">Lot of content that scrolls</div>
</div>
Finally I was able to calculate the % scroll from these metrics:
var $w = $(this),
scroll_top = $w.scrollTop(),
total_height = $w.find(".content").height(),
viewable_area = $w.height(),
scroll_percent = Math.floor((scroll_top + viewable_area) / total_height * 100);
Here is a fiddle with working example: http://jsfiddle.net/prEGf/

Everyone has great answers, but I just needed an answer as one variable. I didn't need an event listener, I just wanted to get the scrolled percentage. This is what I got:
const scrolledPercentage =
window.scrollY / (document.documentElement.scrollHeight - document.documentElement.clientHeight)
document.addEventListener("scroll", function() {
const height = window.scrollY / (document.documentElement.scrollHeight - document.documentElement.clientHeight)
document.getElementById("height").innerHTML = `Height: ${height}`
})
.container {
position: relative;
height: 200vh;
}
.sticky-div {
position: sticky;
top: 0;
}
<!DOCType>
<html>
<head>
</head>
<body>
<div id="container" class="container">
<div id="height" class="sticky-div">
Height: 0
</div>
</div>
</body>

First attach an event listener to some document you want to keep track
yourDocument.addEventListener("scroll", documentEventListener, false);
Then:
function documentEventListener(){
var currentDocument = this;
var docsWindow = $(currentDocument.defaultView); // This is the window holding the document
var docsWindowHeight = docsWindow.height(); // The viewport of the wrapper window
var scrollTop = $(currentDocument).scrollTop(); // How much we scrolled already, in the viewport
var docHeight = $(currentDocument).height(); // This is the full document height.
var howMuchMoreWeCanScrollDown = docHeight - (docsWindowHeight + scrollTop);
var percentViewed = 100.0 * (1 - howMuchMoreWeCanScrollDown / docHeight);
console.log("More to scroll: "+howMuchMoreWeCanScrollDown+"pixels. Percent Viewed: "+percentViewed+"%");
}

My two cents, the accepted answer in a more "modern" way. Works back to IE9 using #babel/preset-env.
// utilities.js
/**
* #param {Function} onRatioChange The callback when the scroll ratio changes
*/
export const monitorScroll = onRatioChange => {
const html = document.documentElement;
const body = document.body;
window.addEventListener('scroll', () => {
onRatioChange(
(html.scrollTop || body.scrollTop)
/
((html.scrollHeight || body.scrollHeight) - html.clientHeight)
);
});
};
Usage:
// app.js
import { monitorScroll } from './utilities';
monitorScroll(ratio => {
console.log(`${(ratio * 100).toFixed(2)}% of the page`);
});

I reviewed all of these up there but they use more complex approaches to solve. I found this through a mathematical formula; brief.
The formula goes Value/Total * 100. Say Total is 200 u wanna know the percentage of 100 out of 200, you do it 100/200 * 100% = 50% (the value)
pageYOffset = The vertical scroll count without including borders. When you scroll down to bottom you get the maximum count.
offsetHeight = The total height of the page including borders!
clientHeight = The height in pixels without borders but not to the end of content!
When u scroll to bottom u get pageyoffset of 1000 for example, whereas offsetHeight of 1200 and clientHeight of 200. 1200 - 200(clientheight) now u get paggeYOffset value in offsetHeight and so scrollPosition300(300 of 1000)/1000 * 100 = 30%.
`pageOffset = window.pageYOffset;
pageHeight = document.documentElement.offsetHeight;
clientHeight = document.documentElement.clientHeight;
percentage = pageOffset / (pageHeight - clientHeight) * 100 + "%";
console.log(percentage)`
The reason why we must do offsetHeight - clientHeight it is because client heights shows all the available content in px without borders, and offsetheight shows the available content including borders, whereas pageYOffset counts the scrolls made; The scrollbar is quite long to count the whole windows it counts the scrolls itself until reaches the end, the available space in scrollbar is in px pageYOffset, so to reach that number you substract offsetHeight - clientHeight to bring to the lower value of pageYOffset.
i'll update when i get on pc, please leave a comment to make it clear so i don't forget! Thanks :)

Using jQuery
$(window).scrollTop();
will get you the scroll position, you can then work out from there what the percentage is based on the window height.
There is also a standard DOM property scrollTop that you can use like document.body.scrollTop however I'm not sure how this behaves cross-browser, I would assume if there are inconsistencies then the jQuery method accounts for these.

var maxScrollTop = messages.get(0).scrollHeight - messages.height();
var scroll = messages.scrollTop() / maxScrollTop; // [0..1]

I found a way to correct a previous answer, so it works in all cases. Tested on Chrome, Firefox and Safari.
(((document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight) || 0) * 100)

Related

can someone please explain this simple code to ? scroll indicator

this a simple code a found in the internet to create a scroll indicator but the problem is that I don't understand the logic behind it.... why subtract max Height and inner Height and after that use the result for division , and why multiplying by 100 what is the logic behind this ?
P.S. I know that :
scrollHeight is to return full height of the HTMLdocument.
innerHeight returns the height of the viewable window (with the scrollbar)
pageYoffset is the same thing as ScrollY returns number of pixels scrolled
it's a matter of why using not how to use....
const progressbar = document.querySelector('.scroll--progress');
const scroll = () => {
// return the scroll height of the entier page
const maxHeight = document.body.scrollHeight;
//return the innerheight of the ViewPort
const inner = window.innerHeight;
const a = maxHeight - innerHeight;
const t = Math.round(window.pageYOffset);
const b = (window.scrollY / a) * 100;
//setup the progress bar width
progressbar.style.width = `${b}` + '%';
};
window.addEventListener('scroll', scroll);
There's better cross-compatibility with the use of pageYOffset; I'd not use the scrollY unless there's a scenario that proves it useful. Rare browser-cases?
scrollY and pageYOffset are the same! But all browsers on MDN support pageYOffset.

Interpolating element as it scrolls through window

I'm trying to interpolate a percentage as an element moves through the window as you scroll.
Basically, when the top of the element meets the bottom of the window, that returns 0%. When the bottom of the element meets the top of the window, that returns 100%.
So far I can interpolate using the middle of the element, but I can't figure out how to get it to start at the top and bottom instead of the middle.
Heres a Demo
$(document).ready(()=>{
//Element Vars - Test for Element 3
const $interpolateTarget = $('#element3');
const elementH = $interpolateTarget.height();
const elementY = $interpolateTarget.offset().top;
//Window Vars
const windowH = $(window).height()
let scrollY;
let interpolation;
let middlePoint;
$(window).scroll((event)=>{
scrollY = $(window).scrollTop()
pctOfWindow = elementH / windowH
//Calculate middle of element and compare to middle of window
middleY = elementH / 2 + elementY - scrollY
windowMiddleY = windowH / 2
interpolation = middleY / windowMiddleY / 2
//Convert to percentage and reverse
interpolation = (interpolation * 100 - 100 ) * -1
updateInfo(scrollY, interpolation);
})
})
Any Help would be greatly appreciated, thanks!
I think you need to consider the document height instead of the window height. The following may help you.
updateInfo(scrollY, 100 * $(document).scrollTop() / ($(document).height() - $(window).height()));
$(document).scrollTop() : how much is scrolled in the entire document
$(document).height() : actual document height
$(document).height() - $(window).height() : scrollable document height
Seems I figured it out, changed elementH / 2 + elementY - scrollY to elementH + elementY - scrollY

JavaScript - Scroll down to element

You can scroll to an element using a url with a hashtag and the elements ID:
window.location.href = "#ID"
This will scrol so that the top of the element is at the top of the browser. How would I scroll to an element so that it's vertically centered?
you can scroll up right after the navigation happens:
addEventListener("hashchange", function(){
setTimeout(function(){
document[
document.documentElement.scrollTop ?
"documentElement":
"body"
].scrollTop-= (innerHeight/2.1);
}, 1);
}, false);
this will cause the focused element to appear half-way up the screen, vertically centered.
the 2.1 causes it to scroll just under half the screen, since there will be some room at the top already. you can adjust the ".1" to match your desired effect (baseline, middle, etc).
obligatory fiddle link: http://jsfiddle.net/ckhafLzq/2/
This is what I have achieved:
function centerScroll(element) {
if (!(element instanceof Element)) {
throw new TypeError("Element expected");
}
var bodyRect = document.body.getBoundingClientRect();
var elementRect = element.getBoundingClientRect();
var left = elementRect.left - bodyRect.left;
var top = elementRect.top - bodyRect.top;
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var elementWidth = element.offsetWidth;
var elementHeight = element.offsetHeight;
var x = left - Math.max(0, (windowWidth - elementWidth) / 2);
var y = top - Math.max(0, (windowHeight - elementHeight) / 2);
window.scrollTo(x, y);
return [x, y];
}
No, there's no built-in way, you'd have to write that yourself:
function center_element_vertically(elt) {
var rect = elt.getBoundingClientRect();
window.scrollTo(0, rect.top + window.pageYOffset -
(window.innerHeight - rect.height)/2);
}
Alternatives without writing your own code: you could scroll so that the element was at the bottom by passing false to scrollIntoView, or scroll only if the element is not already visible by calling scrollIntoViewIfNeeded, available only in Chrome AFAIK.

Making JavaScript Custom ScrollBar

I said “JavaScript”. I'm trying to make my own JavaScript custom scrollbar. It's almost successful. The problem is that I can't get an accurate multiplier for the scrolling speed. This is my code:
var elem = document.getElementById('scroll-area'),
track = elem.children[1],
thumb = track.children[0],
height = parseInt(elem.offsetHeight, 10),
cntHeight = parseInt(elem.children[0].offsetHeight, 10),
trcHeight = parseInt(track.offsetHeight, 10),
distance = cntHeight - height,
mean = 50, // For multiplier (go faster or slower)
current = 0;
elem.children[0].style.top = current + "px"; // Set default `top` value as `0` for initiation
thumb.style.height = Math.round(trcHeight * height / cntHeight) + 'px'; // Set the scrollbar thumb hight
var doScroll = function (e) {
// cross-browser wheel delta
e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
// (1 = scroll-up, -1 = scroll-down)
// Always check the scroll distance, make sure that the scroll distance value will not
// increased more than the content height and/or less than zero
if ((delta == -1 && current * mean >= -distance) || (delta == 1 && current * mean < 0)) {
current = current + delta;
}
// Move element up or down by updating the `top` value
elem.children[0].style.top = (current * mean) + 'px';
thumb.style.top = 0 - Math.round(trcHeight * (current * mean) / cntHeight) + 'px';
e.preventDefault();
};
if (elem.addEventListener) {
elem.addEventListener("mousewheel", doScroll, false);
elem.addEventListener("DOMMouseScroll", doScroll, false);
} else {
elem.attachEvent("onmousewheel", doScroll);
}
And the markup:
<div id="scroll-area">
<div><!-- CONTENT --></div>
<span class="scrollbar-track"><span class="scrollbar-thumb"></span></span>
</div>
My problem is on mean = 50. When you scroll the container until the bottom of content, the red line that I made in the demo page should stop right at the bottom of the container, not higher than that.
Anyone have an idea for the accurate result?
PS: I also want to add a function that will enable user to scroll the content by dragging the scrollbar thumb. But I think I want to focus on this issue first. Thanks for your help.
DEMO: http://jsfiddle.net/tovic/2B8Ye/
You could have done this much much easier.
Instead of calculating the top property for the container you can set its scrollTop property, and you guess what - you don't have to check if the content moves upper than 0 or lower than height because scrollTop property can not be set to a value smaller than 0 or greater than container's height!
Here's your modified fiddle
Notice that I had to wrap everything with additional <div id="everything">...</div> so the scrollbar won't be scrolled together with the #scroll-area div.
Also I assume using clientHeight instead of offsetHeight for .scroll-content div to include padding in the cntHeight variable.
More info on the scrollTop property you can find in this article.

"Zooming" elements on a page while keeping the centre of enlargement in the centre of the window

I'm trying to work out how to enlarge all elements on a page, but keep the centre of enlargement in the centre of the window.
On this page, once the image reaches the top or the left side of the window the centre of enlargement changes. It also changes when you move the image. (exactly what you would expect)
I'm thinking I'd need to take a completely different approach to achieve what I want. But I'm not sure what that approach is..
Any ideas?
Well, here's my take.
Only thing is that I ditched the containers you were using. Is that cheating? Seems like they were only there to get the image centered. No need.
This works as expected with no side effects.
Here's a working demo you can test:
http://jsfiddle.net/YFPRB/1/
(You need to click on the pane with the baboon first.)
HTML
<body>
<img src="http://cdn.sstatic.net/stackoverflow/img/apple-touch-icon.png" />
</body>
CSS
html, body {
width: 100%;
height: 100%;
overflow: hidden;
}​
jQuery
EDIT: Thanks to #stagas for the reminder to clean up redundancies.
var $img = $('img'); // Cache the image. Better for performance.
$img.draggable();
$img.css({left: ($('body').width() / 2) - ($img.width() / 2)})
.css({top: ($('body').height() / 2) - ($img.height() / 2)})
$(document).keydown(function(event) {
if (event.keyCode == 38) {
var adjustment = 1.25;
} else if (event.keyCode == 40) {
var adjustment = 0.8;
} else {
return;
}
var offset = $img.offset();
var width = $img.width();
var height = $img.height();
var newWidth = width * adjustment;
var newHeight = height * adjustment;
var diffWidth = newWidth - width;
var diffHeight = newHeight - height;
var hcenter = $('body').width() / 2;
var vcenter = $('body').height() / 2;
var leftPercent = (hcenter - offset.left) / width;
var topPercent = (vcenter - offset.top) / height;
$img.offset({top: offset.top - (diffHeight * topPercent), left: offset.left - (diffWidth * leftPercent)});
$img.width(newWidth).height(newHeight);
});​
This is what I came up, it works as you say except the image will always go to the center after zooming in or out:
$('document').ready(function() {
zoomimg=$('#zoomimg'); // we store this in a variable since we don't need to traverse the DOM every time -- this is faster
var viewportWidth = $(window).width();
var viewportHeight = window.innerHeight ? window.innerHeight : $(window).height(); // this is to work with Opera
zoomimg.css({'position': 'absolute', 'left': (viewportWidth/2)-(zoomimg.width()/2), 'top' : (viewportHeight/2)-(zoomimg.height()/2)}).draggable();
$(document).keydown(function(event) {
event = event || window.event;
var viewportWidth = $(window).width();
var viewportHeight = window.innerHeight ? window.innerHeight : $(window).height(); // this is to work with Opera
if (event.keyCode == 38) {
width = zoomimg.width();
height = zoomimg.height();
zoomimg.width(width*1.2).height(height*1.2);
var viewportWidth = $(window).width();
var viewportHeight = window.innerHeight ? window.innerHeight : $(window).height();
zoomimg.css({'left': (viewportWidth/2)-(zoomimg.width()/2), 'top' : (viewportHeight/2)-(zoomimg.height()/2)});
} else if (event.keyCode == 40) {
width = zoomimg.width();
height = zoomimg.height();
zoomimg.width(width*0.8).height(height*0.8);
var viewportWidth = $(window).width();
var viewportHeight = window.innerHeight ? window.innerHeight : $(window).height();
zoomimg.css({'left': (viewportWidth/2)-(zoomimg.width()/2), 'top' : (viewportHeight/2)-(zoomimg.height()/2)});
} else {
return
}
});
});
You should put an ID 'zoomimg' on the tag for it to work, and overflow:hidden on the #container . Also ditch that display:table and display:table-cell they're useless now that we center with Javascript. Also, pressing the down arrow key will cause the container to scroll down, so you should use other keys, as the arrows are reserved by the browser for scrolling the viewport.

Categories

Resources