i wrote a script that basically gives the topper element a negative margin bottom,
so it makes the element below it go up and it kind of makes a parallax like scroll, my problem is i wrote the function as a scroll and i think the function runs so much that it makes the page kind of laggy, here`s my code, i hope you guys can help me out.
$(window).on('scroll', function () {
if (scrollY >= 100) {
$("#gallery").css("margin-bottom", -((scrollY - 100) / 4));
}
});
For one, you are re-querying the DOM every time the scroll event fires. I would start by caching the selector outside of the event handler.
var $gallery = $("#gallery");
$(window).on('scroll', function () {
if (scrollY >= 100) {
$gallery.css("margin-bottom", -((scrollY - 100) / 4));
}
});
You then might want to play around with wrapping your DOM manipulations in a setTimeout.
var $gallery = $("#gallery");
var delay;
$(window).on('scroll', function () {
delay = setTimeout(function() {
if (scrollY >= 100) {
$gallery.css("margin-bottom", -((scrollY - 100) / 4));
}
}, 50);
});
EDIT:
For detecting scroll direction, try something like:
var $gallery = $("#gallery");
var delay;
var lastScrollTop = 0;
$(window).scroll(function(event){
var scrollPos = $(this).scrollTop();
delay = setTimeout(function() {
if (scrollPos > lastScrollTop){
if (scrollY >= 100) {
$gallery.css("margin-bottom", -((scrollY - 100) / 4));
}
} else {
if (scrollY < 100) {
$gallery.css("margin-bottom", -((scrollY + 100) / 4));
}
}
}, 50);
lastScrollTop = st;
});
The lag you experience is due to two main factors.
There are a huge number of Scroll events fired when a page is
scrolled
Javascript implementations are primarily single
threaded.
What can we do two fix these problems?
To fix the first issue, you can either debounce or throttle the onscroll callback function. Here is a simple but good write up about these techniques.
To fix the second problem, is to give that single thread time to do other tasks as well. If you endup using debounce or throttle, it should help or you can rely on browsers eventloop.
Basically, the idea is to tell the browser that Hey, this is something I want you to do, but take your time. The simplest ways to do that is by using setTimeout webapi.
Rewriting your implementation,
var gallery = $("#gallery");
var scrollHandler = function() {
if (scrollY >= 100) {
gallery.css("margin-bottom", -((scrollY - 100) / 4));
}
}
$(window).on('scroll', function() {
setTimeout(scrollHandler, 4)
});
Related
I want to scroll to the bottom of the div when the page loads. I am using jQuery's animate() function this. The issue is that my code is working on desktop view, but if I change it to mobile view then my code is not working.
$(".chat-msg-list").animate({
scrollTop: $(".chat-msg-list").prop("scrollHeight")
}, 1000);
jQuery:
$(".chat-msg-list").animate({
scrollTop: $(".chat-msg-list").offset().top},
}, 1000);
Javascript:
function getElementY(query) {
return window.pageYOffset + document.querySelector(query).getBoundingClientRect().top
}
function doScrolling(element, duration) {
var startingY = window.pageYOffset
var elementY = getElementY(element)
// If element is close to page's bottom then window will scroll only to some position above the element.
var targetY = document.body.scrollHeight - elementY < window.innerHeight ? document.body.scrollHeight - window.innerHeight : elementY
var diff = targetY - startingY
// Easing function: easeInOutCubic
// From: https://gist.github.com/gre/1650294
var easing = function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
var start
if (!diff) return
// Bootstrap our animation - it will get called right before next frame shall be rendered.
window.requestAnimationFrame(function step(timestamp) {
if (!start) start = timestamp
// Elapsed miliseconds since start of scrolling.
var time = timestamp - start
// Get percent of completion in range [0, 1].
var percent = Math.min(time / duration, 1)
// Apply the easing.
// It can cause bad-looking slow frames in browser performance tool, so be careful.
percent = easing(percent)
window.scrollTo(0, startingY + diff * percent)
// Proceed with animation as long as we wanted it to.
if (time < duration) {
window.requestAnimationFrame(step)
}
})
}
//Apply event handlers. Example of firing the scrolling mechanism.
doScrolling('#section1', 1000)
This is what I use to make 2 divs "unwrap" while scrolling:
CSS
.entry {
height: 40px;
}
.entry.expanded {
height:600px;
}
JavaScript
$($('.entry').get(0)).addClass('expanded');
$(window).on('scroll', function (e) {
var x = $(window).scrollTop();
if (x > 820) {
$($('.entry').get(1)).addClass('expanded');
}
if (x > 1525) {
$($('.entry').get(2)).addClass('expanded');
}
});
It works perfectly fine on my 1920x1080p screen but it doesn't on a friend's 1920x1200px because there aren't 820px to scroll..
How can I solve this to work with every resolution? I tried with this, but unfortunately nothing happens:
$($('.entry').get(0)).addClass('expanded');
$(window).on('scroll', function (e) {
var availableScroll = $(document).height() - $window.height();
var x = $(window).scrollTop();
if (x > 820 || x == availableScroll) {
$($('.entry').get(1)).addClass('expanded');
}
if (x > 1525 || x == availableScroll) {
$($('.entry').get(2)).addClass('expanded');
}
});
Is there a fancy method, that maybe calculates the pixels from the bottom or some method relative to the vertical res?
Here's the webpage with the code live (you can see the 2 divs unwrapping when scrolling).
In general, avoid the == for scrolling because if the scroll is off by even .0001 it will resolve as false. Also replace $window with $(window).
$($('.entry').get(0)).addClass('expanded');
$(window).on('scroll', function (e) {
var availableScroll = $(document).height() - $(window).height();
var x = $(window).scrollTop();
if (x > 820 || Math.abs(x - availableScroll) < 10) {
$($('.entry').get(1)).addClass('expanded');
}
if (x > 1525 || Math.abs(x - availableScroll) < 10) {
$($('.entry').get(2)).addClass('expanded');
}
});
Also, if you want to execute code when the page first loads, use the $(document).ready(handler) pattern.
Your former functions seems to working fine. I am testing it as MacBook Pro. However, at sometime it seems it is not fired at JQuery. What you can do is you can wait for few milliseconds to check if the scroll is finished. If scroll is finished then you can simply check the value of scroll.
Option 1:
jQuery debounce is a nice one for problems like this. jsFidlle
So your modified code will be (you need to use debounce)
$(window).scroll($.debounce( 250, true, function(){
console.log("Still scrolling");
}));
$(window).scroll($.debounce( 250, function(){
var x = $(window).scrollTop();
console.log("Scrolling finished");
if (x > 820) {
$($('.entry').get(1)).addClass('expanded');
}
if (x > 1525) {
$($('.entry').get(2)).addClass('expanded');
}
}));
Option 2:
There may be a chance you don't like use JQuery Debounce then you can native approach with timer function. See the code below and you can adjust the timer duration as per your needs.
It is simply waiting for scroll event to be finished and wait for certain milliseconds before it scroll event recalled. If scroll refires then it simply clear the timer and start waiting again. If timer is finished then it executes the method you have stated.
$(window).scroll(function() {
var timerDuration = 250; // In milliseconds
clearTimeout($.data(this, 'scrollTimer'));
$.data(this, 'scrollTimer', setTimeout(function() {
// do something
var x = $(window).scrollTop();
console.log("Scrolling finished");
if (x > 820) {
$($('.entry').get(1)).addClass('expanded');
}
if (x > 1525) {
$($('.entry').get(2)).addClass('expanded');
}
}, timerDuration));
});
Reading the AngularJS docs I haven't figured out if $anchorScroll can have a duration/easing option to smooth scroll to elements.
It only says:
$location.hash('bottom');
// call $anchorScroll()
$anchorScroll();
I do not use jquery and don't want to; is there still a clever yet simple way to make or extend $anchorScroll in order to make scrolling more smooth?
Unfortunately this is not possible using $anchorScroll. As you discovered $anchorScroll doesn't have any options and doesn't work with $ngAnimate. In order to animate the scroll you would need to use your own service/factory or just straight javascript.
For the sake of self-learning I put together an example with a smooth scrolling service. There are probably better ways to do this so any feedback is encouraged.
To scroll to an element you attach a ng-click="gotoElement(ID)" to any element. I think an even better route would be to make this a directive.
Here's the working example on jsFiddle.
Update
There are now a number of third-party directives for accomplishing this.
https://github.com/oblador/angular-scroll.
https://github.com/d-oliveros/ngSmoothScroll
https://github.com/arnaudbreton/angular-smoothscroll
https://gist.github.com/justinmc/d72f38339e0c654437a2
You can also use the angular-scroll, link "https://github.com/durated/angular-scroll/". It is smooth scrolling also few easing functions to get a professional look.
The answer from Brett worked great for me. I did some small changes on his solution in terms of modularization and testability.
Here's is yet another working example on JsFiddle that includes the other version with testing included.
For testing, I'm using Karma and Jasmine. Signature has been slightly modified as follows:
anchorSmoothScroll.scrollTo(elementId, speed);
Where element is a mandatory attribute to scroll to and speed is optional where the default is 20 (as it was before).
You can also use ngSmoothScroll, link: https://github.com/d-oliveros/ngSmoothScroll.
Just include the smoothScroll module as a dependency and use it like this:
Click me!
None of the solutions here actually does what OP originally asked, that is, make $anchorScroll scrolling smoothly.
Difference between smooth scrolling directives and $anchroScroll is that it uses/modifies $location.hash(), which may be desirable in some cases.
Here is gist for simple module that replaces $anchorScroll scrolling with smooth scrolling. It uses https://github.com/oblador/angular-scroll library for the scrolling itself (replace it with something else if you want, it should be easy).
https://gist.github.com/mdvorak/fc8b531d3e082f3fdaa9
Note: It actually does not get $anchorScroll to scroll smoothly, but it replaces its handler for scrolling.
Enable it simply by referencing mdvorakSmoothScroll module in your application.
Alan, thank you. If anyone interested, I formatted it based on John Pappa standards.
(function() {
'use strict';
var moduleId = 'common';
var serviceId = 'anchorSmoothScroll';
angular
.module(moduleId)
.service(serviceId, anchorSmoothScroll);
anchorSmoothScroll.$inject = ['$document', '$window'];
function anchorSmoothScroll($document, $window) {
var document = $document[0];
var window = $window;
var service = {
scrollDown: scrollDown,
scrollUp: scrollUp,
scrollTo: scrollTo,
scrollToTop: scrollToTop
};
return service;
function getCurrentPagePosition(currentWindow, doc) {
// Firefox, Chrome, Opera, Safari
if (currentWindow.pageYOffset) return currentWindow.pageYOffset;
// Internet Explorer 6 - standards mode
if (doc.documentElement && doc.documentElement.scrollTop)
return doc.documentElement.scrollTop;
// Internet Explorer 6, 7 and 8
if (doc.body.scrollTop) return doc.body.scrollTop;
return 0;
}
function getElementY(doc, element) {
var y = element.offsetTop;
var node = element;
while (node.offsetParent && node.offsetParent !== doc.body) {
node = node.offsetParent;
y += node.offsetTop;
}
return y;
}
function scrollDown(startY, stopY, speed, distance) {
var timer = 0;
var step = Math.round(distance / 25);
var leapY = startY + step;
for (var i = startY; i < stopY; i += step) {
setTimeout('window.scrollTo(0, ' + leapY + ')', timer * speed);
leapY += step;
if (leapY > stopY) leapY = stopY;
timer++;
}
};
function scrollUp(startY, stopY, speed, distance) {
var timer = 0;
var step = Math.round(distance / 25);
var leapY = startY - step;
for (var i = startY; i > stopY; i -= step) {
setTimeout('window.scrollTo(0, ' + leapY + ')', timer * speed);
leapY -= step;
if (leapY < stopY) leapY = stopY;
timer++;
}
};
function scrollToTop(stopY) {
scrollTo(0, stopY);
};
function scrollTo(elementId, speed) {
var element = document.getElementById(elementId);
if (element) {
var startY = getCurrentPagePosition(window, document);
var stopY = getElementY(document, element);
var distance = stopY > startY ? stopY - startY : startY - stopY;
if (distance < 100) {
this.scrollToTop(stopY);
} else {
var defaultSpeed = Math.round(distance / 100);
speed = speed || (defaultSpeed > 20 ? 20 : defaultSpeed);
if (stopY > startY) {
this.scrollDown(startY, stopY, speed, distance);
} else {
this.scrollUp(startY, stopY, speed, distance);
}
}
}
};
};
})();
I am not aware of how to animate $anchorScroll . Here's how I do it in my projects:
/* Scroll to top on each ui-router state change */
$rootScope.$on('$stateChangeStart', function() {
scrollToTop();
});
And the JS function:
function scrollToTop() {
if (typeof jQuery == 'undefined') {
return window.scrollTo(0,0);
} else {
var body = $('html, body');
body.animate({scrollTop:0}, '600', 'swing');
}
log("scrollToTop");
return true;
}
We can use JQuery and Javascript with Directive to perform the scrolling to a specific div on anchor tag click.
Please check the working example on the below link -
https://stackoverflow.com/a/67513880/6656918
What is the equivalent of this:
$('html, body').animate({
scrollTop: 200
}, 400);
In MooTools or agnostic javascript?
Im going round in circles....
Cheers
This loop can animate the scrollTop property of the window in basic javascript:
window.onload = function() {
var offset = 0; //vertical offset
var interval = setInterval(function() {
window.scrollTo(0, offset);
offset += 4; // plus 4 pixels
if (offset >= 200) {
clearInterval(interval);
}
}, 8); // 200px/4px==50 cycles ; 400ms/50cycles == 8ms per cycle
};
fiddle
Because you asked for a version in mootools, it's just one line:
new Fx.Scroll(window).start(0, 200);
http://jsfiddle.net/ye3vX/
Note: It needs mootools more's Fx.Scroll.
I'm wondering if there is a simple way to make use of JavaScript (probably jQuery too?) in order to make the contents of a fixed-height div element scroll infinitely up and down (top, bottom, top, bottom, etc) when the page loads and without any user input or manipulation?
Thanks ahead of time, any input is greatly appreciated as I am hardly mediocre with JavaScript.
With pure js you can do something like this:
var scroller = document.getElementById('scroller');
var delta = 15;
var lastSc;
//console.log(scroller.scrollTop, scrollHeight);
setInterval(function(){
var sc = scroller.scrollTop + delta;
scroller.scrollTop = sc;
if (scroller.scrollTop === lastSc){
delta = delta*(-1);
}
lastSc = scroller.scrollTop;
}, 10);
Here is demo
Edit: updated demo
Here is something I've just written, using jQuery:
var speed = 100; //smaller means faster
var offset = 5; //bigger means more text will be "scrolled" every time
function ScrollMyDiv() {
var myDiv = $("#MyDiv");
var direction = myDiv.attr("scroll_dir") || "";
var lastScrollTop = parseInt(myDiv.attr("last_scroll_top") || "0", 10);
if (direction.length === 0) {
myDiv.attr("scroll_dir", "down");
direction = "down";
}
var top = myDiv.scrollTop();
myDiv.attr("last_scroll_top", top + "")
if (direction === "down") {
if (top > 0 && lastScrollTop === top)
myDiv.attr("scroll_dir", "up");
top += offset;
} else {
if (top <= 0)
myDiv.attr("scroll_dir", "down");
top -= offset;
}
myDiv.scrollTop(top);
window.setTimeout(ScrollMyDiv, speed);
}
$(document).ready(function() {
ScrollMyDiv();
});
Live test case: http://jsfiddle.net/HmfNJ/1/
Basically, it will start by scrolling down (adding to the scrollTop) then when it identify it reached the bottom by seeing the scrollTop remains the same, it will change direction and start scroll up.
Thanks for the replies but I found my answer elsewhere. Here's what I ended up using: http://jsbin.com/onohan/3/edit#preview
It had a couple of small problems but I at least knew enough about basic JavaScript to fix them. Hopefully this will benefit someone in the future. :)
To get a smooth transition for scroll to bottom this is VanillaJS code that works well with me
var delta = 0.6, interval;
interval = setInterval(function(){
window.scrollBy(0, delta);
}, 20);
To clear the Interval you can run
clearInterval(interval);