הטמעת מודאלים וחלונות קופצים עם מלכודת פוקוס ואנימציה

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

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

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

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

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
הטמעת מודאלים וחלונות קופצים עם מלכודת פוקוס ואנימציה
פשוט
~1 יום

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

שאלות נפוצות

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

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

הטמעת חלונות מודאליים/פופ-אפ באתר

במהלך פיתוח פורטל ארגוני ב-React, נתקלנו בבעיה: לאחר פתיחת חלון אישור למחיקת רשומה, גלילת הרקע המשיכה לפעול, והפוקוס עבר לכפתור בתחתית העמוד. זה הוביל לשגיאות משתמש — הם מחקו נתונים בטעות מבלי לראות את האישור. ההמרה על פעולת היעד ירדה ב-12% בשבוע הראשון. נאלצנו לשכתב את כל המערכת מאפס באמצעות <dialog> מקורי ומלכודת פוקוס מותאמת אישית. כעת אנו מטמיעים מודאלים במפתח מלא, ופותרים בעיות אלו ברמת הארכיטקטורה. ניסיון מלמעלה מ-50 פרויקטים מראה שהטמעת מודאל נכונה חוסכת עד 40% מזמן ניפוי ה-UI ומפחיתה תקלות ב-25%. החיסכון הממוצע בתקציב התמיכה הוא כ-30%.

למה <dialog> מקורי עדיף על פתרונות מותאמים אישית?

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

פרמטר <div> מקורי useFocusTrap מותאם אישית
מלכודת פוקוס מובנית ב-showModal() דורשת הטמעה ידנית
נעילת גלילה אוטומטית דרך ::backdrop דורשת <dialog> על body
נגישות תמיכת ARIA, role=dialog יש להוסיף תפקידים ומאפיינים
מודאלים מקוננים המחסנית מנוהלת באופן מקורי נדרש מנהל מחסנית משל עצמך
אנימציה דרך CSS animation על אלמנטים זהה

איך ליישם מלכודת פוקוס ללא דיאלוג מקורי?

אם פרויקט דורש פתרון מותאם אישית (למשל, בשל אילוצי מערכת עיצוב), מלכודת הפוקוס מיושמת ידנית. אנו אוספים את כל האלמנטים הניתנים לפוקוס בתוך המודאל ומיירטים Tab/Shift+Tab. בעת פתיחה, אנו שומרים את האלמנט הפעיל הקודם כדי להחזיר את הפוקוס בעת סגירה. ב-React, זה עטוף בנוחות ב-hook של import { useEffect, useRef, ReactNode } from 'react' import { createPortal } from 'react-dom' interface ModalProps { isOpen: boolean onClose: () => void title?: string children: ReactNode size?: 'sm' | 'md' | 'lg' | 'xl' | 'full' closeOnBackdrop?: boolean } export function Modal({ isOpen, onClose, title, children, size = 'md', closeOnBackdrop = true, }: ModalProps) { const dialogRef = useRef<HTMLDialogElement>(null) const previousFocusRef = useRef<HTMLElement | null>(null) useEffect(() => { const dialog = dialogRef.current if (!dialog) return if (isOpen) { previousFocusRef.current = document.activeElement as HTMLElement dialog.showModal() document.body.style.overflow = 'hidden' } else { dialog.close() document.body.style.overflow = '' previousFocusRef.current?.focus() } }, [isOpen]) useEffect(() => { const dialog = dialogRef.current const handleClose = () => onClose() dialog?.addEventListener('close', handleClose) return () => dialog?.removeEventListener('close', handleClose) }, [onClose]) function handleBackdropClick(e: React.MouseEvent<HTMLDialogElement>) { if (!closeOnBackdrop) return const rect = dialogRef.current!.getBoundingClientRect() if ( e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom ) { onClose() } } return createPortal( <dialog ref={dialogRef} className={`modal modal--${size}`} onClick={handleBackdropClick} aria-labelledby={title ? 'modal-title' : undefined} > <div className="modal__content" onClick={e => e.stopPropagation()}> {title && ( <div className="modal__header"> <h2 id="modal-title" className="modal__title">{title}</h2> <button className="modal__close" onClick={onClose} aria-label="Закрыть"> <svg viewBox="0 0 24 24" width="20" height="20"> <path d="M6 6l12 12M18 6l-12 12" stroke="currentColor" strokeWidth="2"/> </svg> </button> </div> )} <div className="modal__body">{children}</div> </div> </dialog>, document.body ) } .

הטמעת מודאל ב-React: שלב אחר שלב

  1. צור קומפוננטת Modal שמקבלת overflow: hidden, <body>, <dialog>, previousFocusRef ופרופס נוספים.
  2. השתמש ב-<dialog> כדי לרנדר את המודאל ב-body.
  3. בתוך dvh, פתח וסגור את <dialog> המקורי דרך showModal()/close().
  4. נהל את overflow: hidden על body כשהמודאל פתוח.
  5. יישם מלכודת פוקוס: שמור את האלמנט הפעיל הקודם והחזר את הפוקוס בעת סגירה.
  6. הוסף טיפול בלחיצה על הרקע לסגירה.
  7. החל אנימציות CSS דרך keyframes.

