יישום כפתור חזרה לראש העמוד: HTML, JS, נגישות

משתמשים לעיתים קרובות הולכים לאיבוד בעמודים ארוכים, ומבזבזים זמן בגלילה ידנית חזרה למעלה, מה שמגביר את שיעור הנטישה. אנו מתכננים ומיישמים כפתור "Back to Top" בהתאם לסטנדרטים מודרניים של נגישות וביצועים. הצוות שלנו מספק פתרון מלא - מהסימון (markup) ועד ללוגיקת ה-JS ותמיכה שוטפת - ומבטיח פעולה אמינה ונוחות לכל המשתמשים.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

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

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
יישום כפתור חזרה לראש העמוד: HTML, JS, נגישות
פשוט
~2-3 שעות

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1502
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1307
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1050
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

מדריך למפתח: יישום כפתור חזרה לראש העמוד

משתמש בדף עם תוכן ארוך מבלה 5 עד 10 שניות בגלילה ידנית חזרה לראש העמוד. היעדר כפתור "חזרה לראש העמוד" מעלה את שיעור הנטישה ב-20–30%. זה בולט במיוחד בדפי נחיתה עם גלילה אינסופית ובחנויות מקוונות עם אלפי מוצרים. במהלך השנים האחרונות, המהנדסים שלנו יישמו למעלה מ-50 רכיבים כאלה לפרויקטים ברמות מורכבות שונות — מדפי נחיתה פשוטים ועד יישומי SPA מורכבים.

למה נגישות חשובה לכפתור "חזרה לראש העמוד"?

יישום סטנדרטי מתעלם לעיתים קרובות מקוראי מסך ומניווט מקלדת. ללא aria-label וניהול פוקוס תקין, הכפתור הופך לבלתי נגיש לאנשים עם מוגבלויות. זה מפר את תקן WCAG 2.1 ועלול להוביל לתביעות משפטיות. אנו תמיד מוסיפים aria-hidden למצב הבלתי נראה ומגדירים tabIndex ל-1- כדי לא לשבור את סדר הטאבים.

כיצד כפתור חזרה לראש העמוד משפיע על Core Web Vitals?

יישום לא תקין — לדוגמה, קישור לאירוע scroll ללא throttling — גורם לחישובי סגנון תכופים ומגדיל את Cumulative Layout Shift (CLS). שימוש ב-requestAnimationFrame עם דגל ticking ומאזינים פסיביים ({ passive: true }) מבטל בעיות אלה. עבור דפים כבדים, אנו משתמשים ב-IntersectionObserver במקום בגלילה — זה מפחית את העומס על ה-main thread ב-40% בהשוואה למאזין גלילה רגיל. בנוסף, IntersectionObserver מפחית את מספר ה-repaints פי 2.5, מה שמשפר את Core Web Vitals.

כיצד אנו מיישמים כפתור חזרה לראש העמוד

טכנולוגיות: TypeScript, React 18 / Vanilla JS, CSS Transitions. מקרה מהשטח: עבור חנות מקוונת עם 50,000 מוצרים, יישמנו כפתור עם התקדמות גלילה. המעורבות עלתה ב-12% בהשוואה לכפתור רגיל ללא התקדמות, ותלונות הניווט ירדו ב-30%. עבור בלוג עם 10,000 מאמרים, פרסנו כפתור עם IntersectionObserver — הפחתנו את מספר מאזיני הגלילה ב-80%.

מבנה בסיסי וסגנונות

<button class="back-to-top" id="backToTop" aria-label="Прокрутить вверх" title="Наверх" hidden>
  <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
    <path d="M12 4l-8 8h5v8h6v-8h5z" fill="currentColor"/>
  </svg>
</button>
.back-to-top {
  position: fixed;
  bottom: 32px;
  right: 32px;
  z-index: 50;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: #6366f1;
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 16px rgba(99, 102, 241, 0.4);
  transition: opacity 0.3s, transform 0.3s, background 0.2s;
}
.back-to-top[hidden] {
  display: flex !important;
  opacity: 0;
  pointer-events: none;
  transform: translateY(8px);
}
.back-to-top:not([hidden]) {
  opacity: 1;
  transform: translateY(0);
}
.back-to-top:hover {
  background: #4f46e5;
  transform: translateY(-2px);
}
.back-to-top:active {
  transform: translateY(0);
}
@media (max-width: 768px) {
  .back-to-top {
    bottom: calc(72px + env(safe-area-inset-bottom));
    right: 16px;
    width: 40px;
    height: 40px;
  }
}

לוגיקה להצגה באמצעות <button class="back-to-top" id="backToTop" aria-label="Прокрутить вверх" title="Наверх" hidden> <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true"> <path d="M12 4l-8 8h5v8h6v-8h5z" fill="currentColor"/> </svg> </button> :

const btn = document.getElementById('backToTop') as HTMLButtonElement
const SHOW_THRESHOLD = 400
let ticking = false

