איך לעשות תוכן עניינים רספונסיבי, דינמי ודביק

אם הגעתם לפוסט הזה, כנראה שאנחנו נמצאים באותה הבעיה: אין תוסף table of contents שניתן להתאים את העיצוב שלו למשהו שמרגיש כמו עיצוב של השנה הנוכחית או שלא מצריך יכולות קוד מטורפות. הכל די מיושן או שלא באמת ניתן להתאמה אישית כפי שחשקה נפשנו.

עריכה: הרכיב תוכן עניינים של אלמנטור, הוא לא הרכיב המדובר, אלא רכיב שנבנה בקוד, שנמצא בתחתית המסך בצד שמאל.

תוכן עניינים

מחפשים את תוכן העניינים?
הרכיב נמצא בתחתית המסך בצד שמאל 🙂

הפתרון: לבנות משהו מאפס ולשבת לדבג 8 שעות את ה-CSS, כמובן.

איך הבעיה התעוררה?

אחרי שיחה עם אביתר גיל שמתמחה בשיפור המרות וחווית משתמש, שאמר שהפוסטים שלי די ארוכים ולפעמים מרגיש כמו גלילה אינסופית לתשובה ספציפית למקרה ספציפי (הוא אמר גם דברים טובים אני מבטיח), הציע שאוסיף תוכן עניינים דביק לפוסטים שלי, לניווט נוח יותר, בפוסטים שאני לא ממעיט בכלל במילים (מעיד על עצמי שאני חופר).

חשבתי שהרעיון מצוין, כי המטרה של הפוסטים שאני כותב היא לשתף מהידע והמחקרים שאני מבצע או ביצעתי בעבר, ולקדם התפתחות משותפת בין כל מי שקורא את הפוסטים.

הייתי רוצה שתוכלו לחזור לנקודה הרלוונטית לכם בפוסט בכל רגע נתון. 

דמיינתי לעצמי איך יראה הווידג'ט של תוכן עניינים דביק, לאחר שיחה עם עצמי, הבנתי שאין מצב שלא יהיה פתרון זמין בספריית תוספים של וורדפרס.

למען הכנות, ישנם פתרונות של תוספים מוכנים בוורדפרס. הם פשוט לא התאימו לאיך שרציתי שזה יראה אצלי. רוב התוספים לא נותנים פתרון רספונסיבי למכשירים ניידים. זה נראה טוב במסך המחשב, ו.. פחות בנייד..

תוספי וורדפרס לתוכן עניינים (דביק)

בכל מקרה, מצרף כאן רשימה של התוספים שבדקתי, מציע לכם לבדוק אותם בעצמכם ולראות אם יוכלו לתת פתרון הולם לפוסטים או דפי מידע לפרויקטים שלכם.

למה בכלל לעשות תוכן עניינים רספוניסיבי ודביק?

בראש ובראשונה, חווית משתמש. אנחנו רוצים לתת למשתמש הקצה שקורא את התוכן שלנו, לנווט בצורה נוחה בין תוכן הפוסט וראשי הפרקים לפי הצורך שלו ולא בהכרח לפי הצורך שלנו או הסדר שאנחנו כתבנו.

ישנם מספר מקומות שכותבי תוכן או בוני אתרים יטמיעו תוכן עניינים:

  • בתחילת הפוסט (לרוב אחרי הכותרת הראשית ומידע על הפוסט)
    דוגמה לתוכן עניינים בבלוג שלי
    דוגמה לתוכן עניינים בבלוג שלי
  • בסוף הפוסט
    דוגמה לתוכן עניינים בסוף הפוסט, נלקח מהבלוג של עשהאל דרייר
    דוגמה לתוכן עניינים בסוף הפוסט, נלקח מהבלוג של עשהאל דרייר
  • בצד הפוסט (מתאים יותר לתצוגה במחשב)
    דוגמה לתוכן עניינים דביק בצד נלקח מהבלוג של שי תודר
    דוגמה לתוכן עניינים דביק בצד נלקח מהבלוג של שי תודר

יתרונות וחסרונות למיקומים של תוכן העניינים בפוסטים

לכל דרך תצוגה יש יתרונות וחסרונות.

