jQuery UI tooltip not displaying - javascript

I want to show a tooltip popup when clicking on a date in the UI Datepicker:
(function($) {
var events = [
{ Title: "Five K for charity", Date: new Date("09/16/2013") },
{ Title: "Dinner", Date: new Date("09/17/2013") },
{ Title: "Meeting with manager", Date: new Date("09/18/2013") }
];
//console.log(events[1]);
$("#my-calendar" ).datepicker({
beforeShowDay: function(date) {
var result = [true, '', null];
var matching = $.grep(events, function(event) {
//console.log(event.Date.valueOf() );
return event.Date.valueOf() === date.valueOf();
});
if (matching.length) {
result = [true, 'highlight', null];
}
return result;
},
onSelect: function(dateText) {
var date,
selectedDate = new Date(dateText),
i = 0,
event = null;
/* Determine if the user clicked an event: */
while (i < events.length && !event) {
date = events[i].Date;
if (selectedDate.valueOf() === date.valueOf()) {
event = events[i];
}
i++;
}
if (event) {
/* If the event is defined, perform some action here; show a tooltip, navigate to a URL, etc. */
var day = new Date(event.Date).getDate().toString();
$("a.ui-state-default:contains("+day+")").tooltip({content:'yay'}); //selector definitely returns the relevant day
}
}
});
})(jQuery);
However, no tooltip appears when I click on the date. The tooltip eventually needs to contain links to events.
Example here

Related

How do I speed up Javascript event handlers?

I implemented a list of courses so that when a course name is hovered over, the corresponding dates on the calendar are highlighted.
Javascript parses the dates when it is loaded and stores it with the DOM element, so that on mouseenter and mouseleave, JQuery highlights or removes the highlighting of the corresponding table cells in the calendar.
However, the page takes 8 seconds for the list of courses and the calendar to load and another 1 second to highlight or remove the highlighting of the table cells. coursesLoaded is when the course list finishes loading.
(function($) {
$(window).load(function() {
if ($('.calendar').length) {
var calendarOs = [];
$('.calendar').each(function(i, v){
$(this).html('');
$(this).prev().hide();
var calendar = $(this);
calendar.attr('index', i);
calendarO = new FullCalendar.Calendar(calendar[0], {
plugins: [ 'dayGrid' ],
defaultView: 'dayGridMonth',
header: {
left: '',
center: 'title',
right: ''
},
titleFormat: {
year: 'numeric',
month: 'long'
},
themeSystem: 'bootstrap',
fixedWeekCount: true,
contentHeight: "auto",
navLinks: false,
eventRender: function(event, eventElement) {
eventElement.addClass('calendar');
},
validRange: function(currentDate) {
return {
start: new Date(currentDate.getFullYear(), currentDate.getMonth(), 1),
// end: new Date(currentDate.getFullYear(), currentDate.getMonth() + 4, 0)
};
}
});
calendarO.render();
calendarOs.push(calendarO);
});
var year = new Date().getFullYear();
var date, location, color, array, td;
//make update only one color
var updateColors = function(index) {
var calendar = $(`.calendar[index='${index}']`);
var calendarO = calendarOs[index];
var month = calendarO.getDate().getMonth();
var datesO = JSON.parse(calendar.attr('dates'));
calendar.find('td.richmond, td.burnaby').removeClass('richmond burnaby');
var traverseDates = (month == 0) ? [11, 0, 1] : ((month == 11) ? [10, 11, 0] : [month - 1, month, month + 1]);
traverseDates.forEach(function(month) {
datesO[month].forEach(function(obj) {
// console.log(obj)
calendar.find(`td[data-date=${obj.date}]:not(.fc-day-top)`).addClass(obj.location);
});
});
}
$(document).on(`coursesLoaded`, function(e, calendarIndex, dates) {
// console.log('coursesLoaded ' + calendarIndex);
var calendar = $(`.calendar[index='${calendarIndex}']`);
var calendarO = calendarOs[calendarIndex];
calendar.attr('dates', JSON.stringify(dates));
console.log(calendar.attr('dates'));
updateColors(calendarIndex);
calendar.parent().prev().find('ul.courses-list li a').hover(function(e){
console.log("hovering over: " + performance.now());
calendar.find('td.active').removeClass('active');
location = $(this).attr('location');
array = JSON.parse($(this).attr('dates'));
calendarO.gotoDate(array[0]);
updateColors(calendarIndex);
if (e.type == 'mouseenter') {
array.forEach(function(element) {
calendar.find(`td[data-date=${element}]:not(.fc-day-top)`).addClass('active');
});
}
console.log("done hovering: " + performance.now());
});
});
};
$(document).trigger('doneCalendars');
});
})(jQuery)
Here is the website: http://ess.ccmcanada.org/certified-training/

How to improve my AJAX live search by doing less amount of requests