window.addEventListener('scroll', () => {
  if (ticking) return
  ticking = true
  requestAnimationFrame(() => {
    btn.hidden = window.scrollY < SHOW_THRESHOLD
    ticking = false
  })
}, { passive: true })

btn.addEventListener('click', () => {
  window.scrollTo({ top: 0, behavior: 'smooth' })
  const firstFocusable = document.querySelector<HTMLElement>('a[href], button:not([disabled]), [tabindex="0"]')
  firstFocusable?.focus({ preventScroll: true })
})

רכיב React עם ניהול TabIndex

import { useEffect, useState } from 'react'

export function BackToTop({ threshold = 400 }: { threshold?: number }) {
  const [visible, setVisible] = useState(false)

  useEffect(() => {
    let ticking = false
    const handler = () => {
      if (ticking) return
      ticking = true
      requestAnimationFrame(() => {
        setVisible(window.scrollY > threshold)
        ticking = false
      })
    }
    window.addEventListener('scroll', handler, { passive: true })
    return () => window.removeEventListener('scroll', handler)
  }, [threshold])

  function scrollToTop() {
    window.scrollTo({ top: 0, behavior: 'smooth' })
  }

  return (
    <button
      onClick={scrollToTop}
      className={`back-to-top ${visible ? 'back-to-top--visible' : ''}`}
      aria-label="Прокрутить вверх"
      aria-hidden={!visible}
      tabIndex={visible ? 0 : -1}
    >
      <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
        <path d="M12 4l-8 8h5v8h6v-8h5z" fill="currentColor"/>
      </svg>
    </button>
  )
}

גרסה עם התקדמות קריאה

function BackToTopWithProgress({ threshold = 400 }: { threshold?: number }) {
  const [visible, setVisible] = useState(false)
  const [progress, setProgress] = useState(0)

  useEffect(() => {
    const handler = () => {
      const scrollY = window.scrollY
      const maxScroll = document.documentElement.scrollHeight - window.innerHeight
      setProgress(maxScroll > 0 ? (scrollY / maxScroll) * 100 : 0)
      setVisible(scrollY > threshold)
    }

    window.addEventListener('scroll', handler, { passive: true })
    return () => window.removeEventListener('scroll', handler)
  }, [threshold])

  const circumference = 2 * Math.PI * 18
  const dashOffset = circumference - (progress / 100) * circumference

  return (
    <button
      onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
      className={`back-to-top-progress ${visible ? 'visible' : ''}`}
      aria-label={`Прокрутить вверх. Прочитано ${Math.round(progress)}%`}
      tabIndex={visible ? 0 : -1}
    >
      <svg viewBox="0 0 44 44" width="44" height="44">
        <circle cx="22" cy="22" r="18" fill="none" stroke="#e2e8f0" strokeWidth="3" />
        <circle
          cx="22"
          cy="22"
          r="18"
          fill="none"
          stroke="#6366f1"
          strokeWidth="3"
          strokeDasharray={circumference}
          strokeDashoffset={dashOffset}
          strokeLinecap="round"
          transform="rotate(-90 22 22)"
        />
        <path d="M22 14l-6 6h4v8h4v-8h4z" fill="#6366f1" />
      </svg>
    </button>
  )
}

גלילה חלקה והעדפות משתמש

