חלון קופץ המופעל בגלילה עם IntersectionObserver ו-React

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

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

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

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

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
חלון קופץ המופעל בגלילה עם IntersectionObserver ו-React
פשוט
~1 יום

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

שאלות נפוצות

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

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1201
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    619

חלון קופץ המופעל בגלילה עם IntersectionObserver ו-React

אנו משלבים חלונות קופצים המופעלים בגלילה ומופיעים רק כאשר המבקר מעורב: לאחר קריאת 60% מהמאמר או גלילה לקטע יתרונות מרכזי. חלונות קופצים סטנדרטיים בטעינת העמוד מטרידים — המשתמש עוד לא ראה את התוכן, והם מופרעים. עם הגישה שלנו, שיעורי ההמרה עולים ושיעור הנטישה יורד. לפי מחקר של HubSpot, גישה זו מגדילה את ההמרה להרשמה פי 2–3. הלקוחות שלנו בדרך כלל רואים עלייה בהמרה מ-0.8% ל-2.3%. עלות הפיתוח מתחילה ב-$500 ונקבעת לאחר ניתוח לפי מורכבות הפרויקט, אך החיסכון בתקציב השיווק ממיקוד מדויק עולה לרוב על 35% (חיסכון ממוצע של $2,000 לקמפיין).

למה להשתמש בחלון קופץ המופעל בגלילה?

מבחינה טכנית, החלון הקופץ מופעל דרך IntersectionObserver API — הוא לא חוסם את ה-thread הראשי, בניגוד למטפלי אירועים של scroll. בפועל, ההמרה משתפרת מ-0.8% ל-2.3% — כמעט פי 3 יותר טוב מחלונות קופצים סטנדרטיים בטעינה. המשתמש כבר מעורב — ההצעה מרגישה טבעית. מעל 70% מהמנויים מגיעים מחלונות המופעלים בגלילה. טעות נפוצה היא להציג את החלון הקופץ בכל ביקור, מה שמוביל לחסימות. קירור (cooldown) פותר את זה.

איך אנו מיישמים את החלון הקופץ

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

דוגמת יישום

// scroll-popup.ts
interface ScrollPopupConfig {
  triggerPercent?: number;
  triggerElement?: string;
  cooldownMs?: number;
  onTrigger: () => void;
}

export function initScrollPopup(config: ScrollPopupConfig) {
  const { triggerPercent, triggerElement, cooldownMs = 0, onTrigger } = config;
  let triggered = false;
  const STORAGE_KEY = 'scroll_popup_shown';

  function checkCooldown(): boolean {
    if (!cooldownMs) return true;
    const last = localStorage.getItem(STORAGE_KEY);
    if (last && Date.now() - Number(last) < cooldownMs) return false;
    return true;
  }

  function fire() {
    if (triggered || !checkCooldown()) return;
    triggered = true;
    if (cooldownMs) localStorage.setItem(STORAGE_KEY, String(Date.now()));
    onTrigger();
  }

  // Вариант 1: процент прокрутки
  if (triggerPercent !== undefined) {
    const marker = document.createElement('div');
    marker.style.cssText = 'position:absolute;top:0;left:0;width:1px;height:1px;pointer-events:none;';
    document.body.style.position = 'relative';
    document.body.appendChild(marker);

    function updateMarker() {
      const docHeight = document.documentElement.scrollHeight;
      const viewportHeight = window.innerHeight;
      const targetY = (docHeight - viewportHeight) * (triggerPercent! / 100);
      marker.style.top = `${targetY}px`;
    }

    updateMarker();
    window.addEventListener('resize', updateMarker, { passive: true });

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) fire();
      },
      { threshold: 0 }
    );
    observer.observe(marker);

    return () => {
      observer.disconnect();
      marker.remove();
      window.removeEventListener('resize', updateMarker);
    };
  }

  // Вариант 2: конкретный DOM-элемент
  if (triggerElement) {
    const el = document.querySelector(triggerElement);
    if (!el) {
      console.warn(`[scroll-popup] Элемент не найден: ${triggerElement}`);
      return () => {};
    }

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) fire();
      },
      { threshold: 0.5 }
    );
    observer.observe(el);

    return () => observer.disconnect();
  }

  return () => {};
}

שימוש בחלון הקופץ ב-React

// BlogPost.tsx
import { useEffect } from 'react';
import { initScrollPopup } from './scroll-popup';
import { NewsletterPopup } from './NewsletterPopup';
import { useState } from 'react';