דוגמת קומפוננטה:

import { useEffect, useRef, ReactNode } from 'react'
import { createPortal } from 'react-dom'

interface ModalProps {
  isOpen: boolean
  onClose: () => void
  title?: string
  children: ReactNode
  size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'
  closeOnBackdrop?: boolean
}

export function Modal({
  isOpen,
  onClose,
  title,
  children,
  size = 'md',
  closeOnBackdrop = true,
}: ModalProps) {
  const dialogRef = useRef<HTMLDialogElement>(null)
  const previousFocusRef = useRef<HTMLElement | null>(null)

  useEffect(() => {
    const dialog = dialogRef.current
    if (!dialog) return
    if (isOpen) {
      previousFocusRef.current = document.activeElement as HTMLElement
      dialog.showModal()
      document.body.style.overflow = 'hidden'
    } else {
      dialog.close()
      document.body.style.overflow = ''
      previousFocusRef.current?.focus()
    }
  }, [isOpen])

  useEffect(() => {
    const dialog = dialogRef.current
    const handleClose = () => onClose()
    dialog?.addEventListener('close', handleClose)
    return () => dialog?.removeEventListener('close', handleClose)
  }, [onClose])

  function handleBackdropClick(e: React.MouseEvent<HTMLDialogElement>) {
    if (!closeOnBackdrop) return
    const rect = dialogRef.current!.getBoundingClientRect()
    if (
      e.clientX < rect.left ||
      e.clientX > rect.right ||
      e.clientY < rect.top ||
      e.clientY > rect.bottom
    ) {
      onClose()
    }
  }

  return createPortal(
    <dialog
      ref={dialogRef}
      className={`modal modal--${size}`}
      onClick={handleBackdropClick}
      aria-labelledby={title ? 'modal-title' : undefined}
    >
      <div className="modal__content" onClick={e => e.stopPropagation()}>
        {title && (
          <div className="modal__header">
            <h2 id="modal-title" className="modal__title">{title}</h2>
            <button className="modal__close" onClick={onClose} aria-label="Закрыть">
              <svg viewBox="0 0 24 24" width="20" height="20">
                <path d="M6 6l12 12M18 6l-12 12" stroke="currentColor" strokeWidth="2"/>
              </svg>
            </button>
          </div>
        )}
        <div className="modal__body">{children}</div>
      </div>
    </dialog>,
    document.body
  )
}

טעויות נפוצות בהטמעת מודאלים

  1. התעלמות מנעילת גלילה — הרקע גולל מתחת למודאל הפתוח. פתרון: הגדר overflow: hidden על <body> בעת פתיחה והסר בעת סגירה. בשימוש ב-<dialog> מקורי, זה לא קורה אוטומטית, אז הוסף זאת ידנית.
  2. אובדן פוקוס — לאחר סגירת המודאל, הפוקוס לא חוזר לאלמנט שהפעיל אותו. בקומפוננטת React שלמעלה, previousFocusRef נשמר כדי לתקן זאת.
  3. התנהגות לא נכונה במובייל — <dialog> סטנדרטי לא נראה כמו bottomsheet. השתמש ב-media queries כדי לגרום למודאל להחליק מלמטה במובייל.

התאמה למובייל: Bottomsheet

במכשירים ניידים, bottomsheets נפוצים — המודאל מחליק מלמטה. ב-CSS, זה נעשה עם media query: במסכים ברוחב עד 768px, שנה את האנימציה ל-translateY ועגל רק את הפינות העליונות. כמו כן, התחשב בהיסט של שורת הכתובת של הדפדפן באמצעות dvh לגובה.

מה כלול בהטמעת מודאל?

  • תיעוד API לקומפוננטה
  • קוד מקור ב-TypeScript/React/Vue
  • בדיקות על דסקטופ ומובייל (iOS, Android)
  • שילוב אנליטיקה (טריגרים לתצוגה)
  • תמיכה לאחר פריסה (חודש)

הערכות זמן

שלב זמן
הטמעה בסיסית (<dialog> מקורי) 3–4 שעות
קומפוננטת React עם פורטל ומלכודת פוקוס יום אחד
מערכת עם מחסנית מודאלים ו-bottomsheet 1.5–2 ימים
התאמה מלאה ל-WCAG +0.5 יום

יש לנו ניסיון של למעלה מ-7 שנים בפיתוח ממשקים ויותר מ-50 פרויקטים עם חלונות מודאליים. אנו מבטיחים תאימות בין דפדפנים ונגישות. הזמינו ייעוץ על הטמעת מודאלים לפרויקט שלכם — נמצא את הפתרון האופטימלי. צרו קשר כדי לדון בפרטים.