הפוסט הזה הוא חלק מסדרה על הקוד של עמוד בתי הספר של אורט באתר אורט החדש, עמוד שמכיל רשימה של כל בתי הספר של אורט לפי ערים, ומפה עם סימוני כל בתי הספר.
בפרק ההקדמה סיפרתי על הרקע הכללי של האתר ומבנה הסדרה, בפרק הראשון סיפרתי על סינון הערים לפי מחרוזת חיפוש, בפרק השני דיברנו על אתגרי המפה ואיך מתחילים איתה, בפרק השלישי הכנו את התשתית לסנכרון הרשימה והמפה, בפרק הרביעי צללנו לסנכרון בין המפה לערים ברשימה, ובפרק החמישי התמקדנו רק במפה: מה שקורה כשלוחצים על אחד מסימוני בתי הספר במפה.
בפרק הזה אנחנו יורדים לרזולוציה נמוכה, ומספרים על ההתמודדות עם מצב שבו המפה והרשימה אינן זו לצד זו.
רזולוציה נמוכה – האתגר
בתצוגה של רזולוציה רחבה, המפה והרשימה ניצבות זו לצד זו. למפה יש גובה מקסימלי והיא מתאימה את עצמה לגובה הזה, ולרשימה יש גובה מקסימלי, וגלילה פנימית. אבל מה עושים במכשירים ניידים? אין מקום להציב את הדברים זה לצד זה, ומצד שני אי אפשר לקצר את הרשימה עד כדי כך שגם המפה תוצג, מפני שזה ייצור גלילה כפולה – גם בתוך הרשימה, וגם בכל המסך. אבל אם הרשימה תוצג במלואה, כיצד יידע המשתמש יש מפה אחריה?
הפתרון – כפתורי קפיצה
לשם כך יצרנו כפתורים. כאשר הגולש רואה את הרשימה, מופיע כפתור שמקפיץ אותו למפה. וכאשר המפה מופיעה והרשימה נעלמת, מופיע כפתור הקפצה לרשימה.
קוד ה-HTML:
את הקוד הזה הכנסתי בווידג’ט HTML של אלמנטור מעל ווידג’ט האקורדיון. הוא מכיל div ריק שמשמש כעוגן לתחילת רשימת בתי הספר, ושני קישורים – אחד לעוגן הזה, ואחד ל-div שעוטף את המפה.
את העוגן לרשימת בתי הספר לכאורה לא הייתי חייבת ליצור, כי יכולתי להשתמש ב-ID של הרשימה שניתן על ידי אלמנטור. אבל החלטתי כן ליצור עוגן, כי לא רציתי לסמוך על מאפיינים של אלמנטור שעלולים להשתנות בעתיד.
למפה לא הייתי צריכה ליצור עוגן מפני שאת ה-div העוטף של המפה יצרתי בעצמי. אמנם את ה-ID נתתי לפי הוראות השימוש במפות של גוגל, ואם הם פעם ישנו את ההוראות אצטרך לזכור לשנות את ההפניה, אבל דיה לצרה בשעתה.
לקישורים הוספתי גם HTML שיוצר איקוני FontAwsome.
<a id="school_list_anchor" name="school_list_anchor"></a>
<a class="schools_page_link link_to_list" href="#school_list_anchor"><i class="fas fa-list"></i>רשימה</a>
<a class="schools_page_link link_to_map" href="#map"><i class="fas fa-map"></i>מפה</a>
קוד ה-CSS
וכך הפכתי אותם לעגולים וורודים, עם איקון לכל אחד:
@media screen and (max-width: $screen__medium) {
a.schools_page_link {
&.schools_page_link { // need the class to repeat itself because of specicifity
background-color: $color__pink-background;
border-radius: 50%;
color: $white;
box-shadow: -3px 3px 0px 0px #939393;
font-size: $font__size_xx-small;
position: fixed;
z-index: 10;
bottom: 3rem;
left: 3rem;
text-align: center;
}
&.link_to_map,
&.link_to_list {
padding: 0.7rem 1.5rem 0.5rem;
}
&.link_to_list {
padding-right: 1.2rem;
padding-left: 1.2rem;
}
.fas {
display: block;
font-size: 20px;
}
}
}
@media screen and (min-width: $screen__medium) {
a.schools_page_link {
display: none;
}
}
שימו לב שברזולוציות גבוהות הסתרתי אותם לחלוטין. הם נועדו רק למצבים שבהם המפה מופיעה אחרי הרשימה.
קוד ה-JavaScript
טוב, ועכשיו לחלק המאתגר: איך להראות רק אחד מהם בכל מצב.
הצבתי מאזין לאירוע גלילה:
window.addEventListener('scroll', windowScrollHandler);
הסתרה וגילוי של כל כפתור בזמנו
בפונקציה הזאת, אם המפה מופיעה לגולש, אני מסתירה את כפתור ההקפצה למפה ומראה את כפתור הקפיצה לרשימה. ולהפך – אם הרשימה היא זו שמופיעה כעת במסך של הגולש, יוסתר כפתור הקפיצה לרשימה ויוצג כפתור ההקפצה למפה. כל זה בתנאי שהכפתורים אינם נסתרים שניהם – כששניהם נסתרים, סימן שאנחנו ברזולוציה גבוהה.
/**
* There is a button to jump from the city list to the map, and a button to jump back.
* They interchange on scroll:
* When the list is in view show the button to the map,
* and when the map is in view show the button to the list
*/
function windowScrollHandler() {
let linkToMap = $('.link_to_map');
let linkToList = $('.link_to_list');
if (linkToMap.is(':visible') || linkToList.is(':visible')) {
if (isElementInViewport(mapElement)) {
// when map is in view, hide the map button
linkToMap.hide();
linkToList.show();
} else {
linkToMap.show();
linkToList.hide();
}
}
}
פונקציה לגילוי מהו האלמנט שנמצא על המסך – המפה או הרשימה
כמו שאפשר לראות בפוקנציה הנ”ל, יצרתי פונקציה שבודקת אם המפה גלויה לגולש או שהרשימה גלויה לגולש. את הקוד מצאתי כרגיל ב-SO. היא מסתמכת על הפונקציה getBoundingClientRect
ומשווה את הערך של הגבולות העליון והתחתון שלו, עם גובה המסך. אחרי זה מצאתי ספריית JS קטנה שלכאורה עושה את זה עם פחות משאבים. אולי יום אחד אבדוק את החלופה הזאת.
/**
* Check if parameter is in viewport by comparing its borders and comparing it with the window's height
* @param el
* @returns true if element is in viewport, and false if not.
*/
function isElementInViewport(el) {
//Use jQuery
if (typeof $ === "function" && el instanceof $) {
el = el[0];
}
/* The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport. */
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.top <= $(window).height()
||
rect.bottom >= 0 &&
rect.bottom <= $(window).height()
);
}
ובזאת סיימנו את ההקפצה החמודה הזאת. במושגי עלות/תועלת זה היה מהקודים המספקים: מצד אחד לא לקח לי הרבה זמן לתכנת את זה, ומצד שני אני נהנית לשחק בטלפון שלי עם הקפיצות…