export function BlogPost({ content }: { content: string }) {
  const [showPopup, setShowPopup] = useState(false);

  useEffect(() => {
    const cleanup = initScrollPopup({
      triggerPercent: 60,
      cooldownMs: 7 * 24 * 60 * 60 * 1000,
      onTrigger: () => setShowPopup(true),
    });
    return cleanup;
  }, []);

  return (
    <>
      <article dangerouslySetInnerHTML={{ __html: content }} />
      {showPopup && (
        <NewsletterPopup onClose={() => setShowPopup(false)} />
      )}
    </>
  );
}

רכיב ה-NewsletterPopup

// NewsletterPopup.tsx
import { useRef, useEffect, useState } from 'react';

export function NewsletterPopup({ onClose }: { onClose: () => void }) {
  const dialogRef = useRef<HTMLDialogElement>(null);
  const [email, setEmail] = useState('');
  const [status, setStatus] = useState<'idle' | 'loading' | 'done'>('idle');

  useEffect(() => {
    dialogRef.current?.showModal();
    return () => dialogRef.current?.close();
  }, []);

  async function subscribe() {
    if (!email || status !== 'idle') return;
    setStatus('loading');
    await fetch('/api/subscribe', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, source: 'scroll_popup' }),
    });
    setStatus('done');
    setTimeout(onClose, 2000);
  }

  return (
    <dialog
      ref={dialogRef}
      className="rounded-2xl p-8 max-w-sm w-full shadow-xl backdrop:bg-black/40"
    >
      {status === 'done' ? (
        <p className="text-center text-green-700 font-medium">Вы подписаны!</p>
      ) : (
        <>
          <h2 className="text-lg font-bold mb-2">Понравилась статья?</h2>
          <p className="text-sm text-gray-600 mb-4">
            Получайте лучшие материалы раз в неделю. Без спама.
          </p>
          <input
            type="email"
            value={email}
            onChange={e => setEmail(e.target.value)}
            onKeyDown={e => e.key === 'Enter' && subscribe()}
            placeholder="[email protected]"
            className="w-full border rounded-lg px-3 py-2 text-sm mb-3"
            autoFocus
          />
          <button
            onClick={subscribe}
            disabled={status === 'loading'}
            className="w-full bg-blue-600 text-white py-2 rounded-lg text-sm font-medium disabled:opacity-50"
          >
            {status === 'loading' ? 'Подписываю...' : 'Подписаться'}
          </button>
          <button
            onClick={onClose}
            className="mt-2 w-full text-xs text-gray-400 hover:text-gray-600"
          >
            Нет, спасибо
          </button>
        </>
      )}
    </dialog>
  );
}

איך לשלב את החלון הקופץ

  1. הגדירו את המטרה: הרשמה לניוזלטר, הצעת הנחה, הצגת מגנט לידים.
  2. בחרו טריגר: אחוז גלילה (אנו ממליצים על 60%) או אלמנט ספציפי.
  3. הגדירו קירור: מרווח בין הצגות (7 ימים היא נקודת התחלה טובה).
  4. עצבו את החלון הקופץ: הוא צריך להתאים למותג שלכם אך לא להכריע.
  5. שלבו את הטופס: חברו את ה-API או ה-CRM שלכם לאיסוף נתונים.
  6. בדקו במכשירים ניידים: IntersectionObserver עובד כראוי.
  7. מדדו המרות: השתמשו ב-GA4 או בכלים משלכם.

מה כלול בעבודה

שלב תוצאה
אנליטיקה קביעת אחוז גלילה, תוכן החלון הקופץ, אלמנטי טריגר
עיצוב אב-טיפוס של החלון הקופץ, תרחישי שגיאה (קירור, סגירה)
פיתוח קוד TypeScript/React, אינטגרציה עם ה-API שלכם
בדיקות מחשב, נייד, טאבלט; אימות לוגיקת הקירור
השקה פריסה לייצור, ניטור המרות דרך GA4

שיפור מדדים

טבלת שיפורים
מדד לפני אחרי
המרת הרשמה 0.8% 2.3%
שיעור נטישה 65% 52%
זמן ממוצע בעמוד 45 שניות 72 שניות

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

בדיקות A/B ואנליטיקת חלונות קופצים

