i've got some code from codepen where a progress bar fills up depending on a given number out of 100. i want it to activate when it is scrolled to, instead of on reload, which it currently is set to. I cant find anything on here already.
Javascript is new to me, so trying to get the hang of it, thanks
$('.progress-wrap').each(function(){
percent = $(this);
bar = $(this).children('.progress-bar');
moveProgressBar(percent, bar);
});
// on browser resize...
$(window).resize(function() {
$('.progress-wrap').each(function(){
percent = $(this);
bar = $(this).children('.progress-bar');
moveProgressBar(percent, bar);
});
});
// SIGNATURE PROGRESS
function moveProgressBar(percent, bar) {
var getPercent = (percent.data('progress-percent') / 100);
var getProgressWrapWidth = percent.width();
var progressTotal = getPercent * getProgressWrapWidth;
var animationLength = 1000;
// on page load, animate percentage bar to data percentage length
// .stop() used to prevent animation queueing
bar.stop().animate({
left: progressTotal
}, animationLength);
}
<div class="progress-wrap progress star" data-progress-percent="70">
<div class="progress-bar progress"></div>
</div>
This should work:
Replace #someElement with the Id of the element that when in view fires the function you want.
someFunction() is the function that you want to run when the element is in view
$(document).on('scroll', function() {
if( $(this).scrollTop() >= $('#someElement').position().top ) {
someFunction();
}
});
Looks like you are using jquery - it would be easier to do this in vanilla javascript like so:
window.onscroll = function (e) {
// called when the window is scrolled.
}
You may want to have a look at: Detect if user is scrolling for jquery options.
Related
I'm trying to create a sliding sidebar and was wondering if there was a better way then what I am doing already.
<img id = "MenuIcon" src = "MenuIcon.png" alt = "Menu Icon" onclick = "slideSideBar()" />
At the moment I simply check if the sideSlideCount is even - if it is the sidebar must not be out, so when the function is called it slides out; if sideSlideCount is odd (i.e. % 2 != 0) then the sidebar should slide out of view.
var sideSlideCount = 0; // variable used with the side bar
var bodyHeight = $(window).height();
var bodyWidth = $(window).width();
console.log(bodyWidth);
function slideSideBar() {
if (sideSlideCount % 2 == 0) { // if sideSlideCount is even, i.e. the side bar is hidden
$("#SideBar").animate({width: bodyWidth / 6}, 600); // slide the bar out to 300 width, 0.6 seconds
$("#SideLinks").fadeTo(1000, 0.8); // fade the links into view, 1 second, to 100% opacity
}
else { // else, if the side bar is in view
$("#SideBar").fadeIn(300).animate({width: 0}, 600); // slide the bar back out of view (0 width), 0.6 seconds
$("#SideLinks").fadeTo(200, 0); // fade the links out of view, 0.2 seconds, to 0% opacity
}
sideSlideCount++; // increment the variable
}
You could simply make your code modular to avoid global variables. You should be looking into AMD modules, however to keep it simple you can create yourself a namespace where your code will live.
DEMO: http://jsfiddle.net/BzYuQ/
//Define a SlidingSidebar reusable component
!function (ns, $) {
function SlidingSidebar(el, animateDuration) {
this.$el = $(el);
this.animateDuration = animateDuration;
}
SlidingSidebar.prototype = {
constructor: SlidingSidebar,
toggle: function () {
this.$el.stop().animate({ width: 'toggle' }, this.animateDuration);
}
};
ns.SlidingSidebar = SlidingSidebar;
}(slideExampleSite = window.slideExampleSite || {}, jQuery);
$(function () {
//The following behavior could also be in a configurable "SlidebarFeature" module
//or you could integrate the button directly into the SlidingSidebar.
//I'm just showing how code can be modularized here.
var sideBar = new slideExampleSite.SlidingSidebar('#sidebar', 600);
$('#toggle-btn').click(function () {
sideBar.toggle();
});
});
Obviously in this case the SlidingSidebar component doesn't do much, but as your application grows, modularizing your code to get away from the $(function () {/*tons of code*/}); anti-pattern will pay off in many ways.
You didn't say what "better" is, but if the intention is just to avoid globals, you can use a closure:
var slideSideBar = (function() {
var sideSlideCount = false; // variable used with the side bar
var bodyHeight = $(window).height();
var bodyWidth = $(window).width();
console.log(bodyWidth);
// This function has access to all the above variables, but no other
// function does.
function slideSideBar() {
if (sideSlideCount) {
$("#SideBar").animate({width: bodyWidth / 6}, 600);
$("#SideLinks").fadeTo(1000, 0.8);
} else {
$("#SideBar").fadeIn(300).animate({width: 0}, 600);
$("#SideLinks").fadeTo(200, 0);
}
sideSlideCount = !sideSlideCount;
}
// Return a reference to the slideSideBar function so it's available
// globally, but access to variables is "private"
return slideSideBar;
}());
The only difference is that slideSideBar won't exist until the above has been executed, so don't try to call it until afterward.
Not sure on your definition of "better", but I see your using jQuery which has a nice $toggle feature which can help here.
function slidesideBar(){
$("#SideBar").toggle(function() {
$(this).animate({width: bodyWidth / 6}, 600); // slide the bar out to 300 width, 0.6 seconds
$("#SideLinks").fadeTo(1000, 0.8); // fade the links into view, 1 second, to 100% opacity
}, function() {
$(this).fadeIn(300).animate({width: 0}, 600); // slide the bar back out of view (0 width), 0.6 seconds
$("#SideLinks").fadeTo(200, 0); // fade the links out of view, 0.2 seconds, to 0% opacity
});
}
Credit: jQuery Animation Toggle. Copy and paste:
When given a list of functions as arguments, the .toggle(fn1, fn2) method will alternate between the functions given starting with the first one. This automatically keeps track of the toggle state for you - you don't have to do that.
jQuery doc is here. There are multiple forms of .toggle() depending upon the arguments used so you don't always find the right one when searching the jQuery doc.
I want to create a webpage that contains several sections. In one of those sections are something like progress bars. These progress bars are 'animated' so that the user sees them loading on the screen as shown in the example.
Example here
Now this is working as it is but my problem is this:
I want the progress bars to start loading when the bars become visible on the screen.
Once the user scrolls down and gets them in the middle of the screen, the 'animation' should start. The way it is now the animation starts on page load, but the bars are not yet visible as in the following fiddle:
Fiddle
A little extra would be that each bar starts loading after the previous is finished.
I found some similar questions on stack but the answer does not suffice to my needs:
Animate progress bar on scroll & Run animation when element is visible on screen
I tried stuff like (it's not the actual code but it's what I remember of it):
var target = $("#third").offset().top;
var interval = setInterval(function() {
if ($(window).scrollTop() >= target) {
//start loading progress bar
}
}, 250);
But without any good results.
Can anyone help me on this matter?
Thanks in advance!
Here is a fiddle: http://jsfiddle.net/rAQev/4/
I've used a comparison of scroll offset and your special section offset to detect a moment when this section becomes visible.
Animations are queued to be processed one after another using jQuery queue function, you can read about it in jQuery docs (http://api.jquery.com/queue/).
Also scroll event is unbinded when the first 'loading' happens, not to run 'loading' again and again on scroll event when section is visible.
Here is an updated fiddle http://jsfiddle.net/9ybUv/
This one allows for all the progress bars to run at the same time. If you were like me and had 5 or more it takes a long time to do one, then the next, then the next.
$(function() {
var $section = $('#third');
function loadDaBars() {
$(".meter > span").each(function() {
$(this)
.data("origWidth", $(this).width())
.width(0)
.animate({
width: $(this).data("origWidth")
}, 1200);
});
}
$(document).bind('scroll', function(ev) {
var scrollOffset = $(document).scrollTop();
var containerOffset = $section.offset().top - window.innerHeight;
if (scrollOffset > containerOffset) {
loadDaBars();
// unbind event not to load scrolsl again
$(document).unbind('scroll');
}
});
});
Let me try something
function startProgressBar() {
$(".meter > span").each(function() {
$(this)
.data("origWidth", $(this).width())
.width(0)
.animate({
width: $(this).data("origWidth")
}, 1200);
});
}
$(window).scroll(function() {
var target = $('#third');
var targetPosTop = target.position().top; // Position in page
var targetHeight = target.height(); // target's height
var $target = targetHeight + targetPosTop; // the whole target position
var $windowst = $(window).scrollTop()-($(window).height()/2); // yes divided by 2 to get middle screen view.
if (($windowst >= $targetPosTop) && ($windowst < $target)){
// start progressbar I guess
startProgressBar();
}
});
Give it a try, let me know.
I am trying to implement synchronized scrolling for two DIV with the following code.
DEMO
$(document).ready(function() {
$("#div1").scroll(function () {
$("#div2").scrollTop($("#div1").scrollTop());
});
$("#div2").scroll(function () {
$("#div1").scrollTop($("#div2").scrollTop());
});
});
#div1 and #div2 is having the very same content but different sizes, say
#div1 {
height : 800px;
width: 600px;
}
#div1 {
height : 400px;
width: 200px;
}
With this code, I am facing two issues.
1) Scrolling is not well synchronized, since the divs are of different sizes. I know, this is because, I am directly setting the scrollTop value. I need to find the percentage of scrolled content and calculate corresponding scrollTop value for the other div. I am not sure, how to find the actual height and current scroll position.
2) This issue is only found in firefox. In firefox, scrolling is not smooth as in other browsers. I think this because the above code is creating a infinite loop of scroll events.
I am not sure, why this is only happening with firefox. Is there any way to find the source of scroll event, so that I can resolve this issue.
Any help would be greatly appreciated.
You can use element.scrollTop / (element.scrollHeight - element.offsetHeight) to get the percentage (it'll be a value between 0 and 1). So you can multiply the other element's (.scrollHeight - .offsetHeight) by this value for proportional scrolling.
To avoid triggering the listeners in a loop you could temporarily unbind the listener, set the scrollTop and rebind again.
var $divs = $('#div1, #div2');
var sync = function(e){
var $other = $divs.not(this).off('scroll'), other = $other.get(0);
var percentage = this.scrollTop / (this.scrollHeight - this.offsetHeight);
other.scrollTop = percentage * (other.scrollHeight - other.offsetHeight);
// Firefox workaround. Rebinding without delay isn't enough.
setTimeout( function(){ $other.on('scroll', sync ); },10);
}
$divs.on( 'scroll', sync);
http://jsfiddle.net/b75KZ/5/
Runs like clockwork (see DEMO)
$(document).ready(function(){
var master = "div1"; // this is id div
var slave = "div2"; // this is other id div
var master_tmp;
var slave_tmp;
var timer;
var sync = function ()
{
if($(this).attr('id') == slave)
{
master_tmp = master;
slave_tmp = slave;
master = slave;
slave = master_tmp;
}
$("#" + slave).unbind("scroll");
var percentage = this.scrollTop / (this.scrollHeight - this.offsetHeight);
var x = percentage * ($("#" + slave).get(0).scrollHeight - $("#" + slave).get(0).offsetHeight);
$("#" + slave).scrollTop(x);
if(typeof(timer) !== 'undefind')
clearTimeout(timer);
timer = setTimeout(function(){ $("#" + slave).scroll(sync) }, 200)
}
$('#' + master + ', #' + slave).scroll(sync);
});
This is what I'm using. Just call the syncScroll(...) function with the two elements you want to synchronize. I found pawel's solution had issues with continuing to slowly scroll after the mouse or trackpad was actually done with the operation.
See working example here.
// Sync up our elements.
syncScroll($('.scroll-elem-1'), $('.scroll-elem-2'));
/***
* Synchronize Scroll
* Synchronizes the vertical scrolling of two elements.
* The elements can have different content heights.
*
* #param $el1 {Object}
* Native DOM element or jQuery selector.
* First element to sync.
* #param $el2 {Object}
* Native DOM element or jQuery selector.
* Second element to sync.
*/
function syncScroll(el1, el2) {
var $el1 = $(el1);
var $el2 = $(el2);
// Lets us know when a scroll is organic
// or forced from the synced element.
var forcedScroll = false;
// Catch our elements' scroll events and
// syncronize the related element.
$el1.scroll(function() { performScroll($el1, $el2); });
$el2.scroll(function() { performScroll($el2, $el1); });
// Perform the scroll of the synced element
// based on the scrolled element.
function performScroll($scrolled, $toScroll) {
if (forcedScroll) return (forcedScroll = false);
var percent = ($scrolled.scrollTop() /
($scrolled[0].scrollHeight - $scrolled.outerHeight())) * 100;
setScrollTopFromPercent($toScroll, percent);
}
// Scroll to a position in the given
// element based on a percent.
function setScrollTopFromPercent($el, percent) {
var scrollTopPos = (percent / 100) *
($el[0].scrollHeight - $el.outerHeight());
forcedScroll = true;
$el.scrollTop(scrollTopPos);
}
}
If the divs are of equal sizes then this code below is a simple way to scroll them synchronously:
scroll_all_blocks: function(e) {
var scrollLeft = $(e.target)[0].scrollLeft;
var len = $('.scroll_class').length;
for (var i = 0; i < len; i++)
{
$('.scroll_class')[i].scrollLeft = scrollLeft;
}
}
Here im using horizontal scroll, but you can use scrollTop here instead. This function is call on scroll event on the div, so the e will have access to the event object.
Secondly, you can simply have the ratio of corresponding sizes of the divs calculated to apply in this line $('.scroll_class')[i].scrollLeft = scrollLeft;
I solved the sync scrolling loop problem by setting the scroll percentage to fixed-point notation: percent.toFixed(0), with 0 as the parameter. This prevents mismatched fractional scrolling heights between the two synced elements, which are constantly trying to "catch up" with each other. This code will let them catch up after at most a single extra step (i.e., the second element may continue to scroll an extra pixel after the user stops scrolling). Not a perfect solution or the most sophisticated, but certainly the simplest I could find.
var left = document.getElementById('left');
var right = document.getElementById('right');
var el2;
var percentage = function(el) { return (el.scrollTop / (el.scrollHeight - el.offsetHeight)) };
function syncScroll(el1) {
el1.getAttribute('id') === 'left' ? el2 = right : el2 = left;
el2.scrollTo( 0, (percentage(el1) * (el2.scrollHeight - el2.offsetHeight)).toFixed(0) ); // toFixed(0) prevents scrolling feedback loop
}
document.getElementById('left').addEventListener('scroll',function() {
syncScroll(this);
});
document.getElementById('right').addEventListener('scroll',function() {
syncScroll(this);
});
I like pawel's clean solution but it lacks something I need and has a strange scrolling bug where it continues to scroll and my plugin will work on multiple containers not just two.
http://www.xtf.dk/2015/12/jquery-plugin-synchronize-scroll.html
Example & demo: http://trunk.xtf.dk/Project/ScrollSync/
Plugin: http://trunk.xtf.dk/Project/ScrollSync/jquery.scrollSync.js
$('.scrollable').scrollSync();
If you don't want proportional scrolling, but rather to scroll an equal amount of pixels on each field, you could add the value of change to the current value of the field you're binding the scroll-event to.
Let's say that #left is the small field, and #right is the bigger field.
var oldRst = 0;
$('#right').on('scroll', function () {
l = $('#left');
var lst = l.scrollTop();
var rst = $(this).scrollTop();
l.scrollTop(lst+(rst-oldRst)); // <-- like this
oldRst = rst;
});
https://jsfiddle.net/vuvgc0a8/1/
By adding the value of change, and not just setting it equal to #right's scrollTop(), you can scroll up or down in the small field, regardless of its scrollTop() being less than the bigger field. An example of this is a user page on Facebook.
This is what I needed when I came here, so I thought I'd share.
From the pawel solution (first answer).
For the horizzontal synchronized scrolling using jQuery this is the solution:
var $divs = $('#div1, #div2'); //only 2 divs
var sync = function(e){
var $other = $divs.not(this).off('scroll');
var other = $other.get(0);
var percentage = this.scrollLeft / (this.scrollWidth - this.offsetWidth);
other.scrollLeft = percentage * (other.scrollWidth - other.offsetWidth);
setTimeout( function(){ $other.on('scroll', sync ); },10);
}
$divs.on('scroll', sync);
JSFiddle
An other solution for multiple horizontally synchronized divs is this, but it works for divs with same width.
var $divs = $('#div1, #div2, #div3'); //multiple divs
var sync = function (e) {
var me = $(this);
var $other = $divs.not(me).off('scroll');
$divs.not(me).each(function (index) {
$(this).scrollLeft(me.scrollLeft());
});
setTimeout(function () {
$other.on('scroll', sync);
}, 10);
}
$divs.on('scroll', sync);
NB: Only for divs with same width
JSFiddle
I have a responsive layout and I've created a fading panels animated element with jQuery. I set the jQuery function to only activate if the user is above a certain screen size. However, if I scale the window down, the function still runs.
Here's what I'd like to achieve:
When the user scrolls bellow a browser width of 1440px, stop that fading panels animation.
Once the animation is stopped, I want to reset the area to display the 1st panel.
If the user scrolls back up above that screen size, start the animation again.
Here is my code, thanks in advance for your time:
// Get the viewport size!
var viewport = $(document).width();
if (viewport >= 1140) {
var InfiniteRotator =
{
init: function()
{
// hard set height of container
$('#circles').height('286.717px');
// initial fade-in time
var initialInterval = 3000;
// interval between items
var itemInterval = 5000;
// cross-fade time
var fadeTime = 2000;
// count number of items
var numberOfItem = $('.rotating-item').length;
// set current item
var currentItem = 0;
// create loop
var infiniteLoop = setInterval(function() {
// initial fade out
$('.rotating-item').eq(currentItem).fadeOut(fadeTime);
// set counter
if (currentItem == numberOfItem -1) {
currentItem = 0;
} else {
currentItem++;
}
// next item fade in
$('.rotating-item').eq(currentItem).fadeIn(fadeTime);
}, itemInterval);
}
}
// go Go GO!
InfiniteRotator.init();
}
Please note: I used this great tutorial to create the fading panels: http://trendmedia.com/news/infinite-rotating-images-using-jquery-javascript/
window.onresize = function() {
clearInterval(infiniteLoop)
}
Note that you must still be in your init function in order to get the infiniteLoop variable.
You can use
window.onresize = function(){ ... }
$(window).resize(function() {
//stop and reset animation in here.
});
I am trying to set up infinite-scroll on a site I am developing with Coldfusion, I am new to javascript and jquery so I am having some issues wrapping my head around all of this. Do I need to have pagination on my site in order to use the infinite-scroll plugin, or is there a way to do it with out it?
You do not need infinite scroll plug-in for this. To detect when scroll reaches end of page, with jQuery you can do
$(window).scroll(function () {
if ($(window).scrollTop() >= $(document).height() - $(window).height() - 10) {
//Add something at the end of the page
}
});
Demo on JsFiddle
I'm using Hussein's answer with AJAX requests. I modified the code to trigger at 300px instead of 10px, but it started causing my appends to multiply before the AJAX request was finished since the scroll call triggers much more frequently in a 300px range than a 10px range.
To fix this, I added a trigger that would be flipped on successful AJAX load. My code looks more like this:
var scrollLoad = true;
$(window).scroll(function () {
if (scrollLoad && $(window).scrollTop() >= $(document).height() - $(window).height() - 300) {
scrollLoad = false;
//Add something at the end of the page
}
});
then in my AJAX response, I set scrollLoad to true.
I built on top of Hussein's little example here to make a jQuery widget. It supports localStorage to temporarily save appended results and it has pause functionality to stop the appending every so often, requiring a click to continue.
Give it a try:
http://www.hawkee.com/snippet/9445/
$(function(){
$(window).scroll(function(){
if($(document).height()<=$(window).scrollTop()+$(window).height()+100){
alert('end of page');
}
});
});
Some one asked for explanation so here is the explanation
here $(document).height()-->is the height of the entire document.In most cases, this is equal to the element of the current document.
$(window).height()-->is the height of the window (browser) means height of whatever you are seeing on browser.
$(window).scrollTop()-->The Element.scrollTop property gets or sets the number of pixels that the content of an element is scrolled upward. An element's scrollTop is a measurement of the distance of an element's top to its topmost visible content. When an element content does not generate a vertical scrollbar, then its scrollTop value defaults to 0.
$(document).height()<=$(window).scrollTop()+$(window).height()+100
add $(window).scrollTop() with $(window).height() now check whether the result is equal to your documnet height or not. if it is equal means you reached at the end.we are adding 100 too because i want to check before the 100 pixels from the bottom of document(note <= in condition)
please correct me if i am wrong
I had same problem but didn't find suitable plugin for my need. so I wrote following code. this code appends template to element by getting data with ajax and pagination.
for detecting when user scrolls to bottom of div I used this condition:
var t = $("#infiniteContent").offset().top;
var h = $("#infiniteContent").height();
var ws = $(window).scrollTop();
var dh = $(document).height();
var wh = $(window).height();
if (dh - (wh + ws) < dh - (h + t)) {
//now you are at bottom of #infiniteContent element
}
$(document).ready(function(){
$.getJSON("https://jsonplaceholder.typicode.com/comments", { _page: 1, _limit:3 }, function (jsonre) {
appendTemplate(jsonre,1);
});
});
function appendTemplate(jsonre, pageNumber){
//instead of this code you can use a templating plugin like "Mustache"
for(var i =0; i<jsonre.length; i++){
$("#infiniteContent").append("<div class='item'><h2>"+jsonre[i].name+"</h2><p>"+jsonre[i].body+"</p></div>");
}
if (jsonre.length) {
$("#infiniteContent").attr("data-page", parseInt(pageNumber)+1);
$(window).on("scroll", initScroll);
//scroll event will not trigger if window size is greater than or equal to document size
var dh = $(document).height() , wh = $(window).height();
if(wh>=dh){
initScroll();
}
}
else {
$("#infiniteContent").attr("data-page", "");
}
}
function initScroll() {
var t = $("#infiniteContent").offset().top;
var h = $("#infiniteContent").height();
var ws = $(window).scrollTop();
var dh = $(document).height();
var wh = $(window).height();
if (dh - (wh + ws) < dh - (h + t)) {
$(window).off('scroll');
var p = $("#infiniteContent").attr("data-page");
if (p) {
$.getJSON("https://jsonplaceholder.typicode.com/comments", { _page: p, _limit:3 }, function (jsonre) {
appendTemplate(jsonre, p);
});
}
}
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<div id="infiniteContent"></div>
If you have a scrollable element, like a div with scroll overflow, but no scrollable document/page, you can take this way.
$(function () {
var s = $(".your-scrollable-element");
var list = $("#your-table-list");
/* On element scroll */
s.scroll(function () {
/* The scroll top plus element height equals to table height */
if ((s.scrollTop() + s.height()) == list.height()) {
/* you code */
}
});
});
I wrote this function using Hussein and Nick's ideas, but I wanted it to use promises for the callback. I also wanted the infinite scrolling area to be on a fixed div and not just the window if the div is sent into the options object. There is an example of that in my second link below. I suggest using a promise library like Q if you want to support older browsers. The cb method may or may not be a promise and it will work regardless.
It is used like so:
html
<div id="feed"></div>
js
var infScroll = infiniteScroll({
cb: function () {
return doSomethingPossiblyAnAJAXPromise();
}
});
If you want the feed to temporarily stop you can return false in the cb method. Useful if you have hit the end of the feed. It can be be started again by calling the infiniteScroll's returned object method 'setShouldLoad' and passing in true and example to go along with the above code.
infScroll.setShouldLoad(true);
The function for infinite scrolling is this
function infiniteScroll (options) {
// these options can be overwritten by the sent in options
var defaultOptions = {
binder: $(window), // parent scrollable element
loadSpot: 300, //
feedContainer: $("#feed"), // container
cb: function () { },
}
options = $.extend(defaultOptions, options);
options.shouldLoad = true;
var returnedOptions = {
setShouldLoad: function (bool) { options.shouldLoad = bool; if(bool) { scrollHandler(); } },
};
function scrollHandler () {
var scrollTop = options.binder.scrollTop();
var height = options.binder[0].innerHeight || options.binder.height();
if (options.shouldLoad && scrollTop >= (options.binder[0].scrollHeight || $(document).height()) - height - options.loadSpot) {
options.shouldLoad = false;
if(typeof options.cb === "function") {
new Promise(function (resolve) {resolve();}).then(function() { return options.cb(); }).then(function (isNotFinished) {
if(typeof isNotFinished === "boolean") {
options.shouldLoad = isNotFinished;
}
});
}
}
}
options.binder.scroll(scrollHandler);
scrollHandler();
return returnedOptions;
}
1 feed example with window as scroller
2 feed example with feed as scroller