Automaticly scrollTo closest div - javascript

I'm building a single page ScrollTo website with 4 div's. These divs represent my pages.
Home -> My work -> About me -> Contact
The width and hight are defined by a small piece of javascript that reads the users screen resolution on bodyload or resize. So the divs are always the inner-width and height of the users screen.
function resize() {
document.getElementById("home").style.height = viewportheight+"px";
document.getElementById("work").style.height = viewportheight+"px";
document.getElementById("about").style.height = viewportheight+"px";
document.getElementById("contact").style.height = viewportheight+"px";
What I'm trying to accomplish is that once the user scrolls (let's say 100px down or up), the window automaticly snaps to the top of the nearest div.
Something like:
onScroll("100px") up or down { scrollTo("closest #div") };

The methods you can use to start here are:
//OnScroll:
$(window).scroll(function(){
//Get current scoll position:
var iSrollT = $(document).scrollTop();
//Get the position of your element:
var iOffT = $('#home').offset().top;
});
//Set scroll top using an animation:
$('html, body').animate({
scrollTop: iOffT
}, 300);
But you will have to implement more ... e.g. prevent that the scoll position always snaps to the next div and scolling is no more possible.

var STELLARJS = {
init: function() {
var self = this;
$(function(){
self.$sections = $('#landing_page, #work, #about, #contact').each(function(index){
$(this).data('sectionIndex', index);
});
self.handleEvents();
});
},
handleEvents: function() {
var self = this,
//Debounce function from Underscore.js
debounce = function(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
}
},
handleScroll = function() {
var scrollTop = $(window).scrollTop(),
sectionIndex = Math.round((scrollTop) / self.$sections.first().outerHeight()),
$activeSection = self.$sections.eq(sectionIndex);
if ($activeSection.length === 0) {
$activeSection = self.$sections.last();
}
if ($activeSection.length === 0) return;
$(window).unbind('scroll.stellarsite');
if (scrollTop === 0) {
$(window).unbind('scroll.stellarsite').bind('scroll.stellarsite', debounce(handleScroll, 500));
} else {
$('html,body').animate({
scrollTop: $activeSection.offset().top
}, 600, 'easeInOutExpo', function() {
setTimeout(function(){
$(window).unbind('scroll.stellarsite').bind('scroll.stellarsite', debounce(handleScroll, 500));
}, 10);
});
}
$(window).bind('mousewheel', function(){
$('html,body').stop(true, true);
});
$(document).bind('keydown', function(e){
var key = e.which;
if (key === 37 || key === 39) {
$('html,body').stop(true, true);
}
});
};
if (window.location.href.indexOf('#show-offset-parents-default') === -1) {
$(window).bind('scroll.stellarsite', debounce(handleScroll, 500));
}
} });

Related

jQuery - How to make sticky header return to default state on top of the page without scroll event?

I have sticky header and a stats counter on my page, and when fires if i return to top of the page, header won't return to default.
Here is the code
//here is counter
$(function () {
var screenSize = $(window).height();
var scrollHeight = ( screenSize ) + 100;
function comma(num) {
var parts = num.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
};
var fx = function fx() {
$(".stat-number").each(function (i, el) {
var data = parseInt(this.dataset.n, 10);
var props = {
"from": {
"count": 0
},
"to": {
"count": data
}
};
$(props.from).animate(props.to, {
duration: 1000 * 1,
step: function (now, fx) {
$(el).text(comma(Math.ceil(now)));
},
complete:function() {
if (el.dataset.sym !== undefined) {
el.textContent = el.textContent.concat(el.dataset.sym)
}
}
});
});
};
var reset = function reset() {
console.log($(this).scrollTop())
if ($(this).scrollTop() > scrollHeight) {
$(this).off("scroll");
fx()
}
};
$(window).on("scroll", reset);
});
//here is sticky header
$(function () {
$(window).scroll(function(){
if ($(window).scrollTop() > 2) {
$('.header').addClass('fixed-head');
} else {
$('.header').removeClass('fixed-head');
}
});
});
I think the problem is they both use 'scroll' event, and counter have this line
$(this).off("scroll");
So my question is, is there way to check if user is on top of the page without 'scroll' event?

jQuery - Repositioning links when window is resized

