Fullcalendar v2: How to maintain the same scroll time when navigating weeks? - javascript

On Fullcalendar 2, when I navigate between weeks, I'd like to maintain the same time ranges in the vertical scroll. For example, in the below images, I am initially looking at times from 12am-3pm. But when I press the next arrow to go to the next week, it resets at 8am.
I know that I can change the default starting time with
scrollTime: "08:00:00",
but how do I make it so that the vertical time range is "fixed" to what I am on?

Unfortunately this is not build-in functionality. There is a workaround but you will always have a little bit of flickering when you go to the previous/next week.
var scroll = -1,
viewNames = ['agendaWeek', 'agendaDay'];
$('#calendar').fullCalendar({
//...
eventAfterAllRender: function(view) {
if(scroll > -1 && viewNames.indexOf(view.name) !== -1)
//Use a setTimeout hack here because the scrollTime will be set after eventAfterAllRender is processed.
setTimeout(function(){
document.querySelector('.fc-agenda-slots').parentNode.parentNode.scrollTop = scroll;
}, 0);
},
viewDestroy: function(view) {
if(viewNames.indexOf(view.name) !== -1)
scroll = document.querySelector('.fc-agenda-slots').parentNode.parentNode.scrollTop;
}
//...
});
jsfiddle
This code will work for FullCalendar v2. It assumes that the scrolling div is the parent of the parent of the .fc-agenda-slots div.

Working Code compatible with fullcalendar 2.6.1
I started this code from the post below (A1rPun).
Working JSFiddle
var scroll = -1;
$('#calendar').fullCalendar({
// init calendar
header: {left: 'today prev,next, refresh title',
right: 'agendaDay,agendaWeek'},
allDaySlot: false,
defaultView: 'agendaDay',
// when all the events are rendered, scroll to the previous saved scroll position
eventAfterAllRender: function(view) {
if(scroll > -1 && (view.name=='agendaDay' || view.name=='agendaWeek'))
setTimeout(function(){
document.querySelector('.fc-scroller').scrollTop = scroll;
},0);
},
// when view is destroyed, we keep the scroll position in a variable
viewDestroy: function(view) {
if(view.name=='agendaDay' || view.name=='agendaWeek')
scroll = document.querySelector('.fc-scroller').scrollTop;
}
}); // end calendar
This solution is working in 'agendaDay', and 'agendaWeek' views.
It's also working when you switch between them.
I don't think it is very pretty because you need to wait until after all the events are rendered.
The more events you have on your calendar, the more time the scroll will take..
A good solution would be to use the
Fullcalendar option scrollTime
You can set it in viewRender like this.
That will have the effect to make the calendar scroll to this time.
viewRender: function(view, element){
view.options.scrollTime = '09:00:00';
}
Maybe there is a way to convert the scroll value into time and then render it to the calendar.
EDIT 1
I figured out that is way much better to use the
viewRender
callback, instead of the eventAfterAllRender callback to set the scroll position.
Here is the JSFiddle
viewRender: function(view, element){
if(scroll > -1 && (view.name=='agendaDay' || view.name=='agendaWeek')){
setTimeout(function(){
document.querySelector('.fc-scroller').scrollTop = scroll;
},0);
}
},
It's allowing to switch between other wiews (month, basicWeek...) and keep saving the scroll.
And it's a bit faster

What I have is:
var height = $(window).scrollTop();
console.log("current height " + height);
$('#calendar').fullCalendar( 'removeEvents');
$('#calendar').fullCalendar( 'addEventSource', e);
$('#calendar').fullCalendar( 'addEventSource', holiday);
$('#calendar').fullCalendar( 'refetchEvents');
window.scrollTo(0,height);
console.log("scroll to " + height);
and it works for me

Here you can set basic configuration.
scrollTime: '08:00:00',
minTime: '00:00:00', // Set your min time
maxTime: '24:00:00', // Set your max time
yes, main thing is 'height' parameter should not be there in calendar configuration.
Remove below parameter
height: 'auto'