I am building a AJAX live search page.So far everything is working as intended, but I have noticed that I am doing TONS of AJAX calls.
I know where and why this is happening, but I cannot find a way to stop these AJAX calls from happening.
I will try to give a quick explanation and paste the code below afterwards.
On the left side of the page I have a bunch of filters (e.g. location, radius, interests, engagements, daterange, ...). Each and every one of them has an event listener on them (eg changed). When one of those listeners is triggered, I update my query parameters and request the results via AJAX.
Now the problem occurs when for example someone has selected 10 interests (there are 36 of them), and then he shares the URL, it would look like this:
http://localhost/codeigniter/nl-be/sociale-teambuildings/zoeken?locations=&distance=0&minemployees=0&maxemployees=1000&minprice=0&maxprice=50000&interests=1%3B2%3B3%3B4%3B5%3B6%3B7%3B8%3B9%3B10&sdgs=&startdate=2018-12-03&enddate=2019-12-03&engagements=
Now first off, I will say that I am using iCheck.js for my checkboxes. This means that there's a check on the 'ifChecked' event.
Since the list contains 10 interest entries, the 'ifChecked' event will be fired 10 times, and thus resulting in 10 AJAX request. Now consider this in combination with 5 SDG's, 2 engagaments, 3 locations, ... and I end up with a ton of AJAX requests.
As well while removing all of my interests at the same time (there's a 'remove' link), it will fire the 'ifUnchecked' event 10 times, and thus again perform 10 AJAX requests.
This is my full JS code, i've tried creating a jsfiddle with the HTML and all, but the code is a bit intermingled with CodeIgniter framework and hard to place in there. But the JS code is enough to get the picture across.
//Set vars available to entire scope for filtering
var searchLocation = null;
var searchRadius = 0;
var searchMin = 0;
var searchMax = 1000;
var searchMinPrice = 0;
var searchMaxPrice = 50000;
var searchSelectedInterests = [];
var searchSelectedSdgs = [];
var searchStartDate = moment().format('YYYY-MM-DD');
var searchEndDate = moment().add(1, 'years').format('YYYY-MM-DD');
var searchSelectedEngagements = [];
var getUrl = window.location;
var baseUrl = getUrl .protocol + "//" + getUrl.host + "/" + getUrl.pathname.split('/')[1];
$(document).ready(function(){
'use strict';
// Square Checkbox & Radio
$('.skin-square input').iCheck({
checkboxClass: 'icheckbox_square-blue'
});
$('.searchinterests input').on('ifChecked', function(event) {
var interestid = event.target.value;
searchSelectedInterests.push(interestid);
updateURLParameters();
performSearch();
});
$('.searchinterests input').on('ifUnchecked', function(event) {
var interestid = event.target.value;
var arrayPos = $.inArray(interestid, searchSelectedInterests);
if (arrayPos > -1) {
searchSelectedInterests.splice(arrayPos, 1);
}
performSearch();
});
$('.searchsdgs input').on('ifChecked', function(event) {
var sdgid = event.target.value;
searchSelectedSdgs.push(sdgid);
updateURLParameters();
performSearch();
});
$('.searchsdgs input').on('ifUnchecked', function(event) {
var sdgid = event.target.value;
var arrayPos = $.inArray(sdgid, searchSelectedSdgs);
if (arrayPos > -1) {
searchSelectedSdgs.splice(arrayPos, 1);
}
performSearch();
});
$('.searchengagements input').on('ifChecked', function(event) {
var social_engagement_id = event.target.value;
searchSelectedEngagements.push(social_engagement_id);
updateURLParameters();
performSearch();
});
$('.searchengagements input').on('ifUnchecked', function(event) {
var social_engagement_id = event.target.value;
var arrayPos = $.inArray(social_engagement_id, searchSelectedEngagements);
if (arrayPos > -1) {
searchSelectedEngagements.splice(arrayPos, 1);
}
performSearch();
});
var searchLocationSelect = $('.stb-search-location');
var radiusSlider = document.getElementById('radius-slider');
var minMaxSlider = document.getElementById('min-max-slider');
var priceSlider = document.getElementById('price-slider');
var daterangepicker = $('#searchdaterange');
var curr_lang = $('#curr_lang').val();
switch(curr_lang) {
case 'nl':
moment.locale('nl');
daterangepicker.daterangepicker({
minDate: moment(),
startDate: moment(),
endDate: moment().add(1, 'years'),
ranges: {
'Volgende week': [moment(), moment().add(1, 'weeks')],
'Volgende maand': [moment(), moment().add(1, 'months')],
'Volgende 3 maanden': [moment(), moment().add(3, 'months')],
'Volgende 6 maanden': [moment(), moment().add(6, 'months')],
'Volgend jaar': [moment(), moment().add(1, 'years')]
},
alwaysShowCalendars: true,
locale: {
"customRangeLabel": "Vrije keuze",
"format": "DD-MM-YYYY"
}
});
break;
case 'en':
moment.locale('en');
daterangepicker.daterangepicker({
minDate: moment(),
startDate: moment(),
endDate: moment().add(1, 'years'),
ranges: {
'Next week': [moment(), moment().add(1, 'weeks')],
'Next month': [moment(), moment().add(1, 'months')],
'Next 3 months': [moment(), moment().add(3, 'months')],
'Next 6 months': [moment(), moment().add(6, 'months')],
'Next year': [moment(), moment().add(1, 'years')]
},
alwaysShowCalendars: true,
locale: {
"customRangeLabel": "Free choice",
"format": "DD-MM-YYYY"
}
});
break;
case 'fr':
moment.locale('fr');
daterangepicker.daterangepicker({
minDate: moment(),
startDate: moment(),
endDate: moment().add(1, 'years'),
ranges: {
'Semaine prochaine': [moment(), moment().add(1, 'weeks')],
'Mois prochain': [moment(), moment().add(1, 'months')],
'3 mois prochains': [moment(), moment().add(3, 'months')],
'6 mois prochains': [moment(), moment().add(6, 'months')],
'L\'année prochaine': [moment(), moment().add(1, 'years')]
},
alwaysShowCalendars: true,
locale: {
"customRangeLabel": "Libre choix",
"format": "DD-MM-YYYY"
}
});
break;
}
daterangepicker.on('hide.daterangepicker', function (ev, picker) {
var startdate = picker.startDate.format('YYYY-MM-DD');
var enddate = picker.endDate.format('YYYY-MM-DD');
setStartDate(startdate);
setEndDate(enddate);
updateURLParameters();
performSearch();
});
if (searchLocationSelect.length) {
searchLocationSelect.selectize({
create: false,
sortField: {
field: 'text',
direction: 'asc'
},
dropdownParent: 'body',
plugins: ['remove_button'],
onChange: function(value) {
setLocation(value);
var size = value.length;
if (size == 1) {
enableRadius(radiusSlider);
} else {
disableAndResetRadius(radiusSlider);
}
updateURLParameters();
performSearch();
}
});
}
noUiSlider.create(radiusSlider, {
start: [0],
step: 5,
range: {
'min': 0,
'max': 100
}
});
var radiusNodes = [
document.getElementById('radius-value')
];
// Display the slider value and how far the handle moved
// from the left edge of the slider.
radiusSlider.noUiSlider.on('update', function (values, handle, unencoded, isTap, positions) {
var value = values[handle];
radiusNodes[handle].innerHTML = Math.round(value);
});
radiusSlider.noUiSlider.on('set', function (value) {
setRadius(value);
updateURLParameters();
performSearch();
});
var minmax_slider_options = {
start: [0,1000],
behaviour: 'drag',
connect: true,
tooltips: [wNumb({
decimals: 0
}), wNumb({
decimals: 0
})],
range: {
'min': 0,
'max': 1000
},
step: 5
};
noUiSlider.create(minMaxSlider, minmax_slider_options);
var minMaxNodes = [
document.getElementById('minmax-lower-value'),
document.getElementById('minmax-upper-value')
];
// Display the slider value and how far the handle moved
// from the left edge of the slider.
minMaxSlider.noUiSlider.on('update', function (values, handle, unencoded, isTap, positions) {
var value = values[handle];
minMaxNodes[handle].innerHTML = Math.round(value);
});
minMaxSlider.noUiSlider.on('set', function (values) {
setMin(values[0]);
setMax(values[1]);
updateURLParameters();
performSearch();
});
var price_slider_options = {
start: [0,50000],
behaviour: 'drag',
connect: true,
tooltips: [wNumb({
decimals: 0
}), wNumb({
decimals: 0
})],
range: {
'min': 0,
'max': 50000
},
step: 250
};
noUiSlider.create(priceSlider, price_slider_options);
var priceNodes = [
document.getElementById('price-lower-value'), // 1000
document.getElementById('price-upper-value') // 3500
];
// Display the slider value and how far the handle moved
// from the left edge of the slider.
priceSlider.noUiSlider.on('update', function (values, handle, unencoded, isTap, positions) {
var value = values[handle];
priceNodes[handle].innerHTML = '€'+Math.round(value);
});
priceSlider.noUiSlider.on('set', function (values) {
setMinPrice(values[0]);
setMaxPrice(values[1]);
updateURLParameters();
performSearch();
});
/** Reset methods **/
$('#resetLocations').on('click', function(e) {
e.preventDefault();
var locationInputField = $('.stb-search-location');
var control = locationInputField[0].selectize;
control.clear();
});
$('#resetRadius').on('click', function(e) {
e.preventDefault();
document.getElementById('radius-slider').noUiSlider.set(0);
});
$('#resetMinMax').on('click', function(e) {
e.preventDefault();
document.getElementById('min-max-slider').noUiSlider.set([0,1000]);
});
$('#resetPrice').on('click', function(e) {
e.preventDefault();
document.getElementById('price-slider').noUiSlider.set([0,50000]);
});
$('#resetInterests').on('click', function(e) {
e.preventDefault();
searchSelectedInterests = [];
$("input[name='interests[]']").iCheck('uncheck');
});
$('#resetSdgs').on('click', function(e) {
e.preventDefault();
searchSelectedSdgs = [];
$("input[name='sdgs[]']").iCheck('uncheck');
});
$('#resetDate').on('click', function(e) {
e.preventDefault();
$('#searchdaterange').data('daterangepicker').setStartDate(moment());
$('#searchdaterange').data('daterangepicker').setEndDate(moment().add(1, 'years'));
var startdate = $('#searchdaterange').data('daterangepicker').startDate.format('YYYY-MM-DD');
var enddate = $('#searchdaterange').data('daterangepicker').endDate.format('YYYY-MM-DD');
setStartDate(startdate);
setEndDate(enddate);
performSearch();
});
$('#resetEngagement').on('click', function(e) {
e.preventDefault();
searchSelectedEngagements = [];
$("input[name='engagement[]']").iCheck('uncheck');
});
// Set initial parameters (and pre-fill the filters based on query params)
setupConfig(radiusSlider);
});
function getQueryStringValue(){
var currentURL = new URI();
var queryParams = currentURL.query(true);
if ($.isEmptyObject(queryParams) === false) {
return queryParams;
} else {
return undefined;
}
}
//In here we read the query parameters from the URL and set the filters to the query parameters (+ do initial filtering)
function setupConfig(radiusSlider) {
var queryParams = getQueryStringValue();
if (queryParams !== undefined) {
var locations = queryParams.locations;
if (locations !== undefined) {
var locationsArray = locations.split(";");
fillLocations(locationsArray);
if (locationsArray.length != 1) {
disableAndResetRadius(radiusSlider);
}
} else {
disableAndResetRadius(radiusSlider);
}
var distance = queryParams.distance;
if (distance !== undefined) {
if (locationsArray.length != 1) {
disableAndResetRadius(radiusSlider);
} else {
document.getElementById('radius-slider').noUiSlider.set(distance);
}
}
var minEmployees = queryParams.minemployees;
var maxEmployees = queryParams.maxemployees;
if ((minEmployees !== undefined) && (maxEmployees !== undefined)) {
document.getElementById('min-max-slider').noUiSlider.set([minEmployees,maxEmployees]);
}
var minPrice = queryParams.minprice;
var maxPrice = queryParams.maxprice;
if ((minPrice !== undefined) && (maxPrice !== undefined)) {
document.getElementById('price-slider').noUiSlider.set([minPrice,maxPrice]);
}
var interests = queryParams.interests;
if (interests !== undefined) {
var interestsArray = interests.split(";");
fillInterests(interestsArray);
}
var sdgs = queryParams.sdgs;
if (sdgs !== undefined) {
var sdgsArray = sdgs.split(";");
fillSdgs(sdgsArray);
}
var startdate = queryParams.startdate;
var enddate = queryParams.enddate;
if ((startdate !== undefined) && (enddate !== undefined)) {
$('#searchdaterange').data('daterangepicker').setStartDate(moment(startdate));
$('#searchdaterange').data('daterangepicker').setEndDate(moment(enddate));
var startdate = $('#searchdaterange').data('daterangepicker').startDate.format('YYYY-MM-DD');
var enddate = $('#searchdaterange').data('daterangepicker').endDate.format('YYYY-MM-DD');
setStartDate(startdate);
setEndDate(enddate);
}
var engagements = queryParams.engagements;
if (engagements !== undefined) {
var engagementsArray = engagements.split(";");
fillEngagements(engagementsArray);
}
} else {
disableAndResetRadius(radiusSlider);
performSearch();
}
}
function fillLocations(locations) {
var selectize = $('.stb-search-location');
selectize[0].selectize.setValue(locations);
}
function fillInterests(interests) {
for (var i = 0; i < interests.length; i++) {
var checkboxId = "interest-"+interests[i];
var checkbox = $('#'+checkboxId);
checkbox.iCheck('check');
}
}
function fillSdgs(sdgs) {
for (var i = 0; i < sdgs.length; i++) {
var checkboxId = "sdg-"+sdgs[i];
var checkbox = $('#'+checkboxId);
checkbox.iCheck('check');
}
}
function fillEngagements(engagements) {
for (var i = 0; i < engagements.length; i++) {
var checkboxId = "eng-"+engagements[i];
var checkbox = $('#'+checkboxId);
checkbox.iCheck('check');
}
}
function getCurrLang() {
return $('#curr_lang').val();
}
function getCurrLangSegment() {
return $('#curr_abbr').val();
}
function getLocation() {
return $('#location').val();
}
function setLocation(value) {
searchLocation = value;
}
function setRadius(value) {
searchRadius = value;
}
function disableAndResetRadius(radiusSlider) {
radiusSlider.noUiSlider.set(0);
radiusSlider.setAttribute('disabled', true);
}
function enableRadius(radiusSlider) {
radiusSlider.removeAttribute('disabled');
}
function setMin(value) {
searchMin = value;
}
function setMax(value) {
searchMax = value;
}
function setMinPrice(value) {
searchMinPrice = value;
}
function setMaxPrice(value) {
searchMaxPrice = value;
}
function setStartDate(value) {
searchStartDate = value;
}
function setEndDate(value) {
searchEndDate = value;
}
function performSearch() {
$('#stb-items-placeholder').html('<div id="loading"><span>'+res.StbSearchPlaceholder+'</span></div>');
var searchOptions = {
type: 'POST',
url: baseUrl + '/dashboard/socialteambuildings/search/getFilteredStbs',
dataType: 'json'
};
var filterdata = {
"curr_lang" : getCurrLang(),
"curr_abbr" : getCurrLangSegment(),
"location" : getLocation(),
"interests": searchSelectedInterests,
"sdgs": searchSelectedSdgs
};
var options = $.extend({}, searchOptions, {
data: filterdata
});
// ajax done & fail
$.ajax(options).done(function (data) {
var mustacheJsonData = data.result;
var html = Mustache.render( $('#stbItemGen').html(), mustacheJsonData );
$('#stb-items-placeholder').html( html );
});
}
function updateURLParameters() {
if (searchLocation != null) {
var locationsUrlString = searchLocation.join(";");
}
var distanceUrlString = Math.round(searchRadius[0]);
var minEmployeesUrlString = Math.round(searchMin);
var maxEmployeesUrlString = Math.round(searchMax);
var minPriceUrlString = Math.round(searchMinPrice);
var maxPriceUrlString = Math.round(searchMaxPrice);
var interestUrlString = searchSelectedInterests.join(";");
var sdgUrlString = searchSelectedSdgs.join(";");
var startDateUrlString = searchStartDate;
var endDateUrlString = searchEndDate;
var engagementUrlString = searchSelectedEngagements.join(";");
var params = {locations: locationsUrlString, distance: distanceUrlString, minemployees: minEmployeesUrlString, maxemployees: maxEmployeesUrlString, minprice: minPriceUrlString, maxprice: maxPriceUrlString, interests: interestUrlString, sdgs: sdgUrlString, startdate: startDateUrlString, enddate: endDateUrlString, engagements: engagementUrlString};
var query = $.param(params);
addURLParameter(query);
}
//This function removes all the query parameters and adds the completely newly built query param string again
function addURLParameter(queryString){
var currentUrl = window.location.href;
var urlNoQueryParams = currentUrl.split("?");
var baseUrl = urlNoQueryParams[0];
var result = baseUrl + "?" + queryString;
window.history.replaceState('', '', result);
}
I tried using e.stopPropagation() and e.stopImmediatePropagation() on the remove option for example. This does not stop the events going back to the iCheck library.
Debounce will not work here as the problem is NOT in just one event listener having too many requests in a short amount of time, but with many independent event listeners firing one after the other.
Possible solutions:
Add a button eg SEARCH which will actually perform the search, instead of this being triggered by individual updates. This is a nice and simple solution to the many independent listeners problem.
If you dont want to add a new button do sth like the following. Add a timeinterval with setInterval to perform a search with AJAX. And have a flag whether a search should be performed. Then when either listener on checkbox changes simply set the flag to true. Also if a request is already in progress do NOT make another AJAX request untill the current one finishes.
pseudocode follows:
var do_search = false, timer = null, doing_ajax = false, TROTTLE = 500;
timer = setTimeout(performSearch, TROTTLE);
function performSearch()
{
if ( !do_search || doing_ajax )
{
timer = setTimeout(performSearch, TROTTLE);
return;
}
doing_ajax = true;
do_search = false;
// do the ajax request here
// ...
// NOTE: on ajax complete reset the timer, eg timer = setTimeout(performSearch, TROTTLE);
// and set doing_ajax = false;
}
// then your checkboxes listeners will simply update the do-search flag eg:
$('.searchsdgs input').on('ifChecked', function(event) {
var sdgid = event.target.value;
searchSelectedSdgs.push(sdgid);
updateURLParameters();
//performSearch();
do_search = true;
});
$('.searchsdgs input').on('ifUnchecked', function(event) {
var sdgid = event.target.value;
var arrayPos = $.inArray(sdgid, searchSelectedSdgs);
if (arrayPos > -1) {
searchSelectedSdgs.splice(arrayPos, 1);
}
//performSearch();
do_search = true;
});
$('.searchengagements input').on('ifChecked', function(event) {
var social_engagement_id = event.target.value;
searchSelectedEngagements.push(social_engagement_id);
updateURLParameters();
//performSearch();
do_search = true;
});
$('.searchengagements input').on('ifUnchecked', function(event) {
var social_engagement_id = event.target.value;
var arrayPos = $.inArray(social_engagement_id, searchSelectedEngagements);
if (arrayPos > -1) {
searchSelectedEngagements.splice(arrayPos, 1);
}
//performSearch();
do_search = true;
});
NOTE you can adjust the TROTTLE interval to balance between more immediate interactivity and less AJAX requests. Experiment with different values to get a feeling for it for your app.
NOTE2 You can build on this example and make it like a multi-debounce function for example by clearing the timeout and resetting it in each individual listener (instead of simply setting do_search=true you can set do_search=true and clear the previous interval and reset it again). This will make sure only the last update will be performed.

How to highlight dates of calendar if event in between two dates jquery?

code:
<script>
$(document).ready(function(){
var events = [{Title:"Delhi Sight Seeing – Jama Masjid, Rajghat, President House, India Gate & Qutab Minar",Duration:"3 days, 2 nights",Date:new Date("08/29/2018"),End:new Date("08/31/2018")}];
$("#calen").datepicker({
beforeShowDay: function(date) {
var result = [true, '', null];
var matching = $.grep(events, function(event) {
return event.Date.valueOf() === date.valueOf();
});
if (matching.length) {
result = [true, 'highlight', null];
}
return result;
},
onSelect: function(dateText) {
var date,
selectedDate = new Date(dateText),
i = 0,
event = null;
while (i < events.length && !event) {
date = events[i].Date;
if (selectedDate.valueOf() === date.valueOf()) {
event = events[i];
}
i++;
}
if (event) {
alert(event.Title);
alert(event.Duration);
}
}
});
});
</script>
I have created event calendar which is work perfectly for single date now I want to show those dates and onclick on any dates they show event title.
For example: suppose Date is 08/29/2018 and End is 08/31/2018. Then calendar must highlights these dates 08/29/2018,08/30/2018 and 08/31/2018.
So, How can I do this ?

How to disable dates between the saved events in fullcalendar

I am using a fullcalendar for creating schedules. What I am doing is, for each week, there must be only 3 schedules (processing date, payout date and credit date). If there are already schedules for that week, I need to prompt the user that schedules are already set. But if there's no schedule set, user can still post new schedule. I am already done with the logic of this scheduler, the only problem I have is how to disable dates between the set schedules for the week?
Example, I set 04-24-2018(processing date), 04-24-18(payout date) and 04-26-18(credit date)..
How can I disable the 04-22-18,04-23-18,04-25-18,04-27-18 and 04-28-18 on that week so that user cant create new schedules for that week?
schedule image
JAVASCRIPT
select:
function (start, end, jsEvent, view, resource) {
IsDateHasEvent(start);
}
function IsDateHasEvent(date) {
var allEvents = [];
allEvents = $('#calendar').fullCalendar('clientEvents');
var event = $.grep(allEvents, function (v) {
//alert(v.start);
//return +v.start == +date;
if (v.start <= date) {
$("#eventIsAlreadySetModal").modal();
//alert(v.start);
}
});
return event.length > 0;
}
I can get all the dates with events whenever I try to alert the value of start date. But the dates between are still not disabled.
Can someone help me through this?
Thank you so much.
Full Javascript code
$(document).ready(function () {
var events = [];
var selectedEvent = null;
FetchEventAndRenderCalendar();
// ******************************************
// GET ALL SCHEDULES AND DISPLAY IN CALENDAR
// ******************************************
function FetchEventAndRenderCalendar() {
$.ajax({
type: 'GET',
url: '#Url.Action("GetSchedule")',
success: function (data) {
$.each(data, function (i, v) {
var eColor = "";
if (v.status == 'Completed')
{
eColor = '#3498DB';
}
if (v.status == 'Active') {
eColor = '#2CB05B';
}
if (v.status == 'Pending') {
eColor: '#DE6209';
}
events.push({
eventID: v.scheduleId,
title: v.processedDescription,
start: moment(v.processedDatetimeStart),
status: v.status,
color: eColor
});
events.push({
eventID: v.scheduleId,
title: v.payoutDescription,
start: moment(v.payoutDatetimeStart),
status: v.status,
color: eColor
});
events.push({
eventID: v.scheduleId,
title: v.creditDescription,
start: moment(v.creditDatetimeStart),
status: v.status,
color: eColor,
end: moment(v.creditDatetimeStart)
});
})
GenerateCalendar(events);
},
error: function (error) {
alert('failed');
}
})
}
// ******************************************
// GENERATE THE CALENDAR VIEW AND SCHEDULES
// ******************************************
function GenerateCalendar(events) {
$('#calendar').fullCalendar('destroy');
$('#calendar').fullCalendar({
contentHeight: 500,
header: {
left: 'prev,next today',
center: 'title',
right: 'month, agendaWeek, agendaDay, listWeek'
},
navLinks: true,
editable: true,
eventLimit: true,
eventColor: '#2CB05B',
droppable: false,
timeFormat: 'h(:mm)A',
timeZone: 'local',
events: events,
// **************************************
// display the saved schedule in calendar
// **************************************
eventClick:
function (calEvent, jsEvent, view) {
$("#statusLabel").text(calEvent.status);
$("#schedId").val(calEvent.eventID);
$("#schedDesc").html(calEvent.title);
$("#txtDateStart_Edit").val(calEvent.start.format("MM-DD-YYYY HH:mm A"));
$('#modalEditSchedule').modal();
if ($("#statusLabel").html() == "Completed")
{
$("#btnEditSched").hide();
}
if ($("#statusLabel").html() == "Active") {
$("#btnEditSched").hide();
}
},
// *************************************************
// select dates in calendar for posting new schedule
// *************************************************
selectable: true,
selectOverlap: true,
select:
function (start, end, jsEvent, view, resource) {
IsDateHasEvent(start);
},
// *********************************************
// disable past navigation button for past dates
// *********************************************
viewRender: function (currentView) {
var minDate = moment();
// Past dates
if (minDate >= currentView.start) {
$(".fc-prev-button").prop('disabled', true);
$(".fc-prev-button").addClass('fc-state-disabled');
}
else {
$(".fc-prev-button").removeClass('fc-state-disabled');
$(".fc-prev-button").prop('disabled', false);
}
},
// ******************************
// disable past dates in calendar
// ******************************
validRange: function (dateNow) {
return {
start: dateNow.subtract(1, 'days')
};
}
, dayClick: function (date) {
var events = $('#calendar').fullCalendar('clientEvents');
for (var i = 0; i < events.length; i++) {
//if (moment(date).isSame(moment(events[i].start))) {
if (moment(events[i].start) <= moment(date)) {
alert('with events');
break;
}
else //if (i == events.length - 1)
{
alert('none');
}
}
}
});
}
// **********************************
// show modal for adding new schedule
// **********************************
function openAddEditForm() {
$('#modalAddSchedule').modal();
}
});
function IsDateHasEvent(date) {
var allEvents = [];
allEvents = $('#calendar').fullCalendar('clientEvents');
var event = $.grep(allEvents, function (v) {
//alert(v.start);
//return +v.start == +date;
if (v.start <= date) {
$("#eventIsAlreadySetModal").modal();
//alert(v.start);
}
});
return event.length > 0;
}
You need to:
check if there's at least 3 schedules on the same week;
if is there, then disable the other dates of that week.
Right? I'll try to solve the first part of your problem with javascript Date class. I don't know about FullCalendar, so if anyone can solve that part I would be glad, hehe.
We must check when a week starts and when it ends. Just with that we'll get ready to do some crazy stuff.
function printDate(year, month, day) {
month = (month < 10 ? '0' : '') + month.toString();
day = (day < 10 ? '0' : '') + day.toString();
return year + '-' + month + '-' + day;
}
function weekStart(dateString) {
var dateObject = new Date(dateString);
var dayOfWeek = dateObject.getDay();
if(dayOfWeek > 0) {
dateObject.setDate(day - dayOfWeek);
}
return printDate(dateObject.getFullYear(), dateObject.getMonth()+1, dateObject.getDate());
}
function weekEnd(dateString) {
var dateObject = new Date(dateString);
var dayOfWeek = dateObject.getDay();
if(dayOfWeek < 6) {
dateObject.setDate(day + (6-dayOfWeek));
}
return printDate(dateObject.getFullYear(), dateObject.getMonth()+1, dateObject.getDate());
}
function weekRange(dateString) {
return [weekStart(dateString), weekEnd(dateString)];
}
Nice, now we can get a "week range" from a date. But from that, can we get all dates of that week? Sure.
function getDatesFromWeek(wStart) {
var dates = [],
date = new Date(wStart),
count = 0;
while(count <= 6) {
date.setDate(date.getDate() + count);
dates.push(printDate(date.getFullYear(), date.getMonth()+1, date.getDate());
count++;
}
return dates;
}
Perfect. So now we should count for each range. Assuming you're receiving your info on a variable called schedules and each schedule have an index called date:
var weeks = {}, lockedDates = [];
for(var x in schedules) {
var week = weekRange(schedules[x].date);
var weekID = week.join('-');
if(weeks[weekID] == undefined) {
weeks[weekID] = 1;
} else {
weeks[weekID]++;
}
if(weeks[weekID] == 3) {
lockedDates = lockedDates.concat(getDatesFromWeek(week[0]));
}
}
Then you have all those dates to disable listed on lockedDates variable in format YYYY-MM-DD. Do you know how to do the rest?
EDIT
Let's change the last part I made to this:
function Locker() {
this.dates = [];
this.weeks = {};
}
Locker.prototype.add = function(dateString) {
var wStart = weekStart(dateString);
if(this.weeks[wStart] == undefined) {
this.weeks[wStart] = 1;
} else {
this.weeks[wStart]++;
}
if(this.weeks[wStart] == 3) {
this.lock(getDatesFromWeek(wStart));
}
}
Locker.prototype.lock = function(dates) {
this.lockedDates = this.lockedDates.concat(dates);
// do something
}
var calendarLocker = new Locker();
// everytime an user add a date, call calendarLocker.add(date);
// so when it reaches the limit, the locker will call calendarLocker.lock

how to disable previous month in Full Calendar Plugin

I want to disable previous month button from full calander
Current month is April. When i clicked on previous button then calendar is showing previous March month. should not be happened.
http://jsfiddle.net/6enYL/
$(document).ready(function () {
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
var calendar = $('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title'
},
selectable: true,
selectHelper: true,
editable: true
});
});
Yep, I've modified your fiddle with lewsid's answer, and it works.
http://jsfiddle.net/6enYL/1/
jQuery('#calendar').fullCalendar({
viewDisplay : function(view) {
var now = new Date();
var end = new Date();
end.setMonth(now.getMonth() + 11); //Adjust as needed
var cal_date_string = view.start.getMonth()+'/'+view.start.getFullYear();
var cur_date_string = now.getMonth()+'/'+now.getFullYear();
var end_date_string = end.getMonth()+'/'+end.getFullYear();
if(cal_date_string == cur_date_string) { jQuery('.fc-button-prev').addClass("fc-state-disabled"); }
else { jQuery('.fc-button-prev').removeClass("fc-state-disabled"); }
if(end_date_string == cal_date_string) { jQuery('.fc-button-next').addClass("fc-state-disabled"); }
else { jQuery('.fc-button-next').removeClass("fc-state-disabled"); }
}
});
Disable past dates and view starts from today
$('#calendar').fullCalendar({
defaultView: 'agendaWeek',
firstDay :moment().weekday(),
viewRender: function(currentView){
var minDate = moment();
// Past
if (minDate >= currentView.start && minDate <= currentView.end) {
$(".fc-prev-button").prop('disabled', true);
$(".fc-prev-button").addClass('fc-state-disabled');
}
else {
$(".fc-prev-button").removeClass('fc-state-disabled');
$(".fc-prev-button").prop('disabled', false);
}
}
});
FullCalendar is not like a traditional DatePicker. There is no way to initially setup the start and end dates of what you want to show.
You have to attach to viewRender event and manipulate the calendar with logic of your own. So if the dates are less than what you want you attach a class to that tile of 'disabled' for example. And also disable the previous button your self. You also then have to re-enable the previous button on the next month. Thanks to this kind of API you build your own custom calendar, but it can take time.
FullCalendar is just a calendar. The rest is up to you.
Here is an update based on Prasad19sara answer : http://jsfiddle.net/6enYL/2/
var calendar = $('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title'
},
selectable: true,
selectHelper: true,
editable: true,
viewDisplay: function (view) {
//========= Hide Next/ Prev Buttons based on date range
if (view.end > endDate) {
$("#calendar .fc-button-next").hide();
return false;
}
else {
$("#calendar .fc-button-next").show();
}
if (view.start < startDate) {
$("#calendar .fc-button-prev").hide();
return false;
}
else {
$("#calendar .fc-button-prev").show();
}
}
});
Please be aware that viewDisplay is deprecated and will no longer be used in V2
This is my simple solution.
Place this code in the renderView function (around line 368 in version 1.5.4)) before ignoreWindowResize--; near the end of the function.
var lammCurrentDate = new Date();
var lammMinDate = new Date( lammCurrentDate.getFullYear(), lammCurrentDate.getMonth(), 1, 0, 0, 0, 0);
if (currentView.start <= lammMinDate){
header.disableButton('prev');
} else {
header.enableButton('prev');
}
For those using the FullCalendar.io version 2, you may use the following code
viewRender: function(view) {
var now = new Date();
var end = new Date();
end.setMonth(now.getMonth() + 1);
var cal_date_string = view.start.format('MM')+'/'+view.start.format('YYYY');
var cur_date_string = now.getMonth()+'/'+now.getFullYear();
var end_date_string = end.getMonth()+'/'+end.getFullYear();
if(cal_date_string == cur_date_string) { jQuery('.fc-prev-button').addClass("fc-state-disabled"); }
else { jQuery('.fc-prev-button').removeClass("fc-state-disabled"); }
if(end_date_string == cal_date_string) { jQuery('.fc-next-button').addClass("fc-state-disabled"); }
else { jQuery('.fc-next-button').removeClass("fc-state-disabled"); }
},
header:{
left: 'title',
center: '',
right: 'today prev,next'
},
Just remove "prev"... http://fullcalendar.io/docs/display/header/
in your options
If you have looking for a more recent solution (v4-compatible), look for validRange
See documentation : https://fullcalendar.io/docs/validRange
In version v2 simply set the header without the option.
Like this for example:
header: {
center: "title",
right: "month,agendaWeek,agendaDay"
},
$('#calendar').fullCalendar({
businessHours: {
start: '10:00', // a start time
end: '22:00', // an end time
dow: [ 1, 2, 3, 4, 5 ]
// days of week. an array of zero-based day of week integers (0=Sunday)
},
hiddenDays: [ 0, 6 ],
defaultView: 'agendaWeek',
viewRender: function(view) {
var now = new Date();
var end = new Date();
end.setMonth(now.getMonth() + 2);
//========= Hide Next/ Prev Buttons based on date range
if (view.end > end) {
$("#calendar .fc-next-button").hide();
return false;
}
else {
$("#calendar .fc-next-button").show();
}
if (view.start < now) {
$("#calendar .fc-prev-button").hide();
return false;
}
else {
$("#calendar .fc-prev-button").show();
}
}
});

Categories