תוכן עניינים שנמצא בתחילת הפוסט, נראה כמו הבחירה שהיא ברירת מחדל: תוכן העניינים נותן תקציר של ראשי פרקים לנושאים שיהיו בפוסט למשתמש חדש שקורא את הפוסט, ואפילו יהיה יותר רלוונטי למשתמש שקרא את הפוסט וחוזר לפוסט בשביל לנווט מחדש לאזור הספציפי שמעוניין להגיע אליו.

תוכן עניינים שנמצא בסוף, רלוונטי רק למי שקרא את הפוסט עד הסוף ורוצה לנווט למעלה לנקודה רלוונטית בשביל לשתף או לגזור/לשמור קטע רלוונטי.

תוכן עניינים שנמצא בצד, רלוונטי אך ורק לגולשים מהמחשב ואולי טאבלט, אבל לא מתאים בכלל למובייל.

התוספים שיש ברשימה של וורדפרס נתנו פתרון, חלקם הכריחו לעשות עבודה ידנית פר פוסט (הטמעה של הכותרות עצמאית), חלקם הכריחו לוותר על עיצוב או מוגבלים ברמת העיצוב, וברובם אין התאמה או התחשבות לניידים. 

אחרי שהתקלתי את עצמי בשאלה הזו ולא מצאתי לה פתרון, ידעתי שאצטרך לחזור לזה עם אנרגיה מחודשת ולמצוא פתרון מותאם יותר.

למה ווידג'ט תוכן עניינים של אלמנטור לא ישים?

הווידג'ט תוכן עניינים של אלמנטור שמיש ומתאים בתור ווידג'ט דביק בעמודה נפרדת (תוכן עניינים בצד) אך רלוונטי רק למכשירים נייחים. בנייד, אי אפשר להפוך את הווידג'ט תוכן עניינים לדביק ורספונסיבי מבלי שיתפוס את כל רוחב המסך ויהפוך את הפוסט ללא קריא. 

הווידג'ט תוכן עניינים של אלמנטור גם לא באמת נגיש, אם הוא במצב סגור, אי אפשר לפתוח אותו בעזרת המקלדת. אם הווידג'ט פתוח, אז אפשר לקבל פוקוס על קישורי עוגן של הכותרות.

דוגמה חיה לתוכן עניינים דביק שמצאתי

דוגמה שאני מעוניין להראות היא בפוסט Guideline מאתר קוצ'בה (קורא להם כוכבה ולא מצטער על זה), אצלם התוכן עניינים דביק ונמצא בתחתית המסך, הפתיחה של הווידג'ט מציגה פופ אפ דיפולטיבי של המכשיר ואופציה לנווט לכותרת הרלוונטית בפוסט עם אופציה לחזרה למעלה (של הפוסט).

*בזמן כתיבת הפוסט צילמתי וידאו של שימוש בתוכן עניינים הדביק של קוצ'בה אך לצערי הם צירפו כפתור של החלפת שפות שמסתיר קצת את תוכן העניינים הדביק..

הייתי מוכן להתפשר על העיצוב הדיפולטי של הפופ-אפ ולהשמיש את הפתרון שקוצ'בה השתמשה אך לא הצלחתי להעתיק את הקוד.

יצא שעזבתי את הנושא שוב, "אחזור לזה עם עוד אנרגיה" אמרתי לעצמי.

לאחר תקופה שהרעיון התבשל אצלי עוד בראש, הבנתי שאני רוצה לבנות את זה בעזרת CSS, HTML ולמצוא מתכנת שיתן לי את המענה שצריך ב-JS בשביל להפוך את כל העניין לדינאמי מבלי שינוי כותרות כל פעם פר פוסט, ולזכור לבצע תוספות ושינויים גם לתוכן עניינים כשאני מוסיף/מעדכן תכנים בפוסטים שכבר עלו. 

כתבתי באחת מהקהילות לבניית אתרים את המטרה שלי, ולמזלי תום תובאל נענה לבקשתי ולאתגרי, לאחר שהסברתי לו את הצורה שחשבתי לבנות את זה ב-HTML ואת הרצונות שלי ב-JS יצאנו לדרך.
(הדרך: 8 שעות בשבילי לדבג CSS וגג 20 דקות בשביל תום לכתוב JS)

HTML & תגית Dialog