While I would like to see this as build-in function, this is the fastest and at the same time less verbose solution I found:
viewDestroy: function (view) {
if (view.name=='agendaDay' || view.name=='agendaWeek') {
var scrollEl = document.querySelector('.fc-scroller');
var scrollFraction = scrollEl.scrollTop / scrollEl.scrollHeight;
this.scrollTime = moment().startOf('day').add(3600 * 24 * scrollFraction, 's').format('HH:mm:00');
view.options.scrollTime = this.scrollTime;
}
}
Notice that this assumes the scroll range is 12AM-12AM. If you limit your visible area, you should adapt the day duration as well.
You can try it out: JSFiddle

give a unique class to event object. add this code in after event render method.
it will scroll to that specific event
$('.fc-scroller').animate({
scrollTop: $('write here unique class of event').position().top
});

To avoid any scroll "flicking", I did this this way (in fullCalendar 2.9.1) :
viewDestroy: function( aView ) {
// At first view date change, get height of an hour
if( !lineHeight )
lineHeight = $('#calendar').find( "tr.fc-minor" ).height() * 2;
// then convert current scroll to hour decimal
var lScrollCurrHour = Math.max( ($('#calendar').find( ".fc-scroller" )[0].scrollTop - 1) / lineHeight, 0 );
// finally, use moment() to convert to a formatted date
aView.options.scrollTime = moment().startOf('day')
.add(lScrollCurrHour, "hours").format("HH:mm:ss");
}
It will keep separated the scroll for week and days agendas
Hope it helps :o)

