Făcând o căutare rapidă aici pe CSS-Tricks, arată câte moduri diferite există de a aborda calendarele. Unii arată cum CSS Grid poate crea aspectul eficient. Unii încearcă să aduce datele reale în amestec. Unele se bazează pe un cadru pentru a ajuta la conducerea statului.
Există multe considerente atunci când construiți o componentă de calendar - mult mai mult decât ceea ce este tratat în articolele pe care le-am legat. Dacă vă gândiți bine, calendarele sunt pline de nuanțe, de la gestionarea fusurilor orare și a formatelor de date până la localizare și chiar asigurarea faptului că datele curg de la o lună la alta... și asta înainte de a intra în considerații suplimentare privind accesibilitatea și aspectul, în funcție de locul în care se află calendarul. este afișat și altele.
Mulți dezvoltatori se tem de Date()
obiect și rămâneți cu biblioteci mai vechi, cum ar fi moment.js
. Dar, deși există multe „obstacuri” când vine vorba de date și formatare, JavaScript are o mulțime de API-uri și chestii grozave de ajutor!
Nu vreau să recreez roata aici, dar vă voi arăta cum putem obține un calendar bun cu JavaScript vanilla. Vom cerceta accesibilitate, folosind marcaj semantic și ușor de citit de ecran <time>
-etichete — precum și internaționalizare și formatare, folosind Intl.Locale
, Intl.DateTimeFormat
și Intl.NumberFormat
-API-uri.
Cu alte cuvinte, facem un calendar... numai fără dependențele suplimentare pe care le puteți vedea de obicei folosite într-un tutorial ca acesta și cu unele dintre nuanțe pe care s-ar putea să nu le vedeți de obicei. Și, în acest proces, sper că veți câștiga o nouă apreciere pentru lucrurile mai noi pe care le poate face JavaScript, în timp ce vă faceți o idee despre felul de lucruri care îmi trec prin minte când pun ceva ca acesta împreună.
În primul rând, denumirea
Cum ar trebui să numim componenta noastră de calendar? În limba mea maternă, s-ar numi „element kalender”, așa că să-l folosim și să scurtăm asta la „Kal-El” - cunoscut și ca Numele lui Superman pe planeta Krypton.
Să creăm o funcție pentru a face lucrurile să meargă:
function kalEl(settings = {}) { ... }
Această metodă va reda o singură lună. Mai târziu vom numi această metodă de la [...Array(12).keys()]
a reda un an întreg.
Date inițiale și internaționalizare
Unul dintre lucrurile comune pe care le face un calendar online tipic este să evidențieze data curentă. Deci, să creăm o referință pentru asta:
const today = new Date();
În continuare, vom crea un „obiect de configurare” pe care îl vom îmbina cu opționalul settings
obiectul metodei primare:
const config = Object.assign( { locale: (document.documentElement.getAttribute('lang') || 'en-US'), today: { day: today.getDate(), month: today.getMonth(), year: today.getFullYear() } }, settings
);
Verificăm dacă elementul rădăcină (<html>
) conține a lang
-atribut cu localizare info; în caz contrar, vom reveni la utilizare en-US
. Acesta este primul pas spre internaţionalizarea calendarului.
De asemenea, trebuie să stabilim ce lună să afișăm inițial când este redat calendarul. De aceea am extins config
obiect cu primarul date
. În acest fel, dacă nu este furnizată nicio dată în settings
obiect, vom folosi today
referinta in schimb:
const date = config.date ? new Date(config.date) : today;
Avem nevoie de puține mai multe informații pentru a formata corect calendarul pe baza localizării. De exemplu, este posibil să nu știm dacă prima zi a săptămânii este duminică sau luni, în funcție de locație. Dacă avem informații, grozav! Dar dacă nu, îl vom actualiza folosind Intl.Locale
API. API-ul are un weekInfo
obiect care returnează un firstDay
proprietate care ne oferă exact ceea ce căutăm fără nicio bătaie de cap. De asemenea, putem afla ce zile ale săptămânii sunt alocate weekend
:
if (!config.info) config.info = new Intl.Locale(config.locale).weekInfo || { firstDay: 7, weekend: [6, 7] };
Din nou, creăm alternative. „Prima zi” a săptămânii pentru en-US
este duminică, deci este implicit la o valoare de 7
. Acest lucru este puțin confuz, deoarece getDay
metodă în JavaScript returnează zilele ca [0-6]
, În cazul în care 0
este duminică... nu mă întreba de ce. Weekend-urile sunt sâmbătă și duminică, deci [6, 7]
.
Înainte să avem Intl.Locale
API și al acestuia weekInfo
metoda, a fost destul de greu să creezi un calendar internațional fără multe **obiecte și matrice cu informații despre fiecare locație sau regiune. În zilele noastre, este ușor-peasy. Dacă trecem înăuntru en-GB
, metoda returnează:
// en-GB
{ firstDay: 1, weekend: [6, 7], minimalDays: 4
}
Într-o țară ca Brunei (ms-BN
), weekendul este vineri și duminică:
// ms-BN
{ firstDay: 7, weekend: [5, 7], minimalDays: 1
}
S-ar putea să vă întrebați ce anume minimalDays
proprietatea este. Asta este cele mai puține zile necesare în prima săptămână a unei luni pentru a fi socotite ca o săptămână întreagă. În unele regiuni, poate fi doar o zi. Pentru alții, ar putea fi șapte zile întregi.
În continuare, vom crea un render
metoda din cadrul nostru kalEl
-metodă:
const render = (date, locale) => { ... }
Mai avem nevoie de câteva date cu care să lucrăm înainte de a reda ceva:
const month = date.getMonth();
const year = date.getFullYear();
const numOfDays = new Date(year, month + 1, 0).getDate();
const renderToday = (year === config.today.year) && (month === config.today.month);
Ultima este a Boolean
care verifică dacă today
există în luna pe care urmează să o redăm.
Markup semantic
Vom aprofunda redarea într-un moment. Dar mai întâi, vreau să mă asigur că detaliile pe care le-am configurat au etichete HTML semantice asociate. Configurarea imediată din cutie ne oferă beneficii de accesibilitate încă de la început.
Înveliș pentru calendar
În primul rând, avem învelișul non-semantic: <kal-el>
. Este în regulă pentru că nu există o semantică <calendar>
etichetă sau ceva de genul ăsta. Dacă nu am face un element personalizat, <article>
ar putea fi elementul cel mai potrivit, deoarece calendarul ar putea sta pe propria pagină.
Nume de luni
<time>
element va fi unul important pentru noi, deoarece ajută la traducerea datelor într-un format pe care cititorii de ecran și motoarele de căutare îl pot analiza mai precis și mai consecvent. De exemplu, iată cum putem transmite „ianuarie 2023” în marcajul nostru:
<time datetime="2023-01">January <i>2023</i></time>
Numele zilelor
Rândul de deasupra datelor calendarului care conține numele zilelor săptămânii poate fi dificil. Este ideal dacă putem scrie numele complete pentru fiecare zi — de exemplu, duminică, luni, marți etc. — dar asta poate ocupa mult spațiu. Deci, să prescurtăm numele pentru moment în interiorul unui <ol>
unde fiecare zi este a <li>
:
<ol> <li><abbr title="Sunday">Sun</abbr></li> <li><abbr title="Monday">Mon</abbr></li> <!-- etc. -->
</ol>
Am putea deveni dificil cu CSS să obținem tot ce este mai bun din ambele lumi. De exemplu, dacă am modificat un pic de markup astfel:
<ol> <li> <abbr title="S">Sunday</abbr> </li>
</ol>
… primim numele complete implicit. Apoi putem „ascunde” numele complet atunci când spațiul se epuizează și să afișăm title
atribut in schimb:
@media all and (max-width: 800px) { li abbr::after { content: attr(title); }
}
Dar, nu mergem așa pentru că Intl.DateTimeFormat
API-ul poate ajuta și aici. Vom ajunge la asta în secțiunea următoare când vom acoperi randarea.
Numerele zilei
Fiecare dată din grila calendarului primește un număr. Fiecare număr este un articol din listă (<li>
) într-o listă ordonată (<ol>
), și inline <time>
eticheta cuprinde numărul real.
<li> <time datetime="2023-01-01">1</time>
</li>
Și, deși nu plănuiesc să fac încă niciun stil, știu că voi dori o modalitate de a stila numerele datei. Acest lucru este posibil așa cum este, dar vreau, de asemenea, să pot stila numerele din zilele lucrătoare în mod diferit față de numerele de weekend, dacă este necesar. Deci, voi include data-*
atribute special pentru asta: data-weekend
și data-today
.
Numerele săptămânii
Există 52 de săptămâni într-un an, uneori 53. Deși nu este foarte comun, poate fi plăcut să afișați numărul pentru o anumită săptămână în calendar pentru context suplimentar. Îmi place să-l am acum, chiar dacă nu ajung să nu îl folosesc. Dar îl vom folosi pe deplin în acest tutorial.
Vom folosi un data-weeknumber
atribut ca un cârlig de stil și includeți-l în marcajul pentru fiecare dată care este prima dată a săptămânii.
<li data-day="7" data-weeknumber="1" data-weekend=""> <time datetime="2023-01-08">8</time>
</li>
de redare
Să punem calendarul pe o pagină! Știm deja asta <kal-el>
este numele elementului nostru personalizat. Primul lucru pe care trebuie să-l configuram este să setăm firstDay
proprietate de pe el, astfel încât calendarul știe dacă duminica sau altă zi este prima zi a săptămânii.
<kal-el data-firstday="${ config.info.firstDay }">
Vom folosi literale șablon pentru a reda marcajul. Pentru a formata datele pentru un public internațional, vom folosi Intl.DateTimeFormat
API, folosind din nou locale
am precizat mai devreme.
Luna si anul
Când numim month
, putem stabili dacă vrem să folosim long
nume (de exemplu februarie) sau short
nume (ex. feb.). Să folosim long
nume deoarece este titlul deasupra calendarului:
<time datetime="${year}-${(pad(month))}"> ${new Intl.DateTimeFormat( locale, { month:'long'}).format(date)} <i>${year}</i>
</time>
Numele zilelor săptămânii
Pentru zilele săptămânii afișate deasupra grilei de date, avem nevoie de ambele long
(de exemplu „duminică”) și short
(abreviat, adică „Soarele”) nume. În acest fel, putem folosi numele „scurt” atunci când calendarul are puțin spațiu:
Intl.DateTimeFormat([locale], { weekday: 'long' })
Intl.DateTimeFormat([locale], { weekday: 'short' })
Să facem o mică metodă de ajutor care face puțin mai ușor să apelezi pe fiecare:
const weekdays = (firstDay, locale) => { const date = new Date(0); const arr = [...Array(7).keys()].map(i => { date.setDate(5 + i) return { long: new Intl.DateTimeFormat([locale], { weekday: 'long'}).format(date), short: new Intl.DateTimeFormat([locale], { weekday: 'short'}).format(date) } }) for (let i = 0; i < 8 - firstDay; i++) arr.splice(0, 0, arr.pop()); return arr;
}
Iată cum invocăm asta în șablon:
<ol> ${weekdays(config.info.firstDay,locale).map(name => ` <li> <abbr title="${name.long}">${name.short}</abbr> </li>`).join('') }
</ol>
Numerele zilei
Și în sfârșit, zilele, învelite într-un <ol>
element:
${[...Array(numOfDays).keys()].map(i => { const cur = new Date(year, month, i + 1); let day = cur.getDay(); if (day === 0) day = 7; const today = renderToday && (config.today.day === i + 1) ? ' data-today':''; return ` <li data-day="${day}"${today}${i === 0 || day === config.info.firstDay ? ` data-weeknumber="${new Intl.NumberFormat(locale).format(getWeek(cur))}"`:''}${config.info.weekend.includes(day) ? ` data-weekend`:''}> <time datetime="${year}-${(pad(month))}-${pad(i)}" tabindex="0"> ${new Intl.NumberFormat(locale).format(i + 1)} </time> </li>`
}).join('')}
Să descompunem asta:
- Creăm o matrice „dummy”, bazată pe variabila „număr de zile”, pe care o vom folosi pentru a repeta.
- Creăm un
day
variabilă pentru ziua curentă în iterație. - Remediem discrepanța dintre
Intl.Locale
API șigetDay()
. - În cazul în care
day
este egal cutoday
, adăugăm adata-*
atribut. - În cele din urmă, returnăm
<li>
element ca șir cu date îmbinate. tabindex="0"
face ca elementul să fie focalizat, atunci când utilizați navigarea de la tastatură, după orice valori pozitive de tabindex (Notă: ar trebui nu adăuga pozitiv tabindex-valori)
La „Tastați” numerele în datetime
atribut, folosim o mică metodă de ajutor:
const pad = (val) => (val + 1).toString().padStart(2, '0');
Numărul săptămânii
Din nou, „numărul săptămânii” este locul în care o săptămână se încadrează într-un calendar de 52 de săptămâni. Folosim o mică metodă de ajutor și pentru asta:
function getWeek(cur) { const date = new Date(cur.getTime()); date.setHours(0, 0, 0, 0); date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); const week = new Date(date.getFullYear(), 0, 4); return 1 + Math.round(((date.getTime() - week.getTime()) / 86400000 - 3 + (week.getDay() + 6) % 7) / 7);
}
Nu eu am scris asta getWeek
-metodă. Este o versiune curățată a acest scenariu.
Si asta e! Mulțumită Intl.Locale
, Intl.DateTimeFormat
și Intl.NumberFormat
API-uri, acum putem schimba pur și simplu lang
-atributul <html>
element pentru a schimba contextul calendarului pe baza regiunii curente:
Stilizarea calendarului
S-ar putea să vă amintiți cum toate zilele sunt doar una <ol>
cu articole din listă. Pentru a le transforma într-un calendar care poate fi citit, ne scufundăm în lumea minunată a CSS Grid. De fapt, putem reutiliza aceeași grilă din un șablon de calendar de pornire chiar aici pe CSS-Tricks, dar a actualizat o smidge cu :is()
pseudo relațional pentru a optimiza codul.
Observați că definesc variabile CSS configurabile pe parcurs (și prefixez-le cu ---kalel-
pentru a evita conflictele).
kal-el :is(ol, ul) { display: grid; font-size: var(--kalel-fz, small); grid-row-gap: var(--kalel-row-gap, .33em); grid-template-columns: var(--kalel-gtc, repeat(7, 1fr)); list-style: none; margin: unset; padding: unset; position: relative;
}
Să desenăm margini în jurul numerelor datei pentru a le separa vizual:
kal-el :is(ol, ul) li { border-color: var(--kalel-li-bdc, hsl(0, 0%, 80%)); border-style: var(--kalel-li-bds, solid); border-width: var(--kalel-li-bdw, 0 0 1px 0); grid-column: var(--kalel-li-gc, initial); text-align: var(--kalel-li-tal, end); }
Grila cu șapte coloane funcționează bine atunci când este prima zi a lunii de asemenea prima zi a săptămânii pentru localitatea selectată). Dar asta este mai degrabă excepția decât regula. De cele mai multe ori, va trebui să schimbăm prima zi a lunii la o altă zi a săptămânii.
Amintește-ți tot în plus data-*
atributele pe care le-am definit când ne scriem marcajul? Ne putem conecta la acestea pentru a actualiza ce coloană a grilei (--kalel-li-gc
) primul număr de dată al lunii este plasat la:
[data-firstday="1"] [data-day="3"]:first-child { --kalel-li-gc: 1 / 4;
}
În acest caz, ne întindem de la prima coloană grilă la a patra coloană grilă - care va „împinge” automat următorul articol (Ziua 2) la a cincea coloană grilă și așa mai departe.
Să adăugăm puțin stil datei „actuale”, astfel încât să iasă în evidență. Acestea sunt doar stilurile mele. Aici poți să faci ceea ce îți dorești.
[data-today] { --kalel-day-bdrs: 50%; --kalel-day-bg: hsl(0, 86%, 40%); --kalel-day-hover-bgc: hsl(0, 86%, 70%); --kalel-day-c: #fff;
}
Îmi place ideea de a stila numerele date pentru weekenduri diferit față de zilele lucrătoare. Voi folosi o culoare roșiatică pentru a le coafa. Rețineți că putem ajunge la :not()
pseudo-clasă pentru a le selecta, lăsând doar data curentă:
[data-weekend]:not([data-today]) { --kalel-day-c: var(--kalel-weekend-c, hsl(0, 86%, 46%));
}
A, și să nu uităm de numerele săptămânii care merg înainte de primul număr de dată al fiecărei săptămâni. Am folosit un data-weeknumber
în markup pentru asta, dar numerele nu se vor afișa de fapt decât dacă le dezvăluim cu CSS, ceea ce putem face pe ::before
pseudo-element:
[data-weeknumber]::before { display: var(--kalel-weeknumber-d, inline-block); content: attr(data-weeknumber); position: absolute; inset-inline-start: 0; /* additional styles */
}
Am terminat tehnic în acest moment! Putem reda o grilă de calendar care arată datele pentru luna curentă, completă cu considerații pentru localizarea datelor în funcție de localitate și asigurându-ne că calendarul utilizează o semantică adecvată. Și tot ce am folosit a fost vanilla JavaScript și CSS!
Dar să luăm asta Încă un pas...
Redând un an întreg
Poate că trebuie să afișați un an întreg de date! Deci, în loc să redați luna curentă, este posibil să doriți să afișați toate grilele de luni pentru anul curent.
Ei bine, lucrul bun despre abordarea pe care o folosim este că putem numi render
metoda de câte ori dorim și doar să schimbăm întregul care identifică luna pe fiecare instanță. Să-l numim de 12 ori pe baza anului curent.
la fel de simplu ca si numirea render
-metoda de 12 ori și doar schimbați întregul pentru month
- i
:
[...Array(12).keys()].map(i => render( new Date(date.getFullYear(), i, date.getDate()), config.locale, date.getMonth() )
).join('')
Este probabil o idee bună să creați un nou wrapper părinte pentru anul redat. Fiecare grilă de calendar este a <kal-el>
element. Să numim noul înveliș părinte <jor-el>
, În cazul în care Jor-El este numele tatălui lui Kal-El.
<jor-el id="app" data-year="true"> <kal-el data-firstday="7"> <!-- etc. --> </kal-el> <!-- other months -->
</jor-el>
Putem folosi <jor-el>
pentru a crea o grilă pentru grilele noastre. Deci meta!
jor-el { background: var(--jorel-bg, none); display: var(--jorel-d, grid); gap: var(--jorel-gap, 2.5rem); grid-template-columns: var(--jorel-gtc, repeat(auto-fill, minmax(320px, 1fr))); padding: var(--jorel-p, 0);
}
Demo finală
Bonus: Calendar confetti
Am citit o carte excelentă numită Crearea și spargerea rețelei zilele trecute și am dat peste acest frumos „afiș de Anul Nou”:
M-am gândit că putem face ceva similar fără a schimba nimic în HTML sau JavaScript. Mi-am luat libertatea de a include nume complete luni de zile și numere în loc de nume de zi, pentru a le face mai ușor de citit. Bucurați-vă!
- Distribuție de conținut bazat pe SEO și PR. Amplifică-te astăzi.
- Platoblockchain. Web3 Metaverse Intelligence. Cunoștințe amplificate. Accesați Aici.
- Sursa: https://css-tricks.com/making-calendars-with-accessibility-and-internationalization-in-mind/
- :este
- $UP
- 1
- 11
- 2023
- 7
- 8
- 9
- 98
- a
- Capabil
- Despre Noi
- despre
- mai sus
- Absolut
- accesibilitate
- precis
- de fapt
- Suplimentar
- După
- TOATE
- singur
- deja
- și
- api
- API-uri
- aplicaţia
- apreciere
- abordare
- adecvat
- SUNT
- în jurul
- Mulțime
- bunuri
- AS
- alocate
- asociate
- At
- atribute
- audiență
- în mod automat
- fundal
- bazat
- BE
- frumos
- deoarece
- înainte
- Beneficiile
- CEL MAI BUN
- între
- Mare
- Pic
- carte
- Cutie
- Pauză
- Breaking
- Clădire
- by
- Calendar
- apel
- denumit
- apel
- CAN
- Poate obține
- caz
- Schimbare
- schimbarea
- verifica
- Verificări
- cod
- culoare
- Coloană
- COM
- Comun
- Completă
- component
- confuz
- Considerații
- conține
- conţinut
- context
- Rece
- ar putea
- ţară
- acoperi
- acoperit
- crea
- Trece
- CSS
- Curent
- personalizat
- de date
- Data
- Date
- zi
- Zi
- Mai adânc
- Mod implicit
- implicite
- definit
- definire
- În funcție
- detalii
- Determina
- Dezvoltatorii
- diferit
- discrepanţă
- Afişa
- document
- Dont
- jos
- a desena
- e
- fiecare
- Mai devreme
- mai ușor
- ediţie
- element
- Motoare
- asigurare
- Întreg
- etc
- Chiar
- exact
- exemplu
- excelent
- excepție
- există
- suplimentar
- Cădere
- Falls
- frică
- februarie
- februarie
- imaginat
- În cele din urmă
- capăt
- First
- Repara
- debit
- Pentru
- format
- Al patrulea
- Vineri
- din
- Complet
- funcţie
- Câştig
- decalaj
- obține
- obtinerea
- dat
- oferă
- Go
- merge
- bine
- Grilă
- grilă-șablon-coloane
- Manipularea
- Greu
- Avea
- având în
- ajutor
- ajută
- aici
- Evidențiați
- speranţă
- Cum
- HTML
- HTTPS
- i
- idee
- ideal
- identifică
- in
- include
- info
- informații
- inițială
- inițial
- instanță
- in schimb
- Internațional
- IT
- articole
- repetare
- ESTE
- ianuarie
- JavaScript
- doar unul
- Cunoaște
- cunoscut
- LIMBA
- limbă
- Nume
- Aspect
- lăsând
- Li
- LIBERTY
- biblioteci
- ca
- linii
- legate de
- Listă
- mic
- Localizare
- Lung
- Uite
- cautati
- Lot
- face
- FACE
- Efectuarea
- administrare
- multe
- Margine
- matematica
- max-width
- pur și simplu
- Îmbina
- metodă
- ar putea
- minte
- modificată
- moment
- luni
- Lună
- luni
- mai mult
- cele mai multe
- Mozilla
- nume
- nume
- nativ
- Navigare
- Nevoie
- Nou
- următor
- Nuanță
- număr
- numere
- obiect
- of
- on
- ONE
- on-line
- Optimizați
- Altele
- Altele
- in caz contrar
- propriu
- tampon
- pagină
- planetă
- planificare
- Plato
- Informații despre date Platon
- PlatoData
- poziţie
- pozitiv
- posibil
- destul de
- primar
- probabil
- proces
- adecvat
- cum se cuvine
- proprietate
- prevăzut
- Punând
- Rapid
- mai degraba
- ajunge
- Citeste
- roşcat
- regiune
- regiuni
- tencuială
- necesar
- reveni
- Returnează
- dezvălui
- rădăcină
- RÂND
- Regula
- s
- acelaşi
- Caută
- Motoare de cautare
- Secțiune
- selectate
- semantică
- distinct
- set
- instalare
- setări
- Șapte
- schimbare
- Pantaloni scurți
- să
- Arăta
- indicat
- Emisiuni
- asemănător
- simplu
- pur şi simplu
- întrucât
- singur
- mic
- So
- solid
- unele
- ceva
- Spaţiu
- specific
- specificată
- stand
- Standuri
- Începe
- Stat
- Pas
- Încă
- stil
- Super
- TAG
- Lua
- șablon
- mulțumesc
- acea
- Lor
- Acestea
- lucru
- lucruri
- timp
- ori
- Titlu
- la
- astăzi
- împreună
- INTRU TOTUL
- spre
- Traduceți
- adevărat
- marţi
- tutorial
- tipic
- tipic
- Actualizează
- actualizat
- us
- utilizare
- valoare
- Valori
- versiune
- W3
- Cale..
- modalități de
- săptămână
- weekend
- săptămâni
- BINE
- Ce
- Ce este
- Roată
- dacă
- care
- în timp ce
- voi
- vânt
- cu
- în
- fără
- minunat
- cuvinte
- Apartamente
- fabrică
- lume
- lume
- ar
- Înfășurat
- scrie
- scris
- an
- Tu
- zephyrnet