השתמשתי בתגית <dialog> שהיא תגית פופ אפ מובנית שלא מצריכה יותר מידי Javascript ומקבלת פוקוס במקלדת ברגע שהיא מופיע עליה. 

על התגית למדתי בסרטון של קווין פאוול, נסיך ה-CSS

לקריאה נוספת על תגית <dialog>

ה-HTML מתחלק לשני תגיות, Button & dialog. כך זה יראה:

<button class="sticky-toc open-button" title="לחצו לפתיחת תוכן עניינים"></button>
  
<dialog class="modal" id="modal">
<h2>תוכן עניינים</h2>
<p></p>
<button title="לחצו לסגירת תוכן עניינים" class="close-button">סגירה</button>
</dialog>
 

CSS

את ה-CSS עשיתי לפי התאמה של הבלוג. 

עיצוב הכפתור, הלינקים, הסקרול-בר, נמשכים אוטומטית (inherit) מהגדרות הבסיס של התבנית, אם לא הגדרתם אותם, אז הם יהיו בברירת מחדל בהתאם לאתר שלכם.

.sticky-toc { position: fixed; bottom: 15px; left: 15px; border-radius: 50%; background-color:#1F1F1F; width: auto; height: auto; padding: 4px; opacity: 1; z-index: 999; box-shadow: 0px 0px 0px 2px #000; }.sticky-toc:before { content: ''; background-image:url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20px" height="20px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"%3E%3Cpath fill="white" d="M3 9h14V7H3v2m0 4h14v-2H3v2m0 4h14v-2H3v2m16 0h2v-2h-2v2m0-10v2h2V7h-2m0 6h2v-2h-2v2Z"%2F%3E%3C%2Fsvg%3E'); background-repeat: no-repeat; background-position: center; background-size: 25px; font-style: normal; font-weight:normal; border-radius:50% 0 0 50%; padding: 12px; color:white; position: relative; }.sticky-toc:is(:hover,:focus) { background-color:hsl(0, 100%, 10%); color:#1F1F1F; box-shadow: none; }dialog { padding: 1em; max-width: 50ch; border: 0; color:#1F1F1F; background-color:#E9E6E1; border-radius:10px; }dialog::backdrop { background: rgb(0 0 0 / 0.4); }dialog h2 { text-align: center; margin-top:0.5rem; }dialog p a { line-height: 2; }

שימו לב בבקשה שבחלק מהמקומות תצטרכו להשתמש בצבעים שלכם. השתמשתי בקוד ב- CSS Variables, לנוחות החלפתי את ה-CSS רק לצבעים ועל אחריותכם להחליף את הצבעים בהתאמה לאתרכם.

Javascript

ב-Javascript קורה כל הקסם.

הדיאלוג מופיעה כשלוחצים על הכפתור, הדיאלוג נעלם כאשר לוחצים על Esc במקלדת, או כשלוחצים מחוץ לדיאלוג. בנוסף, הדיאלוג נעלם כאשר לוחצים על כל קישור שהוא כותרת.

ה-JS מושך דינאמית את כל הכותרות ברמת h2-h6 מהפוסט. ביקשתי מתום שיהיה קלאס ספציפי שתחתיו ימשכו כל הכותרות. 

ברירת המחדל יכולה להיות body ואז ימשכו כל הכותרת האפשריות באתר שהן בתגית h2-h6. 

הקלאס ששמתי מתאים לכל תוכן הפוסט של אלמנטור, זה הקלאס המתאים שנשים גם בוידג'ט תוכן עניינים של אלמנטור.

הכותרות שנמשכות דינמית מגיעות בתגית <a> וכמובן תביא אותנו לאזור הרלוונטי בעמוד בלחיצה על הלינק.

const modal = document.querySelector("#modal"); const modalContent = modal.querySelector("p"); // leave 1 p tag empty const openModal = document.querySelector(".open-button"); const closeModal = document.querySelector(".close-button"); openModal.addEventListener("click", () => { modal.showModal(); }); closeModal.addEventListener("click", () => { modal.close(); }); window.onmousedown = function (event) { if (event.target == modal) { modal.close(); } }; document.addEventListener("DOMContentLoaded", function () { const body = document.querySelector(".elementor-widget-theme-post-content"); // The Content area. Change ".elementor-widget-theme-post-content" to .class-name const headings = body.querySelectorAll("h2, h3, h4, h5, h6"); for (var i = 0; i < headings.length; i++) { headings[i].setAttribute("id", "toc-anchor--" + i); br = document.createElement("br"); if (i > 0) modalContent.appendChild(br); row = document.createElement("a"); row.setAttribute("href", "#toc-anchor--" + i); row.innerText = headings[i].innerText; modalContent.appendChild(row); row.addEventListener("click", () => { modal.close(); }); } }, false );

תודה לתום תובאל על העזרה עם ה-JS!

הקוד הסופי

<button class="sticky-toc open-button" title="לחצו לפתיחת תוכן עניינים"></button>
  
<dialog class="modal" id="modal">
<h2>תוכן עניינים</h2>
<p></p>
<button title="לחצו לסגירת תוכן עניינים" class="close-button">סגירה</button>
</dialog>
<script>
const modal = document.querySelector("#modal");
const modalContent = modal.querySelector("p"); // leave 1 p tag empty
const openModal = document.querySelector(".open-button");
const closeModal = document.querySelector(".close-button");
openModal.addEventListener("click", () => {
  modal.showModal();
});
closeModal.addEventListener("click", () => {
  modal.close();
});
window.onmousedown = function (event) {
  if (event.target == modal) {
    modal.close();
  }
};
document.addEventListener("DOMContentLoaded", function () {
  const body = document.querySelector(".elementor-widget-theme-post-content"); // The Content area. Change ".elementor-widget-theme-post-content" to .class-name
  const headings = body.querySelectorAll("h2, h3, h4, h5, h6");
  
  for (var i = 0; i < headings.length; i++) {
    headings[i].setAttribute("id", "toc-anchor--" + i);
    br = document.createElement("br");
    if (i > 0) modalContent.appendChild(br);
    row = document.createElement("a");
    row.setAttribute("href", "#toc-anchor--" + i);
    row.innerText = headings[i].innerText;
    modalContent.appendChild(row);
    row.addEventListener("click", () => {
  modal.close();
});
  }
  },
  false
);
</script>
<style>
.sticky-toc {
position: fixed;
bottom: 15px;
left: 15px;
border-radius: 50%;
background-color:#1F1F1F;
width: auto;
height: auto;
padding: 4px;
opacity: 1;
z-index: 999;
}
.sticky-toc:before {
content: '';
background-image:url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20px" height="20px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"%3E%3Cpath fill="white" d="M3 9h14V7H3v2m0 4h14v-2H3v2m0 4h14v-2H3v2m16 0h2v-2h-2v2m0-10v2h2V7h-2m0 6h2v-2h-2v2Z"%2F%3E%3C%2Fsvg%3E');
background-repeat: no-repeat;
background-position: center;
background-size: 25px;
font-style: normal;
font-weight:normal;
border-radius:50% 0 0 50%;
padding: 12px;
color:white;
position: relative;
}
.sticky-toc:is(:hover,:focus) {
background-color:hsl(0, 100%, 10%);
    color:#1F1F1F;
}
dialog {
padding: 1em;
max-width: 50ch;
border: 0;
color:#1F1F1F;
background-color:#E9E6E1;
border-radius:10px;
}
dialog::backdrop {
background: rgb(0 0 0 / 0.4);
}
dialog h2 {
    text-align: center;
    margin-top:0.5rem;
}
dialog p a {
    line-height: 2;
}
</style>

Happy Debugging!

שתפו את הפוסט:

פייסבוק
וואטסאפ
לינקדאין
אי-מייל
מעוניינים לקבל עדכונים על פוסטים שאני כותב במייל? >>

2 תגובות

  1. משום מה הקוד לא עובד אצלי – וגם לא מצאתי איפה אני בדיוק משנה את החלק שמגדיר מאיפה הוא ימשוך את התגיות H ציינת שזה אמור לקחת את זה מכל הBODY – אבל לא נראה שזה עושה את זה…

    1. היי אלעד, הקלאס המסומן הוא ".elementor-widget-theme-post-content"

      וכאן נמצאים הכותרות HTML שאפשר לבחור כדי להציג אותם בתוכן עניינים: const headings = body.querySelectorAll("h2, h3, h4, h5, h6");

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *

דילוג לתוכן