When the screen is resized I'm putting the sub-nav links into the 'More ul' #MoreList
This works fine but when the window is re-expanded the links are staying inside the #MoreList and I'm trying to get them to go to their original positions when there is enough space available
I've tried a few things but struggling pretty bad and just wondering if anyone knows of a way I could achieve this?
https://jsfiddle.net/yvw93k1n/
function SubNav() {
'use strict';
var $elements = {
body: $('body'),
header: $('header'),
subMenu: $('#SubNav'),
subList: $('.sub-nav__list'),
subItem: $('.sub-nav__list-item'),
subTitle: $('.sub-nav__section-title'),
moreList: $('#MoreList'),
moreItem: $('#MoreItem'),
exclude: $('.js-nav-exclude'),
hashLink: $('a[href*=#]:not([href=#])')
},
$variables = {
totalWidth: 0,
navWidth: 0,
freeSpace: 0,
moreItem: null,
reqSpace: null,
currentOffset: 0,
scrollby: 0,
bodyWidth: 0
},
overflow = true,
moveItem = null,
freeSpace = 0,
firstItem = 0,
target = null;
// Move links into more list
function removeLink(){
moveItem = $elements.moreItem.prev('li');
$elements.moreItem.addClass('is-visible');
$elements.moreList.prepend(moveItem);
}
// Check current free space in sub nav
function checkSpace() {
if ($elements.subTitle.length) {
freeSpace = $elements.subTitle.outerWidth(true);
}
freeSpace = $elements.subMenu.width() - (freeSpace + $elements.subList.outerWidth(true));
firstItem = $elements.moreList.first('li').outerWidth(true);
if (freeSpace > firstItem) {
moveItem = $elements.moreList.first('li');
}
}
// Get current overflow
function getOverflow() {
if ($elements.subMenu.get(0).scrollHeight > $elements.subMenu.get(0).offsetHeight || $elements.subMenu.get(0).scrollWidth > $elements.subMenu.get(0).offsetWidth) {
removeLink();
} else {
overflow = false;
checkSpace();
}
return overflow;
}
// Check for overflow and move links
function checkWidth() {
overflow = true;
while (overflow === true) {
getOverflow();
}
}
// Smooth scrolling functionality
function smoothScroll() {
$elements.hashLink.click(function () {
if (location.pathname.replace(/^\//, '') === this.pathname.replace(/^\//, '') && location.hostname === this.hostname) {
// Get target
target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
if (target.length) {
// Disable pointer events to stop mega menu from displaying
$elements.header.css('pointer-events', 'none');
// Scroll to target
$elements.body.animate({
scrollTop: target.offset().top - 96
}, 1000);
// Reset hidden header
setTimeout(function () {
$elements.body.addClass('hide-offscreen');
}, 1000);
// Re-enable pointer events
setTimeout(function () {
$elements.header.css('pointer-events', 'all');
}, 1200);
return false;
}
}
});
}
this.init = function () {
smoothScroll();
if ($elements.subMenu.length) {
$(window).load(function () {
checkWidth();
});
$(window).resize(function () {
checkWidth();
});
}
};
}
var subNav = new SubNav();
subNav.init();
it looks like you're checking for
if (freeSpace > firstItem) {
moveItem = $elements.moreList.first('li');
}
so why not check if
if (freeSpace < firstItem) {
moveItem = yourDesiredElement
}

SinglePage navigation

My website uses onePageNav and scrollTo jquery code. I would like to insert code which correct my position on the website when i click on the same link in the navigation (i scroll mouse up or down but current button (hover) in navigation has not moved yet on to the next one).
OnePageNav code:
;
(function ($, window, document, undefined) {
// our plugin constructor
var OnePageNav = function (elem, options) {
this.elem = elem;
this.$elem = $(elem);
this.options = options;
this.metadata = this.$elem.data('plugin-options');
this.$nav = this.$elem.find('a');
this.$win = $(window);
this.sections = {};
this.didScroll = false;
this.$doc = $(document);
this.docHeight = this.$doc.height();
};
// the plugin prototype
OnePageNav.prototype = {
defaults: {
currentClass: 'current',
changeHash: true,
easing: 'swing',
filter: '',
scrollSpeed: 750,
scrollOffset: 0,
scrollThreshold: 0.5,
begin: false,
end: false,
scrollChange: false
},
init: function () {
var self = this;
// Introduce defaults that can be extended either
// globally or using an object literal.
self.config = $.extend({}, self.defaults, self.options, self.metadata);
//Filter any links out of the nav
if (self.config.filter !== '') {
self.$nav = self.$nav.filter(self.config.filter);
}
//Handle clicks on the nav
self.$nav.on('click.onePageNav', $.proxy(self.handleClick, self));
//Get the section positions
self.getPositions();
//Handle scroll changes
self.bindInterval();
//Update the positions on resize too
self.$win.on('resize.onePageNav', $.proxy(self.getPositions, self));
return this;
},
adjustNav: function (self, $parent) {
self.$elem.find('.' + self.config.currentClass).removeClass(self.config.currentClass);
$parent.addClass(self.config.currentClass);
},
bindInterval: function () {
var self = this;
var docHeight;
self.$win.on('scroll.onePageNav', function () {
self.didScroll = true;
});
self.t = setInterval(function () {
docHeight = self.$doc.height();
//If it was scrolled
if (self.didScroll) {
self.didScroll = false;
self.scrollChange();
}
//If the document height changes
if (docHeight !== self.docHeight) {
self.docHeight = docHeight;
self.getPositions();
}
}, 250);
},
getHash: function ($link) {
return $link.attr('href').split('#')[1];
},
getPositions: function () {
var self = this;
var linkHref;
var topPos;
var $target;
self.$nav.each(function () {
linkHref = self.getHash($(this));
$target = $('#' + linkHref);
if ($target.length) {
topPos = $target.offset().top;
self.sections[linkHref] = Math.round(topPos) - self.config.scrollOffset;
}
});
},
getSection: function (windowPos) {
var returnValue = null;
var windowHeight = Math.round(this.$win.height() * this.config.scrollThreshold);
for (var section in this.sections) {
if ((this.sections[section] - windowHeight) < windowPos) {
returnValue = section;
}
}
return returnValue;
},
handleClick: function (e) {
var self = this;
var $link = $(e.currentTarget);
var $parent = $link.parent();
var newLoc = '#' + self.getHash($link);
if (!$parent.hasClass(self.config.currentClass)) {
//Start callback
if (self.config.begin) {
self.config.begin();
}
//Change the highlighted nav item
self.adjustNav(self, $parent);
//Removing the auto-adjust on scroll
self.unbindInterval();
//Scroll to the correct position
$.scrollTo(newLoc, self.config.scrollSpeed, {
axis: 'y',
easing: self.config.easing,
offset: {
top: -self.config.scrollOffset
},
onAfter: function () {
//Do we need to change the hash?
if (self.config.changeHash) {
window.location.hash = newLoc;
}
//Add the auto-adjust on scroll back in
self.bindInterval();
//End callback
if (self.config.end) {
self.config.end();
}
}
});
}
e.preventDefault();
},
scrollChange: function () {
var windowTop = this.$win.scrollTop();
var position = this.getSection(windowTop);
var $parent;
//If the position is set
if (position !== null) {
$parent = this.$elem.find('a[href$="#' + position + '"]').parent();
//If it's not already the current section
if (!$parent.hasClass(this.config.currentClass)) {
//Change the highlighted nav item
this.adjustNav(this, $parent);
//If there is a scrollChange callback
if (this.config.scrollChange) {
this.config.scrollChange($parent);
}
}
}
},
unbindInterval: function () {
clearInterval(this.t);
this.$win.unbind('scroll.onePageNav');
}
};
OnePageNav.defaults = OnePageNav.prototype.defaults;
$.fn.onePageNav = function (options) {
return this.each(function () {
new OnePageNav(this, options).init();
});
};
})(jQuery, window, document);
$(function () { // this is the shorthand for document.ready
$(window).scroll(function () { // this is the scroll event for the document
scrolltop = $(window).scrollTop(); // by this we get the value of the scrolltop ie how much scroll has been don by user
if (parseInt(scrolltop) >= 80) // check if the scroll value is equal to the top of navigation
{
$("#navbar").css({
"position": "fixed",
"top": "0"
}); // is yes then make the position fixed to top 0
} else {
$("#navbar").css({
"position": "absolute",
"top": "80px"
}); // if no then make the position to absolute and set it to 80
}
}); //here
}); //here

check if the page is on the top of the window

I need to check if an html page is on the top of the window or not.
So, i am using this code:
$(window).scroll(function(){
a = ($(window).scrollTop());
if (a>0) {
alert('page not in top');
}
});
But this is not working as expected because the event should be fired only when the user stops the scroll action. Any idea?
Try this:
var timer = null;
$(window).addEventListener('scroll', function() {
if(timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(function() {
// do something
}, 150);
}, false);
Or this one:
var timer;
$(window).bind('scroll',function () {
clearTimeout(timer);
timer = setTimeout( refresh , 150 );
});
var refresh = function () {
// do stuff
console.log('Stopped Scrolling');
};
Use setTimeout:
var timeout;
$(window).scroll(function() {
clearTimeout(timeout);
timeout = setTimeout(function(){
a = $(window).scrollTop();
if ( a > 0 ) {
alert('page not in top');
}
}, 100);
});

How do I know when I've stopped scrolling?

How do I know when I've stopped scrolling using Javascript?
You can add an event handler for the scroll event and start a timeout. Something like:
var timer = null;
window.addEventListener('scroll', function() {
if(timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(function() {
// do something
}, 150);
}, false);
This will start a timeout and wait 150ms. If a new scroll event occurred in the meantime, the timer is aborted and a new one is created. If not, the function will be executed. You probably have to adjust the timing.
Also note that IE uses a different way to attach event listeners, this should give a good introduction: quirksmode - Advanced event registration models
There isn't a "Stopped Scrolling" event. If you want to do something after the user has finished scrolling, you can set a timer in the "OnScroll" event. If you get another "OnScroll" event fired then reset the timer. When the timer finally does fire, then you can assume the scrolling has stopped. I would think 500 milliseconds would be a good duration to start with.
Here's some sample code that works in IE and Chrome:
<html>
<body onscroll="bodyScroll();">
<script language="javascript">
var scrollTimer = -1;
function bodyScroll() {
document.body.style.backgroundColor = "white";
if (scrollTimer != -1)
clearTimeout(scrollTimer);
scrollTimer = window.setTimeout("scrollFinished()", 500);
}
function scrollFinished() {
document.body.style.backgroundColor = "red";
}
</script>
<div style="height:2000px;">
Scroll the page down. The page will turn red when the scrolling has finished.
</div>
</body>
</html>
Here's a more modern, Promise-based solution I found on a repo called scroll-into-view-if-needed
Instead of using addEventListener on the scroll event it uses requestAnimationFrame to watch for frames with no movement and resolves when there have been 20 frames without movement.
function waitForScrollEnd () {
let last_changed_frame = 0
let last_x = window.scrollX
let last_y = window.scrollY
return new Promise( resolve => {
function tick(frames) {
// We requestAnimationFrame either for 500 frames or until 20 frames with
// no change have been observed.
if (frames >= 500 || frames - last_changed_frame > 20) {
resolve()
} else {
if (window.scrollX != last_x || window.scrollY != last_y) {
last_changed_frame = frames
last_x = window.scrollX
last_y = window.scrollY
}
requestAnimationFrame(tick.bind(null, frames + 1))
}
}
tick(0)
})
}
With async/await and then
await waitForScrollEnd()
waitForScrollEnd().then(() => { /* Do things */ })
(function( $ ) {
$(function() {
var $output = $( "#output" ),
scrolling = "<span id='scrolling'>Scrolling</span>",
stopped = "<span id='stopped'>Stopped</span>";
$( window ).scroll(function() {
$output.html( scrolling );
clearTimeout( $.data( this, "scrollCheck" ) );
$.data( this, "scrollCheck", setTimeout(function() {
$output.html( stopped );
}, 250) );
});
});
})( jQuery );
=======>>>>
Working Example here
I did something like this:
var scrollEvents = (function(document, $){
var d = {
scrolling: false,
scrollDirection : 'none',
scrollTop: 0,
eventRegister: {
scroll: [],
scrollToTop: [],
scrollToBottom: [],
scrollStarted: [],
scrollStopped: [],
scrollToTopStarted: [],
scrollToBottomStarted: []
},
getScrollTop: function(){
return d.scrollTop;
},
setScrollTop: function(y){
d.scrollTop = y;
},
isScrolling: function(){
return d.scrolling;
},
setScrolling: function(bool){
var oldVal = d.isScrolling();
d.scrolling = bool;
if(bool){
d.executeCallbacks('scroll');
if(oldVal !== bool){
d.executeCallbacks('scrollStarted');
}
}else{
d.executeCallbacks('scrollStopped');
}
},
getScrollDirection : function(){
return d.scrollDirection;
},
setScrollDirection : function(direction){
var oldDirection = d.getScrollDirection();
d.scrollDirection = direction;
if(direction === 'UP'){
d.executeCallbacks('scrollToTop');
if(direction !== oldDirection){
d.executeCallbacks('scrollToTopStarted');
}
}else if(direction === 'DOWN'){
d.executeCallbacks('scrollToBottom');
if(direction !== oldDirection){
d.executeCallbacks('scrollToBottomStarted');
}
}
},
init : function(){
d.setScrollTop($(document).scrollTop());
var timer = null;
$(window).scroll(function(){
d.setScrolling(true);
var x = d.getScrollTop();
setTimeout(function(){
var y = $(document).scrollTop();
d.setScrollTop(y);
if(x > y){
d.setScrollDirection('UP');
}else{
d.setScrollDirection('DOWN');
}
}, 100);
if(timer !== 'undefined' && timer !== null){
clearTimeout(timer);
}
timer = setTimeout(function(){
d.setScrolling(false);
d.setScrollDirection('NONE');
}, 200);
});
},
registerEvents : function(eventName, callback){
if(typeof eventName !== 'undefined' && typeof callback === 'function' && typeof d.eventRegister[eventName] !== 'undefined'){
d.eventRegister[eventName].push(callback);
}
},
executeCallbacks: function(eventName){
var callabacks = d.eventRegister[eventName];
for(var k in callabacks){
if(callabacks.hasOwnProperty(k)){
callabacks[k](d.getScrollTop());
}
}
}
};
return d;
})(document, $);
the code is available here: documentScrollEvents
Minor update in your answer. Use mouseover and out function.
$(document).ready(function() {
function ticker() {
$('#ticker li:first').slideUp(function() {
$(this).appendTo($('#ticker')).slideDown();
});
}
var ticke= setInterval(function(){
ticker();
}, 3000);
$('#ticker li').mouseover(function() {
clearInterval(ticke);
}).mouseout(function() {
ticke= setInterval(function(){ ticker(); }, 3000);
});
});
DEMO
I was trying too add a display:block property for social icons that was previously hidden on scroll event and then again hide after 2seconds. But
I too had a same problem as my code for timeout after first scroll would start automatically and did not had reset timeout idea. As it didn't had proper reset function.But after I saw David's idea on this question I was able to reset timeout even if someone again scrolled before actually completing previous timeout.
problem code shown below before solving
$(window).scroll(function(){
setTimeout(function(){
$('.fixed-class').slideUp('slow');
},2000);
});
edited and working code with reset timer if next scroll occurs before 2s
var timer=null;
$(window).scroll(function(){
$('.fixed-class').css("display", "block");
if(timer !== null) {
clearTimeout(timer);
}
timer=setTimeout(function(){
$('.fixed-class').slideUp('slow');
},2000);
});
My working code will trigger a hidden division of class named 'fixed-class' to show in block on every scroll. From start of latest scroll the timer will count 2 sec and then again change the display from block to hidden.
For more precision you can also check the scroll position:
function onScrollEndOnce(callback, target = null) {
let timeout
let targetTop
const startPosition = Math.ceil(document.documentElement.scrollTop)
if (target) {
targetTop = Math.ceil(target.getBoundingClientRect().top + document.documentElement.scrollTop)
}
function finish(removeEventListener = true) {
if (removeEventListener) {
window.removeEventListener('scroll', onScroll)
}
callback()
}
function isScrollReached() {
const currentPosition = Math.ceil(document.documentElement.scrollTop)
if (targetTop == null) {
return false
} else if (targetTop >= startPosition) {
return currentPosition >= targetTop
} else {
return currentPosition <= targetTop
}
}
function onScroll() {
if (timeout) {
clearTimeout(timeout)
}
if (isScrollReached()) {
finish()
} else {
timeout = setTimeout(finish, 500)
}
}
if (isScrollReached()) {
finish(false)
} else {
window.addEventListener('scroll', onScroll)
}
}
Usage example:
const target = document.querySelector('#some-element')
onScrollEndOnce(() => console.log('scroll end'), target)
window.scrollTo({
top: Math.ceil(target.getBoundingClientRect().top + document.documentElement.scrollTop),
behavior: 'smooth',
})
Here's an answer that doesn't use any sort of timer, thus in my case predicted when the scrolling actually ended, and is not just paused for a bit.
function detectScrollEnd(element, onEndHandler) {
let scrolling = false;
element.addEventListener('mouseup', detect);
element.addEventListener('scroll', detect);
function detect(e) {
if (e.type === 'scroll') {
scrolling = true;
} else {
if (scrolling) {
scrolling = false;
onEndHandler?.();
}
}
}
}

Categories

Resources