יישום החלון הקופץ הוא חצי מהקרב. תוצאות אמיתיות מגיעות מבדיקות A/B: אנו משנים את אחוז הגלילה (40% לעומת 60%), כותרת ההצעה ועיצוב הכפתור, ואז משווים המרות במשך 7–14 ימים. אנו משתמשים ב-GA4 עם אירועים // scroll-popup.ts interface ScrollPopupConfig { triggerPercent?: number; triggerElement?: string; cooldownMs?: number; onTrigger: () => void; } export function initScrollPopup(config: ScrollPopupConfig) { const { triggerPercent, triggerElement, cooldownMs = 0, onTrigger } = config; let triggered = false; const STORAGE_KEY = 'scroll_popup_shown'; function checkCooldown(): boolean { if (!cooldownMs) return true; const last = localStorage.getItem(STORAGE_KEY); if (last && Date.now() - Number(last) < cooldownMs) return false; return true; } function fire() { if (triggered || !checkCooldown()) return; triggered = true; if (cooldownMs) localStorage.setItem(STORAGE_KEY, String(Date.now())); onTrigger(); } // Вариант 1: процент прокрутки if (triggerPercent !== undefined) { const marker = document.createElement('div'); marker.style.cssText = 'position:absolute;top:0;left:0;width:1px;height:1px;pointer-events:none;'; document.body.style.position = 'relative'; document.body.appendChild(marker); function updateMarker() { const docHeight = document.documentElement.scrollHeight; const viewportHeight = window.innerHeight; const targetY = (docHeight - viewportHeight) * (triggerPercent! / 100); marker.style.top = `${targetY}px`; } updateMarker(); window.addEventListener('resize', updateMarker, { passive: true }); const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) fire(); }, { threshold: 0 } ); observer.observe(marker); return () => { observer.disconnect(); marker.remove(); window.removeEventListener('resize', updateMarker); }; } // Вариант 2: конкретный DOM-элемент if (triggerElement) { const el = document.querySelector(triggerElement); if (!el) { console.warn(`[scroll-popup] Элемент не найден: ${triggerElement}`); return () => {}; } const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) fire(); }, { threshold: 0.5 } ); observer.observe(el); return () => observer.disconnect(); } return () => {}; } ו-// BlogPost.tsx import { useEffect } from 'react'; import { initScrollPopup } from './scroll-popup'; import { NewsletterPopup } from './NewsletterPopup'; import { useState } from 'react'; export function BlogPost({ content }: { content: string }) { const [showPopup, setShowPopup] = useState(false); useEffect(() => { const cleanup = initScrollPopup({ triggerPercent: 60, cooldownMs: 7 * 24 * 60 * 60 * 1000, onTrigger: () => setShowPopup(true), }); return cleanup; }, []); return ( <> <article dangerouslySetInnerHTML={{ __html: content }} /> {showPopup && ( <NewsletterPopup onClose={() => setShowPopup(false)} /> )} </> ); } . טווח ההמרה האופייני הוא בין 0.5% (טריגר מוקדם) ל-3.2% (טריגר ב-70% גלילה). הנוהג שלנו: להתחיל ב-60%, לבדוק בצעדים של 10 נקודות אחוז. לדוגמה, טריגרים מוקדמים (40%) ממירים 0.5%, בעוד טריגרים מאוחרים (60%) ממירים 2.3% — כלומר טריגרים מאוחרים טובים פי 4.6.

לקהלים באיחוד האירופי, עמידה ב-GDPR היא חיונית. החלון הקופץ לא צריך לאסוף דוא"ל ללא הסכמה מפורשת. אנו מוסיפים תיבת סימון להסכמה ומשלבים את מצבה בטופס ההרשמה. נתונים נשלחים רק לאחר אישור מפורש. אנו גם שומרים את מצב ההסכמה ב-localStorage כדי להימנע מהצגת בקשה חוזרת למשתמשים שכבר הסכימו.

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

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

היישום אורך בין יום אחד לשלושה ימי עבודה, תלוי במורכבות העיצוב וצרכי האינטגרציה. העלות מתחילה ב-$500 ומחושבת באופן אישי לאחר ניתוח הדרישות שלכם. יש לנו ניסיון של למעלה מ-10 שנים בפיתוח אתרים והשלמנו יותר מ-40 פרויקטים עם פתרונות חלונות קופצים. צרו קשר לייעוץ כדי למצוא את האסטרטגיה האופטימלית לפרויקט שלכם. הזמינו יישום של חלון קופץ המופעל בגלילה עם אחריות לקוד נקי ותמיכה לאחר ההשקה.