I'm trying to make a calendar with JavaScript
. It's working fine, except when the next/previous chevron gets clicked, the entire page scrolls down, as opposed to the calendar animation only. I figure that's because the switching between months works via updating an href. So since the rest of the page can't find that href, it scrolls to the bottom.
I experimented with adding preventDefault()
and return false;
, which does prevent the entire page from scrolling. Unfortunately it also messes with the calendar page scroll to the next month. With either preventDefault()
or return false;
at the end of $("#angle_down").click(function (e)
or $("#angle_up").click(function (e)
, the calendar scroll doesn't work anymore.
I can't figure out how to solve this. Help is much appreciated.
I made a snipped so you can see the code in action.
// Check for Leap Year
function isLeap(year) {
if (year % 4 == 0) {
return 29;
} else {
return 28;
}
}
const days = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstg", "Freitag", "Samstag"];
const months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]
// This array gives the index for months with only 30 days. September = 9
const thirty = [9, 4, 6, 11];
let d = new Date();
let currentMonth = d.getMonth();
let currentDate = d.getDate();
let currentDay = d.getDay();
let currentYear = d.getFullYear();
// limit displayed months
let maxMonths = 32;
// Create text based upon the selected date.
$("#selectedDate").text(`${currentDate}. ${months[currentMonth]} ${currentYear}`);
$("#month h3").text(`${months[currentMonth]} ${currentYear}`);
// to get the days from other months, create a loop over 12 months
for (let j = 1; j <= maxMonths; j++) {
currentMonth++;
// If month goes past December, reset month and add a year.
if (currentMonth == 13) {
currentMonth = 1;
currentYear = currentYear + 1;
}
// Get first of Month and Day.
let firstOfMonth = new Date(`${currentMonth}/1/${currentYear}`);
let firstOfMonthDay = firstOfMonth.getDay();
// Check for Days in Month
if ($.inArray(currentMonth, thirty) > -1) {
var lastDay = 30;
} else if (currentMonth == 2) {
var lastDay = isLeap(currentYear);
} else {
var lastDay = 31;
}
// Loop through the amount of days in the month to create the date tiles
for (let i = 1; i <= lastDay; i++) {
let date = new Date(`${currentMonth}/${i}/${currentYear}`);
let day = date.getDay();
// To correctly display the first date on the correct day, do a check and add empty blocks
if (firstOfMonth.getDate() == i && j == 1) {
for (let k = 0; k < day; k++) {
$("#dates").append('<p class="dates"></p>');
}
}
// Add tile to div
$("#dates").append(`<p class="dates" id="${i}_${currentMonth}_${currentYear}">${i}</>`)
// If it is the last day of the month and last month of the loop - check to add empty tiles.
if (i == lastDay && j == maxMonths) {
for (let l = 0; l < (6 - day); l++) {
$("#dates").append("<p></p>");
}
}
// When a day reaches Saturday, break
if (days[day] == days[6]) {
$("#dates").append("<br/>")
}
}
}
// Add selected style to selected date by accessing its id.
// generates and id, like so: 1_9_2019
$(`#${currentDate}_${d.getMonth() + 1}_${d.getFullYear()}`).addClass("selected");
// When user clicks on a new date, selectedDate will be updated
$(".dates").click(function(e) {
let newDate = e.target.id;
let newDateArr = newDate.split("_");
$(".selected").removeClass("selected");
$(this).addClass("selected");
if (!newDate || newDate == "dates") {
// tile is empty
$("#selectedDate").text("Terminkalender");
} else {
// tile contains date
$("#selectedDate").text(`${newDateArr[0]} ${months[newDateArr[1] - 1]} ${newDateArr[2]}`);
}
});
/* Calendar Navigation */
let getMonth = d.getMonth();
let updatedMonth = getMonth + 1;
let getYear = d.getFullYear();
let updatedYear = getYear;
// Add opacity to next or previous month to add more clarity.
$(".dates").not(`[id*=${updatedMonth}_${updatedYear}]`).addClass("opacity");
// down chevron
$("#angle_down").click(function(e) {
// If user reaches end of calender, disable event
if (updatedMonth == getMonth + 1 && updatedYear == getYear + 10) {
$("#angle_down").addClass("opacity");
return false;
}
$(".opacity").removeClass("opacity");
// when down-chevron gets clicked, add one month
updatedMonth++;
// If month goes past December, reset month and add a year.
if (updatedMonth == 13) {
updatedMonth = 1;
updatedYear = updatedYear + 1;
}
// update the link attribute with the new month and year, and update it in the DOM
var link = `#1_${updatedMonth}_${updatedYear}`;
$("#angle_down").attr("href", link);
// update heading with updated month and year when chevron gets clicked
$("h3").text(months[updatedMonth - 1] + " " + updatedYear);
// Add opacity to next or previous month to add more clarity.
$(".dates").not(`[id*=${updatedMonth}_${updatedYear}]`).addClass("opacity");
// e.preventDefault();
});
//up chevron
$("#angle_up").click(function(e) {
// If user reaches beginning of calender, disable event
if (updatedMonth == getMonth + 1 && updatedYear == getYear) {
$("#angle_up").addClass("opacity");
return false;
}
// when up-chevron gets clicked, remove one month
updatedMonth--;
// If a months value is before January, reset month to December and remove a year.
if (updatedMonth == 0) {
updatedMonth = 12;
updatedYear = updatedYear - 1;
}
var link = `#1_${updatedMonth}_${updatedYear}`; // = 1_9_2019
$("#angle_up").attr("href", link); // = href="#1_9_2019"
$("h3").text(months[updatedMonth - 1] + " " + updatedYear);
$(".opacity").removeClass("opacity");
// Add opacity to any day not in the selected month.
$(".dates").not(`[id*=${updatedMonth}_${updatedYear}]`).addClass("opacity");
// e.preventDefault();
});
/* calendar styles */
.calendar-wrapper {
position: sticky;
top: 10rem;
background: #fff;
margin: 1rem 0;
width: 25rem;
color: #232323;
background-color: #fff;
-webkit-box-shadow: 10px 10px 15px 0px rgba(0, 0, 0, 0.6);
-moz-box-shadow: 10px 10px 15px 0px rgba(0, 0, 0, 0.6);
box-shadow: 10px 10px 15px 0px rgba(0, 0, 0, 0.6);
}
.calendar {
margin-left: auto;
margin-right: auto;
margin-bottom: 2rem;
width: 23rem;
}
.calendar-wrapper span {
color: #232323;
}
.calendar-wrapper h2 {
text-align: center;
font-weight: 400;
color: #fff;
background-color: #595959;
letter-spacing: 0.1rem;
padding: 0.7rem 0;
cursor: pointer;
}
.head p,
.dates p {
text-align: center;
padding: 0.5rem 0;
width: 3rem;
margin: 0;
display: inline-block;
height: auto;
}
.dates {
background-color: #fff;
height: 20rem;
overflow-y: hidden;
scroll-behavior: smooth;
border: 2px solid transparent;
transition: all 0.2s ease-in-out;
}
.dates p:hover {
background-color: #262626;
color: #fff;
}
.selected {
background-color: #fff;
color: #000;
opacity: 1;
}
.month i {
color: #262626;
cursor: pointer;
font-size: 1.5rem;
margin: 0 0.6rem;
}
.month h3 {
font-size: 1.2rem;
letter-spacing: 0.02rem;
font-weight: 400;
display: inline-block;
width: 16rem;
margin-left: 0.5rem;
}
.month a {
text-decoration: none;
}
.opacity {
opacity: 0.5;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="calendar-wrapper" class="calendar-wrapper">
<!-- Date Title -->
<h2 id="selectedDate"></h2>
<!-- Calendar -->
<div id="calendar" class="calendar">
<!-- Month and Arrows-->
<div id="month" class="month">
<h3></h3>
<a id="angle_up" href="#1_9_2019"><i class="fas fa-chevron-up"></i>prev</a>
<a id="angle_down" href="#1_10_2019"><i class="fas fa-chevron-down"></i>next</a>
</div>
<!-- Days of Week -->
<div id="head" class="head">
<p>So</p>
<p>Mo</p>
<p>Di</p>
<p>Mi</p>
<p>Do</p>
<p>Fr</p>
<p>Sa</p>
</div>
<!-- Date Tiles -->
<div id="dates" class="dates"></div>
</div>
This is related to position: sticky
(Same issue happens with position: absolute
). I've had this issue in production before with modals. Do you need your element to be position: sticky
?
You can use position: fixed
for similar UX where your modal floats on top of the content.
Here's a code pen with it working (feel free to scroll down so you could see a potential "jump" to the top of the page)