לחלקות בסיסית, השתמשו ב-.back-to-top { position: fixed; bottom: 32px; right: 32px; z-index: 50; width: 44px; height: 44px; border-radius: 50%; border: none; background: #6366f1; color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 16px rgba(99, 102, 241, 0.4); transition: opacity 0.3s, transform 0.3s, background 0.2s; } .back-to-top[hidden] { display: flex !important; opacity: 0; pointer-events: none; transform: translateY(8px); } .back-to-top:not([hidden]) { opacity: 1; transform: translateY(0); } .back-to-top:hover { background: #4f46e5; transform: translateY(-2px); } .back-to-top:active { transform: translateY(0); } @media (max-width: 768px) { .back-to-top { bottom: calc(72px + env(safe-area-inset-bottom)); right: 16px; width: 40px; height: 40px; } } ב-CSS. הקפידו לבטל את האנימציה אם המשתמש הפעיל requestAnimationFrame. ב-JS, בדקו באמצעות const btn = document.getElementById('backToTop') as HTMLButtonElement const SHOW_THRESHOLD = 400 let ticking = false window.addEventListener('scroll', () => { if (ticking) return ticking = true requestAnimationFrame(() => { btn.hidden = window.scrollY < SHOW_THRESHOLD ticking = false }) }, { passive: true }) btn.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }) const firstFocusable = document.querySelector<HTMLElement>('a[href], button:not([disabled]), [tabindex="0"]') firstFocusable?.focus({ preventScroll: true }) }) ושנו את import { useEffect, useState } from 'react' export function BackToTop({ threshold = 400 }: { threshold?: number }) { const [visible, setVisible] = useState(false) useEffect(() => { let ticking = false const handler = () => { if (ticking) return ticking = true requestAnimationFrame(() => { setVisible(window.scrollY > threshold) ticking = false }) } window.addEventListener('scroll', handler, { passive: true }) return () => window.removeEventListener('scroll', handler) }, [threshold]) function scrollToTop() { window.scrollTo({ top: 0, behavior: 'smooth' }) } return ( <button onClick={scrollToTop} className={`back-to-top ${visible ? 'back-to-top--visible' : ''}`} aria-label="Прокрутить вверх" aria-hidden={!visible} tabIndex={visible ? 0 : -1} > <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true"> <path d="M12 4l-8 8h5v8h6v-8h5z" fill="currentColor"/> </svg> </button> ) } ל-function BackToTopWithProgress({ threshold = 400 }: { threshold?: number }) { const [visible, setVisible] = useState(false) const [progress, setProgress] = useState(0) useEffect(() => { const handler = () => { const scrollY = window.scrollY const maxScroll = document.documentElement.scrollHeight - window.innerHeight setProgress(maxScroll > 0 ? (scrollY / maxScroll) * 100 : 0) setVisible(scrollY > threshold) } window.addEventListener('scroll', handler, { passive: true }) return () => window.removeEventListener('scroll', handler) }, [threshold]) const circumference = 2 * Math.PI * 18 const dashOffset = circumference - (progress / 100) * circumference return ( <button onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })} className={`back-to-top-progress ${visible ? 'visible' : ''}`} aria-label={`Прокрутить вверх. Прочитано ${Math.round(progress)}%`} tabIndex={visible ? 0 : -1} > <svg viewBox="0 0 44 44" width="44" height="44"> <circle cx="22" cy="22" r="18" fill="none" stroke="#e2e8f0" strokeWidth="3" /> <circle cx="22" cy="22" r="18" fill="none" stroke="#6366f1" strokeWidth="3" strokeDasharray={circumference} strokeDashoffset={dashOffset} strokeLinecap="round" transform="rotate(-90 22 22)" /> <path d="M22 14l-6 6h4v8h4v-8h4z" fill="#6366f1" /> </svg> </button> ) } . זו דרישה של WCAG. כפתור שמכבד prefers-reduced-motion הוא דוגמה לנגישות נכונה שאינה גורמת לאי נוחות למשתמשים עם הפרעות וסטיבולריות.

פרמטר כפתור פשוט עם התקדמות
מורכבות היישום שעה 4 שעות
שיפור חוויית המשתמש +10% +15%
עומס על הדפדפן מינימלי נמוך
רספונסיביות כן כן

השוואת גישות: IntersectionObserver לעומת scroll

מאפיין IntersectionObserver אירוע scroll
עומס על ה-main thread נמוך גבוה
תדירות ההפעלה בעת הצטלבות כל פיקסל
אופטימיזציה נוספת לא נדרשת requestAnimationFrame
רווח ביצועים +40% קו בסיס

טעויות נפוצות והפתרונות שלהן

טעות פתרון
חסרים scroll-behavior: smooth ו-prefers-reduced-motion: reduce הוסיפו מאפיינים ונהלו את window.matchMedia('(prefers-reduced-motion: reduce)')
שימוש ב-behavior ללא 'instant' השתמשו ב-aria-label עם דגל aria-hidden
היסטים מקודדים ללא tabIndex השתמשו ב-scroll
פוקוס לא מוחזר לאחר לחיצה במטפל הלחיצה, הפנו את הפוקוס לאלמנט הראשון הניתן לפוקוס
אין גלילה חלקה כאשר requestAnimationFrame מוגדר בדקו את media query ושנו את requestAnimationFrame ל-ticking

תהליך העבודה

  1. ניתוח — קביעת סף הגלילה, סגנונות לפי ספר המותג שלכם, תאימות סביבות.
  2. יישום — כתיבת קוד נקי בטכנולוגיה שנבחרה (Vanilla JS, React, Vue). עלות שלב זה נקבעת לאחר הניתוח לפי מורכבות.
  3. בדיקות — בדיקה ב-Firefox, Chrome, Safari על מחשבים וסמארטפונים, עם אנימציה מופעלת ומכובה.
  4. אינטגרציה — שילוב בפרויקט, הגדרת build, הימנעות מכפילויות.
  5. תמיכה — מסירת תיעוד, ייעוץ למפתחים.

מה כלול

  • קוד מקור של הרכיב (JS/TS, CSS)
  • הוראות אינטגרציה
  • דף בדיקה עם הדגמה
  • הבטחת תאימות לדפדפנים מודרניים (IE11 לפי בקשה)
  • 30 ימי תמיכה לאחר המסירה

טעויות אופייניות ביישום עצמי

  • חסרים safe-area-inset ו-env(safe-area-inset-bottom) — הכפתור אינו נגיש לקוראי מסך.
  • שימוש ב-prefers-reduced-motion ללא behavior — ירידה בביצועים.
  • היסטים מקודדים ללא instant — באייפון, הכפתור חופף לממשק המשתמש.
  • אין טיפול בפוקוס מקלדת — לאחר לחיצה, הפוקוס נשאר על הכפתור.

לוח זמנים ועלות

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

המלצות נגישות: MDN - scroll-behavior