you could use this to make the scroll time as your desired time.
customButtons:{
PreviousButton: {
text: 'Prev',
icon: 'left-single-arrow',
click: function() {
$('#calendar').fullCalendar('prev');
$("#calendar").fullCalendar( 'scrollTime',"08:00:00" );
}
}
}`
The method I specified here is only for previous button. Likewise we can have custom button for all the default buttons in full calendar and we can add this method to scroll the time whenever needed.
i hope this would help.

Related

Callback after element moved

I have list that scrolls up using velocity. I want to play sound each time, first visible item of the list scrolled up.
<div class="viewport" data-winner="">
<ul class="participants-holder container" id="ph">
<li>...<li> //many li elements
</ul>
</div>
moveToEl(name) {
...
$(container).velocity({
translateY: -(offsetToScroll)+'px'
}, {
duration: 15000,
easing: [.74,0,.26,1],
complete: (el) => {
...
// complete animation callback
},
progress: (els, complete, remaining, start, tweenVal) => {
console.log(Math.floor(complete * 100) + '%')
// I think some check should do during progress animation
}
})
}
How to handle event or track changes when each element or entire list are scrolled up by certain pixels, for instance 62px. How can I detect this and call callback function on this happened.
You can find the current TranslateY using something like
+this.container.style.transform.replace(/[^0-9.]/g, '');
from https://stackoverflow.com/a/42267490/1544886, and compare it to the previous value plus an offset.
In the Roulette class add this.prevTranslatePos = 0.0; for storing the old value.
progress: (els, complete, remaining, start) => {
// from https://stackoverflow.com/a/42267490/1544886
var translatePos = +this.container.style.transform.replace(/[^0-9.]/g, '');
if (translatePos >= (this.prevTranslatePos + 62))
{
//console.log(translatePos, this.prevTranslatePos);
this.prevTranslatePos = translatePos;
this.sound.pause();
this.sound.currentTime = 0;
this.sound.play();
}
}
Demo applied to the 'Go To' button only: http://codepen.io/anon/pen/yMXwgd?editors=1010
Note that the sound cuts out when it runs too quickly, but that could be handled a few different ways.
Add a scroll eventListener to the parent element of the list (I believe it's participants-holder in your case), and within that do a check for whether the right amount of pixels have moved since the last check. Store the current position, and compare it to the last time you moved the desired amount.
Hope that helps!

Full page slider with native scrollbar

I am building a full page slider that keeps the native scrollbar and allows the user to either free scroll, use the mouse wheel or navigation dots (on the left) to switch to a slide.
Once the user is on the last slide and tries to scroll down further, the whole slider moves up to reveal a simple scrollable section. If the user scrolls down and then tries to go back up, then this new section moves out of the way again and returns the slider back into view.
Fiddle: http://jsfiddle.net/3odc8zmx/
The parts I'm struggling with:
Only the first two navigation dots work. The third one DOES WORK if you area looking at the first slide. But doesn't do anything, if you are on slide 2. Note: the purple one is a short-cut to the second section of the page and not related to the slider.
When moving to the last slide (via the dots, if you're on the first slide) it causes the code to make the whole slider move upwards as it sees this as the user has slid past the last slide as per the description above. I have tried to combat this using a variable called listen to stop the scroll event listening when using the showSlide method... but it seems to be true even though I set it to false, and only reset it to true again after the animation...
When scrolling down using the mouse wheel, I can get to the second section and back up, but not to the first third section. I'm wondering if I could use the showSlide method to better handle this instead of the current dirty next and prev functions I have implemented.
Note: If the user has free-scrolled, when they use the mouse-wheel, I want the slider to snap to the nearest slide to correct itself... Any suggestions for how I could do this?
Can anyone offer some help?
Here's the JS:
var listen = true;
function nextSlide()
{
$('#section1').stop(true,false).animate({
scrollTop: $('#section1').scrollTop() + $(window).height()
});
}
function prevSlide()
{
$('#section1').stop(true,false).animate({
scrollTop: -$('#section1').scrollTop() + $(window).height()
});
}
function showSlide(index)
{
var offset = $('#section1 div').eq(index).offset();
offset = offset.top;
if(offset){
listen = false;
$('.slide-dot').removeClass('active');
$('.slide-dot').eq(index).addClass('active');
$('#section1').stop(true,false).animate({
scrollTop: offset
}, 500, function(){
listen = true;
});
} else {
alert('error');
}
}
$(document).ready(function(){
var fullHeight = 0;
$('#section1 div').each(function(){
fullHeight = fullHeight + $(this).height();
});
var lastScrollTop1 = 0;
$('#section1').on('scroll', function(e){
var st = $(this).scrollTop();
if (st > lastScrollTop1){
if( $('#section1').scrollTop() + $(window).height() == fullHeight) {
if(listen){
$('body').addClass('shifted');
}
}
}
lastScrollTop1 = st;
});
$('#section1').on('mousewheel', function(e){
e.preventDefault();
var st = $(this).scrollTop();
if (st > lastScrollTop1){
nextSlide();
} else {
prevSlide();
}
});
var lastScrollTop2 = 0;
$('#section2').on('scroll', function(e){
var st = $(this).scrollTop();
if (st > lastScrollTop1){
} else {
if( st == 0 ){
$('body').removeClass('shifted');
}
}
lastScrollTop1 = st;
});
$('.slide-dots').css({'margin-top':-$('.slide-dots').height() / 2});
$('.slide-dot').first().addClass('active');
$(document).on('click', '.slide-dot', function(e){
e.preventDefault();
showSlide( $(this).index() );
});
$(document).on('click', '.slide-dot-fake', function(e){
e.preventDefault();
$('body').addClass('shifted');
});
});
And for those wondering why I'm not using something like fullPage.js, it's because it can't handle the way I want to transition between the two areas and have two scrollbars (one for each area).
You can use:
e.originalEvent.wheelDelta
instead of:
st > lastScrollTop1
in the mousewheel event for your third problem to check if the user has scrolled up or down. And also change the +/- in prevSlide. I used dm4web's fiddle for your first problem. And I used:
scrollTop: offset - 1
instead of:
scrollTop: offset
for your second problem, because when the scroll reaches to the last pixel of the third element, it automatically goes to the next section, so 1 pixel is enough for it not to.
Here's the fiddle: http://jsfiddle.net/3odc8zmx/3/
As suggested by #chdltest, you could do it by using fullPage.js.
Here's an example. Go to the last section.
Code used for the example:
Javascript
$('#fullpage').fullpage({
sectionsColor: ['yellow', 'orange', '#C0C0C0', '#ADD8E6'],
scrollOverflow: true,
scrollBar: true,
afterLoad: function (anchor, index) {
//hiding the main scroll bar
if (index == 4) {
$('body, html').css('overflow', 'hidden');
}
//showing the main scroll bar
if (index == 3) {
$('body, html').css('overflow', 'visible');
}
}
});
CSS (in case you prefer to use the normal style for it)
/* Normal style scroll bar
* --------------------------------------- */
.slimScrollBar {
display: none !important;
}
.fp-scrollable {
overflow: auto !important;
}
Advantages of using fullPage.js instead to your own code:
Strongly tested in different devices and browsers. (IE, Opera, Safari, Chrome, Firefox..)
Prevent problems with trackpads, Apple laptops trackpads or Apple Magic Mouse.
Old browser's compatibility, such as IE 8, Opera 12...
Touch devices compatibility (IE Windows Phone, Android, Apple iOS, touch desktops...)
It provides many other useful options and callbacks.

jQuery wait for an action to complete on different functions

I have created a newsfeed. The feed switches every 2 seconds. You can also manually switch left/right, or click the panel from the squares at the bottom. The switching between slides is down using jQuery UI Slide.
Right now, if you are in the middle of a slide, and you click left/right/squares, then another slide occurs on top of the existing, still going slide and the whole system is messed up.
How can I prevent other actions occurring if a slide/switch is already in progress?
This is my code:
$(document).ready(function(){
newsfeedTimer = setInterval(newsfeed, displayDuration);
// Manual change of feed (LEFT)
$('#newsfeeds_wrapper > .left').click(function(event){
event.stopPropagation();
feedLeft();
clearInterval(newsfeedTimer);
newsfeedTimer = setInterval(newsfeed, displayDuration);
});
// Very similar code for feed right
// Ignore the other method of switching (if it works for above, I can implement it for this one)
});
function newsfeed() {
feedRight();
}
// Feed to the Right
// jump is used to jump multiple newsfeed instead of one at a time
function feedRight(jump)
{
jump = typeof jump !== 'undefined' ? jump : 1;
var current = $('.newsfeed:first');
var next = $('.newsfeed:nth(' + jump + ')');
current.hide('slide',{duration: transitionDuration}, function(){
// Append as many needed
for( var i = 0; i < jump; i++ ) {
$('.newsfeed:first').appendTo('#newsfeeds');
}
next.show('slide',{direction : 'right' , duration: transitionDuration});
}
I don't want to stop() an animation! I want to disable changing the slides IF there is animation happening!!
without seeing the full breadth of the code, I am shooting myself in the foot here. But here is a direction I would take it. You could also have two functions, one to bind, another to unbind. When animation is initiated, you unbind the left/right controls. When stopped, you bind. Or, set a global variable... ala.
var config = {'inProgress': false};
$('#newsfeeds_wrapper > .left').click(function(event){
if(!config.inProgress){
event.stopPropagation();
feedLeft();
clearInterval(newsfeedTimer);
newsfeedTimer = setInterval(newsfeed, displayDuration);
}
});
in your animation function. Seems like when you cut/paste, some of the code is lost, so lets just assume some animation.
when you enter your animation functions, set config.inProgress = true;
function feedRight(jump)
{
config.inProgress = true;
// removed your code, but just using for simplicity sake
// added a callback
next.show('slide',{direction : 'right' , duration: transitionDuration},
function() {
// Animation complete. Set inProgress to false
config.inProgress = false;
});
)
}

Start animation on progress bar when its visible on screen

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.

Any way to make time interval not clickable in fullcalendar?

I want time intervals, occupied by events, to be not clickable. If I just set event property editable to false, it does not help: I am still able to click near that event. Any way to make all the time interval, occupied by the event not clickable? Maybe somehow stretch its width to cover the whole day (actually, this would be a desirable behaviour)?
This code will fire if a day is occupied by an event. So in theory you can block a click by doing return false; in that logic.
http://jsfiddle.net/ppumkin/2QAY4/
The code that does the magic needs jquery. and you need this piece of code.
dayClick: function(date, allDay, jsEvent, view) {
if ($('div.fc-event').length > 0) {
//
var containerD = $(this).offset();
var containerH = $(this).height();
var mousex = jsEvent.pageX;
$('div.fc-event').each(function(index) {
var offset = $(this).offset();
if (((offset.left + $(this).outerWidth()) > mousex && offset.left < mousex) && ((offset.top > containerD.top) && (offset.top < (containerD.top + containerH)))) {
alert($(this).html());
//This will only fire if an empty space is clicked
//This will not fire if an event is clicked on a day
}
});
}
else {
//Put code here to do things if no events on a day
alert('There are no events on this day');
}
},
Well, you can stretch the events to the full height of a day, using the following CSS:
.fc-event-skin { height: 60px; }
But I would call that a workarround.
fullCalendar is designed to display multiple events on a day. Hence the bars are small enough to display overlapping events.
A better solution for a booking system would be a calendar that does not support overlapping events. Unfortunately there is none I could recommend.

Categories

Resources