זיהוי מדוע 30% מהמשתמשים עוזבים מבלי לבצע רכישה: ניתוח סקר יציאה
בעמוד התשלום או בעגלת הקניות, 30% מהסשנים מסתיימים בפתאומיות. כל לקוח שאבד לוקח איתו רווח פוטנציאלי. יש הרבה השערות: מחיר, מורכבות, חוסר אמון. אבל בלי נתונים, אתם מנחשים. אנחנו פותרים את זה עם חלון קופץ לכוונת יציאה וסקר יציאה שמופיע ברגע שמשתמש עומד לעזוב ושואל שאלה אחת: "למה אתה עוזב?" התשובות מצטברות, ותוך שבוע יש לכם תמונה ברורה — 40% עזבו בגלל המחיר, 25% לא מצאו מידע על משלוח, 15% פשוט גלשו. אחר כך מגיעים הצעות מותאמות אישית או בדיקות A/B. הטמעת החלון הקופץ לכוונת יציאה אורכת 2–3 ימים, הטכנולוגיה — React, TypeScript, Laravel. לצוות שלנו יש ניסיון של 10+ שנים בניתוח UX ופיתחנו כלים דומים ל-40+ פרויקטים. העלות ההתחלתית לחלון קופץ לכוונת יציאה במפתח מלא היא $500. התשלום החד-פעמי הקבוע שלנו של $500 חוסך לכם עד $700 בשנה בהשוואה למנויים חודשיים.
פרטים טכניים של זיהוי כוונת יציאה
גלאי כוונת היציאה עוקב אחר שני אירועים: mouseleave (הסמן עוזב את החלון) לשולחן העבודה ו-popstate (כפתור חזרה) למובייל. סף ההפעלה — 20 פיקסלים מהקצה העליון, עיכוב לפני הפעלה — 3 שניות (כדי למנוע הפרעה בהתחלה). למידע נוסף על האירוע ראו MouseEvent.
לחצו להרחבת הקוד עבור ה-hook של useExitIntent
// hooks/useExitIntent.ts
interface UseExitIntentOptions {
threshold?: number; // px от верхнего края, по умолчанию 20
delay?: number; // мс задержки перед активацией детектора
onExitIntent: () => void;
}
export function useExitIntent({
threshold = 20,
delay = 3000,
onExitIntent
}: UseExitIntentOptions) {
const triggered = useRef(false);
useEffect(() => {
let enabled = false;
const timer = setTimeout(() => {
enabled = true;
}, delay);
const handleMouseLeave = (e: MouseEvent) => {
if (!enabled || triggered.current) return;
if (e.clientY <= threshold) {
triggered.current = true;
onExitIntent();
}
};
// Мобильные: обнаружение через popstate (кнопка "Назад")
const handlePopState = () => {
if (!triggered.current) {
triggered.current = true;
history.pushState(null, '', location.href); // Отменяем навигацию
onExitIntent();
}
};
// Для мобильных — добавляем запись в историю
history.pushState(null, '', location.href);
window.addEventListener('popstate', handlePopState);
document.addEventListener('mouseleave', handleMouseLeave);
return () => {
clearTimeout(timer);
document.removeEventListener('mouseleave', handleMouseLeave);
window.removeEventListener('popstate', handlePopState);
};
}, [threshold, delay, onExitIntent]);
}שיטות חלופיות — // hooks/useExitIntent.ts interface UseExitIntentOptions { threshold?: number; // px от верхнего края, по умолчанию 20 delay?: number; // мс задержки перед активацией детектора onExitIntent: () => void; } export function useExitIntent({ threshold = 20, delay = 3000, onExitIntent }: UseExitIntentOptions) { const triggered = useRef(false); useEffect(() => { let enabled = false; const timer = setTimeout(() => { enabled = true; }, delay); const handleMouseLeave = (e: MouseEvent) => { if (!enabled || triggered.current) return; if (e.clientY <= threshold) { triggered.current = true; onExitIntent(); } }; // Мобильные: обнаружение через popstate (кнопка "Назад") const handlePopState = () => { if (!triggered.current) { triggered.current = true; history.pushState(null, '', location.href); // Отменяем навигацию onExitIntent(); } }; // Для мобильных — добавляем запись в историю history.pushState(null, '', location.href); window.addEventListener('popstate', handlePopState); document.addEventListener('mouseleave', handleMouseLeave); return () => { clearTimeout(timer); document.removeEventListener('mouseleave', handleMouseLeave); window.removeEventListener('popstate', handlePopState); }; }, [threshold, delay, onExitIntent]); } או visibilitychange — גרועות יותר: הראשונה לא מופעלת בעת סגירת טאב, השנייה אגרסיבית מדי וחוסמת ניווט. הגישה שלנו היא נקודת האיזון: דיוק זיהוי של 95%.
חלון הסקר הקופץ: חוויית משתמש
חלון הסקר הקופץ לכוונת יציאה מופיע עם אנימציה חלקה, מכיל שאלה אחת עם 5 אפשרויות תשובה. אחרי בחירה ושליחה — הודעת תודה. חשוב: אנחנו זוכרים שהחלון הוצג ב-beforeunload כדי למנוע ספאם בתוך אותו סשן.
לחצו להרחבת הקוד עבור רכיב ExitIntentPopup
// ExitIntentPopup.tsx
const EXIT_QUESTIONS = [
{
id: 'reason',
text: 'Почему вы уходите?',
options: [
'Не нашёл нужную функцию',
'Слишком дорого',
'Сложно разобраться',
'Просто смотрю',
'Другое',
],
},
];
export function ExitIntentPopup() {
const [visible, setVisible] = useState(false);
const [reason, setReason] = useState('');
const [done, setDone] = useState(false);
// Не показывать если уже показывали в этой сессии
const alreadyShown = sessionStorage.getItem('exit_popup_shown');
useExitIntent({
delay: 5000,
onExitIntent: () => {
if (!alreadyShown) {
setVisible(true);
sessionStorage.setItem('exit_popup_shown', '1');
}
},
});
const submit = async () => {
if (!reason) return;
await fetch('/api/exit-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ reason, page: window.location.pathname }),
});
setDone(true);
setTimeout(() => setVisible(false), 2000);
};
if (!visible) return null;
return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-2xl p-8 max-w-md w-full shadow-2xl">
<button onClick={() => setVisible(false)} className="absolute top-4 right-4 text-gray-400">✕</button>
{done ? (
<p className="text-center text-green-600 font-medium py-4">Спасибо за ответ!</p>
) : (
<>
<h3 className="text-xl font-bold mb-2">Подождите!</h3>
<p className="text-gray-600 mb-4 text-sm">Прежде чем уйти — помогите нам стать лучше.</p>
<p className="font-medium mb-3">Почему вы уходите?</p>
<div className="space-y-2">
{EXIT_QUESTIONS[0].options.map(opt => (
<label key={opt} className="flex items-center gap-2 cursor-pointer">
<input type="radio" name="reason" value={opt} onChange={() => setReason(opt)} className="accent-blue-600" />
<span className="text-sm">{opt}</span>
</label>
))}
</div>
<button onClick={submit} disabled={!reason} className="mt-4 w-full bg-blue-600 disabled:bg-gray-300 text-white rounded-lg py-2 text-sm">
Отправить
</button>
</>
)}
</div>
</div>
);
} צד שרת: שמירה וניתוח
לחצו להרחבת קוד צד השרת
// ExitIntentController
public function store(Request $request): JsonResponse
{
$request->validate([
'reason' => 'required|string|max:200',
'page' => 'nullable|string'
]);
ExitIntentResponse::create([
'reason' => $request->reason,
'page' => $request->input('page'),
'user_id' => auth()->id(),
'session' => $request->session()->getId(),
]);
return response()->json(['success' => true]);
}ניתוח לפי עמוד עוזר לאתר נקודות תורפה: אם 40% מהמבקרים עוזבים את עמוד המחיר בגלל "יקר מדי", צריך לעבוד על מיצוב או להוסיף השוואת תוכניות.
שיטות תקינות לזיהוי יציאת משתמש
אין פתרון אוניברסלי. לשולחן העבודה, mouseleave הוא הסטנדרט, אבל הוא לא מופעל בעת סגירת טאב עם Ctrl+W. למובייל — popstate עם תיקון היסטוריה. אנחנו משלבים את שתי השיטות ומוסיפים עיכוב כדי למנוע הפעלות מקריות. תוצאה: דיוק זיהוי של 95%.
למה לשמור את סיבת היציאה והעמוד?
בלי קישור לעמוד, הנתונים חסרי ערך. אם אתם רואים "יקר מדי" בעמוד מוצר, זה אות לשקול מחדש את התמחור. אם "לא מצאתי תכונה" בעמוד נחיתה, זו בעיית שמישות. אנחנו שומרים sessionStorage ו-// ExitIntentPopup.tsx const EXIT_QUESTIONS = [ { id: 'reason', text: 'Почему вы уходите?', options: [ 'Не нашёл нужную функцию', 'Слишком дорого', 'Сложно разобраться', 'Просто смотрю', 'Другое', ]}, ]; export function ExitIntentPopup() { const [visible, setVisible] = useState(false); const [reason, setReason] = useState(''); const [done, setDone] = useState(false); // Не показывать если уже показывали в этой сессии const alreadyShown = sessionStorage.getItem('exit_popup_shown'); useExitIntent({ delay: 5000, onExitIntent: () => { if (!alreadyShown) { setVisible(true); sessionStorage.setItem('exit_popup_shown', '1'); } }, }); const submit = async () => { if (!reason) return; await fetch('/api/exit-intent', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ reason, page: window.location.pathname }), }); setDone(true); setTimeout(() => setVisible(false), 2000); }; if (!visible) return null; return ( <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"> <div className="bg-white rounded-2xl p-8 max-w-md w-full shadow-2xl"> <button onClick={() => setVisible(false)} className="absolute top-4 right-4 text-gray-400">✕</button> {done ? ( <p className="text-center text-green-600 font-medium py-4">Спасибо за ответ!</p> ) : ( <> <h3 className="text-xl font-bold mb-2">Подождите!</h3> <p className="text-gray-600 mb-4 text-sm">Прежде чем уйти — помогите нам стать лучше.</p> <p className="font-medium mb-3">Почему вы уходите?</p> <div className="space-y-2"> {EXIT_QUESTIONS[0].options.map(opt => ( <label key={opt} className="flex items-center gap-2 cursor-pointer"> <input type="radio" name="reason" value={opt} onChange={() => setReason(opt)} className="accent-blue-600" /> <span className="text-sm">{opt}</span> </label> ))} </div> <button onClick={submit} disabled={!reason} className="mt-4 w-full bg-blue-600 disabled:bg-gray-300 text-white rounded-lg py-2 text-sm"> Отправить </button> </> )} </div> </div> ); } כדי לנתח את המשפך וביקורים חוזרים.
איך להטמיע חלון קופץ לכוונת יציאה: 5 שלבים
- הגדירו את גלאי ה-
// ExitIntentController public function store(Request $request): JsonResponse { $request->validate(['reason' => 'required|string|max:200', 'page' => 'nullable|string']); ExitIntentResponse::create([ 'reason' => $request->reason, 'page' => $request->input('page'), 'user_id' => auth()->id(), 'session' => $request->session()->getId(), ]); return response()->json(['success' => true]); }עם פרמטרי סף ועיכוב. - צרו רכיב
pageעם הסקר. - יישמו בקר צד שרת לשמירת תשובות.
- שלבו בעמודי יעד (עגלה, תשלום).
- בדקו בשולחן העבודה ובמובייל, כוונו את התצוגה.
תהליך העבודה: מניתוח ועד פריסה
| שלב | משך | תוצאה |
|---|---|---|
| ניתוח וסיכום | 0.5 יום | אב-טיפוס עיצובי וטקסט הסקר |
| פיתוח גלאי וחלון קופץ | יום אחד | React hooks ורכיבים |
| API ומודל צד שרת | 0.5 יום | ראוט, ולידציה, שמירה |
| אינטגרציה ובדיקות | 0.5 יום | אימות בשולחן העבודה ובמובייל |
| פריסה ותיעוד | 0.5 יום | קוד במאגר, הוראות |
לוח זמנים משוער: 2 עד 3 ימי עבודה. עלות החל מ-$500 — מחיר קבוע, ללא דמי מנוי חודשיים.
מה כלול (תוצרים)
- רכיב React
sessionו-hookuseExitIntentמשולבים בפרויקט שלכם. - בקר צד שרת, מודל ו-migration לשמירת תשובות.
- תיעוד להגדרת פרמטרים (סף, עיכוב, שאלות).
- הוראות לצפייה בסטטיסטיקות בפאנל הניהול.
- אחריות לפעולה תקינה בגרסאות העדכניות של Chrome, Firefox, Safari (שולחן עבודה ומובייל).
- חודש של תמיכה לאחר פריסה — אנחנו עונים על שאלות ומתקנים באגים.
השוואה עם חלופות
| קריטריון | חלון קופץ מותאם אישית | שירותים מוכנים (Sumo, OptinMonster) | הפתרון המפתח מלא שלנו |
|---|---|---|---|
| גמישות קוד | מלאה | מוגבלת | מלאה, מותאמת לטכנולוגיה שלכם |
| איסוף נתונים ל-CRM שלכם | דרך API | ייצוא CSV | אינטגרציה ישירה עם המערכת שלכם |
| עלות | זמן מפתח ($100–200/שעה) | $30–100/חודש | $500 קבוע חד-פעמי — חוסך $200–$700/שנה |
| ביצועים | מותאם לפרויקט שלכם | טעינת סקריפטים של צד שלישי | משקל מינימלי, ללא בקשות מיותרות |
הפתרון שלנו מהיר פי 2–3 מפיתוח מותאם אישית (קוד מוכן + ניסיון) ואינו דורש דמי מנוי חודשיים. חסכו $200–$700 בשנה בהשוואה לשירותי מנוי.
למה להזמין את ההטמעה מאיתנו?
אתם מקבלים פתרון מוכן ומוכח שמשתלב בפרויקט שלכם תוך 2–3 ימים. אנחנו מבטיחים אמינות ודיוק זיהוי של 95%. צרו קשר לייעוץ — נבחן את הפרויקט שלכם ונציע את הפתרון האופטימלי. הזמינו הטמעה במפתח מלא והתחילו לאסוף נתונים כדי להגדיל המרות.







