البرمجة متوسط 8 دقيقة

كيفية بناء رأس صفحة ثابت يختفي عند التمرير

رأس صفحة يلتصق بالأعلى أثناء التمرير بات أمراً بديهياً. أما رأس الصفحة الذي يختفي بذكاء حين تمرر للأسفل — مستردّاً مساحة الشاشة — ويعود للظهور حين تمرر للأعلى، فهو تجربة أفضل بكثير. هذا الدليل يبني الاثنين معاً، خطوة بخطوة.

الخطوات

  1. 1

    جعل الرأس ثابتاً باستخدام CSS

    position: sticky يُبقي الرأس ضمن تدفق المستند (على عكس fixed) ويثبّته أعلى نافذة العرض بمجرد أن يتجاوزه المستخدم أثناء التمرير. z-index: 50 يضمن ظهوره فوق محتوى الصفحة.

    css
    .site-header {
      position: sticky;
      top: 0;
      z-index: 50;
      background: #fff;
      /* optional: soften the edge between header and content */
      box-shadow: 0 1px 0 rgb(0 0 0 / .06);
    }
  2. 2

    تعريف الحالة المخفية بكلاس CSS

    استخدم كلاس .is-hidden يُزيح الرأس خارج نطاق العرض بـ transform: translateY(-100%). الحركة المعتمدة على Transform تُعالَج بالـ GPU — لا تُشغّل إعادة حساب التخطيط وتبقى سلسة عند 60 إطاراً في الثانية حتى على الأجهزة المتوسطة.

    css
    .site-header {
      position: sticky;
      top: 0;
      z-index: 50;
      background: #fff;
      transition: transform 0.3s ease, box-shadow 0.3s ease;
    }
    
    .site-header.is-hidden {
      transform: translateY(-100%);
    }
  3. 3

    تتبع اتجاه التمرير في JavaScript

    احفظ موضع التمرير السابق في lastScrollY. عند كل حدث تمرير، قارن قيمة window.scrollY الحالية بها. التمرير للأسفل يعني أن القيمة الجديدة أكبر؛ والتمرير للأعلى يعني أنها أصغر.

    javascript
    const header = document.querySelector('.site-header');
    let lastScrollY = window.scrollY;
    
    window.addEventListener('scroll', () => {
      const currentScrollY = window.scrollY;
      const scrollingDown = currentScrollY > lastScrollY;
    
      header.classList.toggle('is-hidden', scrollingDown);
    
      lastScrollY = currentScrollY;
    }, { passive: true });
  4. 4

    إضافة حدّ تمرير لمنع الوميض

    قرب أعلى الصفحة، قد يتسبب الاهتزاز البسيط في التمرير بوميض الرأس ظهوراً واختفاءً. اخفِ الرأس فقط بعد أن يتجاوز المستخدم حدّاً معيناً (مثلاً ارتفاع الرأس نفسه).

    javascript
    const header = document.querySelector('.site-header');
    const THRESHOLD = header.offsetHeight;
    let lastScrollY = window.scrollY;
    
    window.addEventListener('scroll', () => {
      const currentScrollY = window.scrollY;
    
      // Never hide the header when near the top
      if (currentScrollY < THRESHOLD) {
        header.classList.remove('is-hidden');
        lastScrollY = currentScrollY;
        return;
      }
    
      const scrollingDown = currentScrollY > lastScrollY;
      header.classList.toggle('is-hidden', scrollingDown);
    
      lastScrollY = currentScrollY;
    }, { passive: true });
  5. 5

    تقليص حجم الرأس عند التمرير بـ CSS

    نمط مكمّل: تقليص الـ padding للرأس (وحجم الشعار اختياراً) حين يمرر المستخدم للأسفل، لاستعادة بعض البكسلات مع إبقاء الرأس مرئياً. بدلاً من إخفائه كلياً، بدّل كلاس .is-scrolled.

    css
    .site-header {
      position: sticky;
      top: 0;
      z-index: 50;
      padding-block: 1.25rem;
      transition: padding 0.3s ease, box-shadow 0.3s ease;
    }
    
    .site-header.is-scrolled {
      padding-block: 0.5rem;
      box-shadow: 0 2px 12px rgb(0 0 0 / .1);
    }
  6. 6

    استكشاف Scroll-Driven Animations

    المتصفحات الحديثة تدعم scroll-driven animations — طريقة CSS خالصة للتحريك بناءً على موضع التمرير، بدون JavaScript. المثال أدناه يقلّص لون خلفية الرأس تدريجياً مع تمرير المستخدم، باستخدام animation-timeline: scroll(). دعم المتصفحات: Chromium 115+ وFirefox 110+ — استخدمه كتحسين تدريجي.

    css
    @supports (animation-timeline: scroll()) {
      .site-header {
        animation: shrink-header linear both;
        animation-timeline: scroll();
        animation-range: 0px 80px;
      }
    
      @keyframes shrink-header {
        from { padding-block: 1.25rem; background: transparent; }
        to   { padding-block: 0.5rem;  background: #fff; box-shadow: 0 1px 8px rgb(0 0 0 / .08); }
      }
    }
  7. 7

    إمكانية الوصول — إبقاء التركيز في مكانه

    حين يختفي الرأس، تظل العناصر التفاعلية بداخله (روابط التنقل، زر البحث) مخفية لكن تبقى في ترتيب التبويب. أضف inert للرأس حين يكون مخفياً، وأزله حين يعود للظهور. هذا يُزيله من تركيز التبويب وإعلانات قارئ الشاشة دون إزالته من DOM.

    javascript
    window.addEventListener('scroll', () => {
      const currentScrollY = window.scrollY;
      if (currentScrollY < THRESHOLD) {
        header.classList.remove('is-hidden');
        header.removeAttribute('inert');
        lastScrollY = currentScrollY;
        return;
      }
    
      const scrollingDown = currentScrollY > lastScrollY;
      header.classList.toggle('is-hidden', scrollingDown);
      header.toggleAttribute('inert', scrollingDown);
    
      lastScrollY = currentScrollY;
    }, { passive: true });

نصائح ومحاذير

  • اجعل مستمع التمرير دائماً <code>{ passive: true }</code> — هذا يُخبر المتصفح أن المعالج لن يستدعي <code>preventDefault()</code> أبداً، مما يُتيح تحسينات أداء التمرير.
  • إذا كانت صفحتك تحتوي روابط مرساة، فإن الرأس الثابت المخفي سيتداخل مع الهدف. عوّض ذلك بـ <code>scroll-margin-top</code> على العناصر المرتبطة: <code>:target { scroll-margin-top: 80px; }</code>.
  • للقوائم المفتوحة داخل الرأس (القوائم المنسدلة، التنقل على الجوال)، أوقف مؤقتاً منطق الإخفاء عند التمرير لأسفل أثناء فتح القائمة — وإلا قد يختفي الرأس في منتصف التفاعل.
  • <code>position: sticky</code> يتطلب ألّا يملك الأب <code>overflow: hidden</code> أو <code>overflow: auto</code> — هذا يقطع خاصية الالتصاق.

خاتمة

رأس الصفحة الثابت المبني جيداً هو عقد من جزأين: CSS يتولى الحركة (سلسة، معتمدة على GPU)، وJavaScript يتولى قرار الحالة فقط (إخفاء أم إظهار). ابقِ معالج التمرير خفيفاً وسلبياً، أضف الحد لتجنّب الوميض قرب الأعلى، وتعامل مع السمة inert للحفاظ على إمكانية الوصول. ستُحل الـ scroll-driven animations API محل نهج JavaScript بالكامل في نهاية المطاف — ابدأ بنمط CSS-first الآن.

#CSS #JavaScript #UX
العودة إلى جميع الأدلة

هل تحتاج مساعدة في مشروعك؟

احجز استشارة مجانية لمدة 30 دقيقة لمناقشة تحدياتك التقنية واستكشاف الحلول معًا.