ปฏิทินเคลื่อนไหวโดยใช้ HTML + CSS + JS
ฉันเป็นนักพัฒนามือใหม่และฉันกำลังสร้างปฏิทินสำหรับสมาร์ทโฟน ฉันใช้HTML, CSS และ JS ฉันยังไม่ได้ทำโปรเจ็กต์ทั้งหมด แต่ฉันมีความรู้สึกว่ากำลังสร้างรหัสที่ยุ่งเหยิง ฉันจะโพสต์ลิงก์ไปยัง git + รหัสที่นี่เพื่อให้คุณสามารถอ่าน / โคลนได้ดีขึ้นและช่วยฉันในการออกแบบรูปแบบ ฯลฯ ...
PS: โครงการนี้ได้รับการพัฒนาโดยใช้ความละเอียดของ iPhoneX (375 x 812) ยังไม่ตอบสนองดังนั้นฉันขอแนะนำให้รันโค้ดโดยใช้ความละเอียดด้านบน
ลิงค์ Git: https://github.com/LucasBiazi/Mobile_Calendar_HTML_CSS_JS
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Calendar</title>
<script src="../script/script.js"></script>
<link rel="stylesheet" href="../css/style.css" />
</head>
<!-- As soon as the page loads, fulfills the table. -->
<body onload="load_DB(new Date().getFullYear(), new Date().getMonth())">
<div class="main">
<div class="title">
<span class="year_title" id="year_title"></span>
<span class="month_title" id="month_title"></span>
</div>
<div class="calendar">
<div id="month_days" class="month_days">
<table id="days">
<tr>
<th style="color: lightsalmon">Sun</th>
<th class="even">Mon</th>
<th>Tue</th>
<th class="even">Wed</th>
<th>Thu</th>
<th class="even">Fri</th>
<th style="color: lightsalmon">Sat</th>
</tr>
</table>
</div>
<!-- Future implementations. -->
<div class="data">
<div class="data_content">
<p style="color: black; font-size: 20px">No content.</p>
</div>
</div>
</div>
<div class="buttons">
<!-- Reteats a month. -->
<button
style="
animation: display_button_back 1s ease;
position: relative;
"
onclick="retreat_month(parseInt(document.getElementById('year_title').textContent),
identify_month(document.getElementById('month_title').textContent))"
>
<img
src="../../media/left-arrow-line-symbol.svg"
style="width: 35px; height: 35px"
alt="back"
/>
</button>
<!-- Shows current month. -->
<button
style="animation: display_opacity_zoom 1s ease-out;"
onclick="advance_month(new Date().getFullYear(), new Date().getMonth() - 1)"
>
T
</button>
<!-- Advances a month. -->
<button
style="
animation: display_button_next 1s ease;
position: relative;
"
onclick="advance_month(parseInt(document.getElementById('year_title').textContent),
identify_month(document.getElementById('month_title').textContent))"
>
<img
src="../../media/right-arrow-angle.svg"
style="width: 35px; height: 35px"
alt="next"
/>
</button>
</div>
</div>
</body>
</html>
CSS:
* {
padding: 0px;
margin: 0px;
font-family: Verdana, Geneva, Tahoma, sans-serif;
font-weight: lighter;
}
body {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-image: url(../../media/grass.jpg);
/* Blurring the background. Applies behind the element... */
backdrop-filter: blur(9px);
background-size: cover;
}
@keyframes display_data {
0% {
transform: scale3d(0.25, 0, 1);
}
25% {
transform: scale3d(0.5, 0, 1);
}
50% {
transform: scale3d(0.1, 0, 1);
}
75% {
transform: scale3d(0.1, 1.2, 1);
}
100% {
transform: scale3d(1, 1, 1);
}
}
@keyframes display_month_days {
from {
opacity: 0%;
}
to {
opacity: 100%;
}
}
@keyframes display_button_back {
0% {
right: 25px;
transform: scale3d(0.75, 0.75, 1);
}
100% {
right: 0px;
transform: scale3d(1, 1, 1);
}
}
@keyframes display_button_next {
0% {
left: 25px;
transform: scale3d(0.75, 0.75, 1);
}
100% {
left: 0px;
transform: scale3d(1, 1, 1);
}
}
@keyframes display_opacity_zoom {
from {
opacity: 0%;
transform: scale3d(0.5, 0.5, 1);
}
to {
opacity: 100%;
transform: scale3d(1, 1, 1);
}
}
.main {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
color: white;
background-color: rgba(0, 0, 0, 0.65);
}
.title {
margin-top: 7%;
height: 80px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
animation: display_opacity_zoom 1s ease-out;
}
.year_title {
margin-left: 5px;
font-size: 40px;
letter-spacing: 5px;
color: lightsalmon;
text-align: center;
}
.month_title {
margin-left: 15px;
font-size: 25px;
letter-spacing: 15px;
text-align: center;
}
.calendar {
height: 75%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.month_days {
margin-top: 10px;
width: 100%;
height: 50%;
animation: display_month_days 1s ease-in-out;
}
table {
margin-top: 20px;
width: 100%;
font-size: 22px;
}
tr,
th,
td {
background-color: none;
}
th {
width: 14%;
text-align: center;
color: white;
}
td {
width: 2.38em;
height: 2.38em;
color: white;
text-align: center;
border-radius: 50%;
}
td:hover {
background-color: black;
}
.data {
display: flex;
justify-content: center;
align-items: center;
width: 95%;
height: 30%;
background-color: rgba(255, 255, 255, 0.9);
border: none;
border-radius: 5px;
animation: display_data 0.8s ease;
}
.data_content {
width: 95%;
height: 95%;
}
.other_month {
background: none;
color: rgba(175, 175, 175, 0.45);
}
.buttons {
width: 100vw;
height: 70px;
display: flex;
justify-content: space-around;
align-items: flex-start;
}
button {
width: 60px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
background: none;
border: none;
font-size: 35px;
font-weight: 400;
color: white;
}
JS:
// Returns the day of the week in which the month starts.
starting_day = (year, month) => new Date(year, month, 1).getDay();
// Returns the amount of days in a month.
total_month_days = (year, month) => new Date(year, month + 1, 0).getDate();
// Set the background color of the cell that contains today's day.
function is_today(months, cell) {
if (
new Date().getFullYear() ==
parseInt(document.getElementById("year_title").textContent) &&
months[new Date().getMonth()].month ==
document.getElementById("month_title").textContent &&
cell.textContent == new Date().getDate()
) {
cell.style.background = "rgba(255, 160, 122, 0.8)";
}
}
// Changes color if it is a weekend.
color_weekend = (let_value, cell) => {
if (let_value == 6 || let_value == 0) cell.style.color = "lightsalmon";
};
// Populates the DB.
function load_DB(year, month) {
let counter = 1;
const table = document.getElementById("days");
const date_object = new Date(year, month);
// Array containing months names, starting_day()_+ total_month_days().
const months = [
{
// Month 0
month: "January",
first_day: starting_day(date_object.getFullYear(), 0),
days: Array(total_month_days(date_object.getFullYear(), 0)),
},
{
// Month 1
month: "February",
first_day: starting_day(date_object.getFullYear(), 1),
days: Array(total_month_days(date_object.getFullYear(), 1)),
},
{
// Month 2
month: "March",
first_day: starting_day(date_object.getFullYear(), 2),
days: Array(total_month_days(date_object.getFullYear(), 2)),
},
{
// Month 3
month: "April",
first_day: starting_day(date_object.getFullYear(), 3),
days: Array(total_month_days(date_object.getFullYear(), 3)),
},
{
// Month 4
month: "May",
first_day: starting_day(date_object.getFullYear(), 4),
days: Array(total_month_days(date_object.getFullYear(), 4)),
},
{
// Month 5
month: "June",
first_day: starting_day(date_object.getFullYear(), 5),
days: Array(total_month_days(date_object.getFullYear(), 5)),
},
{
// Month 6
month: "July",
first_day: starting_day(date_object.getFullYear(), 6),
days: Array(total_month_days(date_object.getFullYear(), 6)),
},
{
// Month 7
month: "August",
first_day: starting_day(date_object.getFullYear(), 7),
days: Array(total_month_days(date_object.getFullYear(), 7)),
},
{
// Month 8
month: "September",
first_day: starting_day(date_object.getFullYear(), 8),
days: Array(total_month_days(date_object.getFullYear(), 8)),
},
{
// Month 9
month: "October",
first_day: starting_day(date_object.getFullYear(), 9),
days: Array(total_month_days(date_object.getFullYear(), 9)),
},
{
// Month 10
month: "November",
first_day: starting_day(date_object.getFullYear(), 10),
days: Array(total_month_days(date_object.getFullYear(), 10)),
},
{
// Month 11
month: "December",
first_day: starting_day(date_object.getFullYear(), 11),
days: Array(total_month_days(date_object.getFullYear(), 11)),
},
];
// Prints the name of the current month and year.
document.getElementById("year_title").textContent = date_object.getFullYear();
document.getElementById("month_title").textContent =
months[date_object.getMonth()].month;
// Month days that will appear in the first row.
const number_of_cells_in_the_first_row =
7 - months[date_object.getMonth()].first_day;
// Month days that will appear after the first row.
let normal_days = number_of_cells_in_the_first_row + 1;
// Creates + populates the 5 last rows.
for (let r = 0; r < 5; r++) {
let row = table.insertRow(r + 1);
let cell;
// Creates + populates 7 cells in each row.
for (let x = 0; x < 7; x++) {
cell = row.insertCell(x);
cell.textContent = normal_days;
is_today(months, cell);
color_weekend(x, cell);
normal_days++;
// Filling the rest of the table with the days of the next month(gray days).
if (normal_days > months[date_object.getMonth()].days.length + 1) {
// Next month days are always lowlighted.
if (x == 6 || x == 0) cell.style.color = "rgba(175, 175, 175, 0.45)";
cell.textContent = counter++;
cell.className = "other_month";
}
}
}
// Creates + populates the 1 row.
for (let i = 0; i < 1; i++) {
let row = table.insertRow(i + 1);
let cell;
let number_of_blank_cells = 7 - number_of_cells_in_the_first_row;
// Populates it.
for (let y = 0; y < number_of_cells_in_the_first_row; y++) {
cell = row.insertCell(0);
cell.textContent = number_of_cells_in_the_first_row - y;
is_today(months, cell);
color_weekend(y, cell);
}
// Filling the rest of the table (next month days).
for (let w = 0; w < number_of_blank_cells; w++) {
cell = row.insertCell(0);
if (months[date_object.getMonth() - 1]) {
cell.textContent = months[date_object.getMonth() - 1].days.length--;
cell.className = "other_month";
} else {
cell.textContent = months[date_object.getMonth() + 11].days.length--;
cell.className = "other_month";
}
}
}
}
// Converts the month name to the month number (January = 0).
function identify_month(string) {
const all_months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
let i = 0;
while (string != all_months[i]) {
i++;
}
return i;
}
// Advancecs a month.
function advance_month(year, month) {
// Cleaning table.
const table = document.getElementById("days");
for (let i = 0; i < 6; i++) {
table.deleteRow(1);
}
// Add new data.
month++;
load_DB(year, month);
}
// Retreats a month.
function retreat_month(year, month) {
// Cleaning table.
const table = document.getElementById("days");
for (let i = 0; i < 6; i++) {
table.deleteRow(1);
}
month--; // Add new data.
load_DB(year, month);
}
คำตอบ
ยินดีต้อนรับสู่ Code Review CSS ดูดีทีเดียว ฉันไม่เห็นสไตล์ชีตที่มีสีมากเกินไปlightsalmon😃 ด้านล่างนี้คือคำแนะนำบางประการ
การอ้างอิง Cache DOM
"... การเข้าถึง DOM มีค่าใช้จ่ายค่อนข้างสูง - ฉันคิดว่าถ้าฉันมีสะพานเช่นที่ดินสองชิ้นที่มีสะพานเก็บค่าผ่านทางและเครื่องมือ JavaScript อยู่ที่ด้านหนึ่งและ DOM อยู่อีกด้านหนึ่งและ ทุกครั้งที่ฉันต้องการเข้าถึง DOM จาก
เอ็นจิ้นJavaScript ฉันต้องจ่ายค่าผ่านทางนั้น” - John Hrvatin, Microsoft, MIX09 ในการพูดคุยเกี่ยวกับการสร้างเว็บแอปพลิเคชั่นและไซต์ประสิทธิภาพสูงเมื่อเวลา 29:38 น. ซึ่งอ้างถึงในO'Reilly หนังสือJavascriptโดย Nicholas C Zakas หน้า 36เช่นเดียวกับที่กล่าวถึงในโพสต์นี้
โปรดจำไว้ว่ามีการระบุไว้เมื่อ 10 ปีก่อนและเบราว์เซอร์มีแนวโน้มที่จะมาพร้อมกันตั้งแต่นั้นมาก็ยังควรระวัง อ้างอิง Store เพื่อองค์ประกอบ DOM - เช่นdocument.getElementById("year_title")ใน variables- นี้อาจเกิดขึ้นในการเรียกกลับไปยังเหตุการณ์DOMContentLoaded
let titleEl;
window.addEventListener('DOMContentLoaded', (event) => {
titleEl = document.getElementById("year_title");
});
ดังที่โจชัวกล่าวไว้ตัวจัดการเหตุการณ์ใน HTML ถูกกำหนดให้เป็นไปตามมาตรฐานของวันนี้และการเรียกกลับนั้นจะเป็นสถานที่ที่เหมาะสมที่จะให้โค้ดอยู่ในonloadคุณลักษณะขององค์ประกอบของร่างกาย
ชื่อเดือน
ดังที่ Joshua ได้กล่าวถึงรหัสสำหรับเดือนนั้นค่อนข้างซ้ำซาก แทนที่จะเก็บชื่อเดือนDate.prototype.toLocaleDateString()สามารถใช้เพื่อสร้างชื่อเดือนแบบไดนามิกอาจเป็นภาษาที่ระบุโดยผู้ใช้และ / หรือnavigator.languageถ้ามี
ชื่อฟังก์ชันที่อ่านได้มากขึ้น
// Set the background color of the cell that contains today's day. function is_today(months, cell) {
ตัดสินโดยชื่อฉันเดาว่าฟังก์ชันจะส่งคืนบูลีนที่ระบุว่าค่าบางอย่างเป็นวันนี้หรือไม่แม้ว่าจะได้รับความคิดเห็นและการใช้งานที่ไม่เป็นเช่นนั้นก็ตาม อาจมีชื่อที่เหมาะสมstyle_todays_date_cellกว่า
ความเสมอภาคที่เข้มงวด
เป็นนิสัยที่ดีที่จะใช้ตัวดำเนินการเปรียบเทียบที่เข้มงวดเช่น===และ!==เมื่อเป็นไปได้ - เช่นสำหรับการเปรียบเทียบนี้ภายในload_DB():
if (x == 6 || x == 0) cell.style.color = "rgba(175, 175, 175, 0.45)";
ส่วนเพิ่มก่อนแก้ไข
บรรทัดเหล่านี้:
month++; load_DB(year, month);
สามารถรวมกันได้เมื่อค่าของmonthเพิ่มขึ้นก่อนการโทรโดยใช้ตัวดำเนินการเพิ่มคำนำหน้า :
load_DB(year, ++month);
รูปแบบอินไลน์ซ้ำซ้อน -> CSS
ภาพภายในปุ่มมีลักษณะแบบอินไลน์เช่น
style="width: 35px; height: 35px"
ที่สามารถย้ายไปยังสไตล์ชีต:
button img {
width: 35px;
height: 35px;
}
สไตล์อินไลน์สำหรับส่วนหัวตาราง
เซลล์แรกและเซลล์สุดท้ายมีลักษณะแบบอินไลน์เพื่อแทนที่thกฎเริ่มต้น:
<th style="color: lightsalmon">Sun</th>
ที่สามารถย้ายไปยังสไตล์ชีตได้เช่นกันโดยใช้ตัวเลือกที่เฉพาะเจาะจงมากขึ้นเช่นตัวเลือกหลอก:first-childและ:last-child:
th:first-child,
th:last-child {
color: lightsalmon;
}
ทางเลือกอื่น ๆ รวมถึง:first-of-typeและ:last-of-typeเทคนิคนั้นยังสามารถใช้เพื่อย้ายบรรทัดด้านบนออกจาก JavaScript และไปยังสไตล์ชีตโดยใช้tdแทนtr:
if (x == 6 || x == 0) cell.style.color = "rgba(175, 175, 175, 0.45)";
PS: คำตอบนั้นประกอบด้วยiPhone VII +
ตัวแปร
ใช้การประกาศตัวแปรทุกครั้งเมื่อประกาศตัวแปรของคุณเป็นครั้งแรก หากคุณไม่ใส่ตัวแปรของคุณไว้ในขอบเขตส่วนกลางซึ่งแทบจะไม่เป็นไปตามที่คุณต้องการเลย นอกจากนี้ยังทำให้ชัดเจนยิ่งขึ้นว่าคุณไม่ต้องการกำหนดซ้ำให้กับตัวแปรส่วนกลางที่มีอยู่แล้ว
ดังนั้นสำหรับstarting_dayตัวอย่างเช่นใส่var, letหรือconstในด้านหน้าของมัน
แอตทริบิวต์ HTML
แทนที่จะใช้แอตทริบิวต์เช่นonclickหรือonloadเพื่อจัดการเหตุการณ์ (เรียกอีกอย่างว่าตัวจัดการแบบอินไลน์) โดยทั่วไปถือว่าเป็นแนวทางปฏิบัติที่ดีที่สุดในการจัดการเหตุการณ์เหล่านี้ใน JavaScript ของคุณเอง ช่วยแยก HTML / JavaScript ของคุณ
ตัวอย่างเช่นในการแทนที่onloadแอตทริบิวต์คุณสามารถใช้ฟังก์ชัน JS ต่อไปนี้แทน:
function ready(func) {
if (document.readyState !== 'loading') {
func();
} else {
document.addEventListener('DOMContentLoaded', func);
}
}
คุณสามารถประกาศฟังก์ชันที่เรียกว่าmainตัวอย่างเช่นซึ่งคุณจะใส่รหัสใด ๆ ที่คุณต้องการเรียกใช้เมื่อ DOM พร้อมที่จะใช้งานแล้วจึงเรียกready(main);ใช้
ในการแทนที่onclickแอตทริบิวต์คุณควรเพิ่มคลาส / รหัส CSS เพื่อใช้เป็นตัวเลือกในโค้ด JavaScript const backButton = document.querySelector('calendarBack');แล้วคุณจะเลือกใช้สิ่งที่ต้องการ จากนั้นคุณสามารถแนบตัวฟังเหตุการณ์เข้ากับองค์ประกอบนั้นได้ดังนี้:
backButton.addEventListener("click", () => {
// The code you want to run when the button is clicked goes here
});
การอ้างอิงรหัส
months
การmonthsประกาศตัวแปรค่อนข้างซ้ำซากและมีเพียงสิ่งเดียวที่เปลี่ยนไปในแต่ละเดือนนั่นคือ 'ดัชนี' ของมัน สิ่งนี้ทำให้ผู้สมัครที่สมบูรณ์แบบในการม้วนเข้าสู่แผนที่ลูป / อาร์เรย์
ฉันจะทำเช่นนี้โดยใช้mapฟังก์ชัน :
// This would ideally not be in load_DB but be a top level declaration, since you could then reference this again in the identify_month function instead of rewriting the months
const monthNames = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
// Saving this in a variable for brevity and since it doesn't change
const fullYear = date_object.getFullYear();
const months = monthNames.map((monthName, index) => {
return {
month: monthName,
first_day: starting_day(fullYear, index),
days: Array(total_month_days(fullYear, index)),
};
});
สิ่งหนึ่งที่ฉันไม่แน่ใจคือdaysคุณสมบัติและเหตุใดจึงเป็นอาร์เรย์ที่จะเริ่มต้นด้วย สถานที่เดียวที่ถูกอ้างอิงในรหัสคือการตรวจสอบความยาว นี่เป็นส่วนหนึ่งของรหัสที่ยังไม่เสร็จสมบูรณ์ที่คุณกล่าวถึงหรือไม่ ถ้าไม่ใช่ฉันจะแทนที่บรรทัดด้วยวัน: Array(total_month_days(fullYear, index)),แทน
identify_month
การใช้งานฟังก์ชันนี้แบบดั้งเดิมควรใช้งานได้แม้ว่าจะเป็นวิธีที่ยาวในการพูดว่า "รับดัชนีของเดือนจากmonthNamesอาร์เรย์"
นี่คือวิธีที่ฉันจะเขียนใหม่:
// Converts the month name to the month number (January = 0).
function identify_month(string) {
return monthNames.indexOf(string);
}
รหัสเดือนปัจจุบัน
รหัสเดิมค่อนข้างอ่านยากเนื่องจากการค้นหาซ้ำของวัตถุเดือนที่พบในmonthsอาร์เรย์ วางไว้ในตัวแปรเพื่อปรับปรุงความสามารถในการอ่านและลบการเข้าถึงอาร์เรย์เพิ่มเติมที่ไม่จำเป็น
const currentMonth = date_object.getMonth();
const currentMonthObj = months[currentMonth];
// Prints the name of the current month and year.
document.getElementById("year_title").textContent = fullYear;
document.getElementById("month_title").textContent = currentMonthObj.month;
// Month days that will appear in the first row.
const number_of_cells_in_the_first_row = 7 - currentMonthObj.first_day;
ฉันจะพยายามกลับมาและเพิ่มความคิดในภายหลัง :)