اذهب إلى المحتوى

التعامل مع نوافذ المتصفح والنوافذ المنبثقة Popups في جافاسكربت


ابراهيم الخضور

تستخدم الأساليب القديمة في JavaScript النوافذ المنبثقة لإظهار صفحات أو مستندات أخرى للمستخدم، إذ يمكن فتح نافذة جديدة تعرض المورد المرتبط بالعنوان المحدَّد بسهولة عن طريق تنفيذ الأمر التالي:

window.open('https://javascript.info/')

تعرض معظم المتصفحات الحديثة محتويات الموارد الموجودة على عنوان محدد في نوافذ متجاورة، بدلًا من فتح نافذة جديدة لكل منها.

إنّ فكرة النوافذ المنبثقة قديمة، وقد استخدمت لعرض محتوىً مختلف للمستخدم دون إغلاق النافذة الرئيسية، لكن حاليًا تستخدَم أساليب أخرى، حيث يمكن تحميل المحتوى ديناميكيًا من خلال التابع fetch ثم إظهاره ضمن معرَّف <div> يُولَّد ديناميكيًا، لذا لم يعد استخدام النوافذ المنبثقة شائعًا. كما أن استخدامها في الأجهزة النقالة التي لا تعرض عدة نوافذ معًا مربك أحيانًا.

مع ذلك يبقى للنوافذ المنبثقة استخداماتها في إنجاز بعض المهام كالاستيثاق "OAuth"، المستخدم لتسجيل الدخول على Google أو Facebook وغيرها، لأنّ:

  1. النوافذ المنبثقة هي نوافذ منفصلة لها بيئة JavaScript مستقلة، وبالتالي سيكون فتح نافذة منبثقة مصدرها طرف ثالث أو موقع غير موثوق آمنًا.
  2. من السهل جدًا فتح نافذة منبثقة.
  3. يمكن للنوافذ المنبثقة الانتقال إلى عنوان آخر، وإرسال رسالة إلى النافذة الأصلية.

منع ظهور النوافذ المنبثقة

أساءت المواقع المشبوهة استخدام النوافذ المنبثقة سابقًا، حيث أمكنها فتح آلاف النوافذ المنبثقة التي تحمل إعلانات أو منشورات دعائية، لذلك تحاول معظم المتصفحات الحديثة حاليًا منع ظهور هذه النوافذ لحماية المستخدم.

تحجب معظم المتصفحات النوافذ المنبثقة إذا استُدعيت خارج شيفرة معالجات الأحداث event handler التي تستجيب لأفعال المستخدِم مثل onClick:

// تُحجب النافذة المنبثقة
window.open('https://javascript.info');

// يُسمح بظهور النافذة المنبثقة
button.onclick = () => {
  window.open('https://javascript.info');
};

وهكذا ستتمكن المتصفحات من حماية المستخدم نوعًا ما، لكن هذا الأسلوب لن يعطل وظيفة النوافذ المنبثقة كاملةً. لكن ماذا لو فتحنا نافذة منبثقة ضمن شيفرة onClcik، لكن بعد تحديد زمن ظهورها باستخدام التعليمة setTimeOut؟ سيبدو الأمر مربكًا لاختلاف السلوك على متصفحات مختلفة.

جرّب الشيفرة التالية:

// ستظهر النافذة لثلاث ثوان
setTimeout(() => window.open('http://google.com'), 3000);

تظهر النافذة المنبثقة على Chrome وسيحجبها Firefox، لكن لو قللنا زمن ظهورها فستظهر أيضًا على Firefox:

// ستظهر النافذة لثانية
setTimeout(() => window.open('http://google.com'), 1000);

إن سبب الاختلاف السابق هو أنّ Firefox سيقبل أية قيمة لزمن الظهور التي تساوي ثانيتين أو أقل، لكنه سيرفع ثقته عن النوافذ المنبثقة معتبرًا أنها ظهرت رغم إرادة المستخدم إن زاد زمن الظهور، لذلك فقد حُجبت في الحالة الأولى وظهرت في الثانية.

التابع window.open

تُستخدم العبارة البرمجية التالية لفتح نافذة منبثقة:

window.open(url, name, params)

حيث:

  • url: عنوان المورد الذي سيُحمّل ضمن الصفحة.
  • name: اسم النافذة التي ستُفتح، فلكل نافذة اسم window.name، وبذلك يمكننا تحديد النافذة التي سنستخدمها لإظهار المحتويات المنبثقة، فإذا وجدت نافذة بهذا الاسم فستُحمّل محتويات المورد ضمنها، وإلا ستُفتح نافذة جديدة بالاسم المحدد.
  • params: سلسلة نصية تمثل تعليمات تهيئة النافذة الجديدة، وتحتوي على إعدادات تفصل بينها فاصلة، ولا ينبغي وجود فراغات بين الإعدادات، مثل السلسلة width=200,height=100، والمعاملات params التي يمكن ضبطها هي:
  • الموضع position:
  • left/top: قيمة عددية تمثل إحداثيات الزاوية العليا اليسرى للنافذة على شاشة العرض، لكن لا يمكن وضع النافذة خارج شاشة العرض.
    • width/height: تحدد عرض النافذة الجديدة وارتفاعها، وهناك طبعًا حد أدنى لقيمة كل منهما، فلا يمكن إنشاء نافذة غير مرئية.
  • ميزات النافذة Window features:
    • menubar: تأخذ إحدى القيمتين yes/no، ومهمتها إظهار قائمة المتصفح في النافذة الجديدة أو إخفاؤها.
    • toolbar: تأخذ إحدى القيمتين yes/no، ومهمتها إظهار شريط التنقل navigation bar في النافذة الجديدة أو إخفاؤه.
    • location: تأخذ إحدى القيمتين yes/no، ومهمتها إظهار شريط العنوان في النافذة الجديدة أو إخفاؤه، ويجب الانتباه إلى أن المتصفحين Firefox وInternet Explorer لا يسمحان بذلك افتراضيًا.
    • status: تأخذ إحدى القيمتين yes/no، ومهمتها إظهار شريط الحالة status bar أو إخفاؤه، وتفرض معظم المتصفحات إظهاره في النوافذ الجديدة.
    • resizable: تأخذ إحدى القيمتين yes/no، تمنع تغيير حجم النافذة الجديدة، ولا يُنصح بتمكين هذه الميزة.
    • scrollbars: تأخذ إحدى القيمتين yes/no، تمنع ظهور أشرطة التمرير في النافذة الجديدة، ولا يُنصح بتمكين هذه الميزة. توجد أيضًا مجموعة من الميزات الأقل دعمًا كونها متعلقة بمتصفحات محددة ولا تستخدم عادةً، ويمكن الاطلاع عليها عن طريق البحث عن التابع window.open ضمن شبكة مطوري Mozilla

مثال نموذجي لنافذة بأقل ميزات

لنفتح نافذةً بأقل مجموعة ممكنة من الميزات، وذلك لمعرفة تلك التي يسمح المتصفح بتعطيلها:

let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=0,height=0,left=-1000,top=-1000`;

open('/', 'test', params);

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

لنضع الآن النافذة في مكان مناسب ونعطي قيمًا مقبولة للإحداثيات width وheight وleft وtop:

let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=600,height=300,left=100,top=100`;

open('/', 'test', params);

ستُظهر معظم المتصفحات النافذة بالشكل المطلوب.

إليك مجموعة من القواعد عند تجاهل بعض الإعدادات:

  • إذا لم تضع قيمة للوسيط الثالث params عند استدعاء التابع open، فستُستخدم المعاملات الافتراضية للنافذة.
  • إذا وضعت السلسلة النصية التي تصف المعاملات params، لكنك أغفلت بعض الميزات التي تأخذ إحدى القيمتين "yes/no"، فستأخذ هذه الميزات القيمة "no"، وبالتالي إن أردت معاملات محددة، فانتبه إلى التصريح علنًا عن الميزات المطلوبة وإسناد القيمة "yes" لها.
  • إذا لم تحدَّد الخاصية left/top في المعاملات params، فسيحاول المتصفح فتح النافذة الجديدة بجانب آخر نافذة قد فتحتها.
  • إن لم تحدّد الخاصية width/height في المعاملات params، فسيكون حجم النافذة الجديدة مساويًا لحجم آخر نافذة فتحتها.

الوصول إلى النافذة المنبثقة من النافذة الأصل

يعيد استدعاء التابع open مرجعًا إلى النافذة الجديدة، يمكن استخدامه للتحكم بخصائص تلك النافذة، وتغيير موضعها وأشياء أخرى.

سنوّلد في هذا المثال نافذةً منبثقةً ذات محتوىً معين باستخدام JavaScript:

let newWin = window.open("about:blank", "hello", "width=200,height=200");

newWin.document.write("Hello, world!");

سنغيّر الآن محتوى النافذة بعد انتهاء تحميله:

let newWindow = open('/', 'example', 'width=300,height=300')
newWindow.focus();

alert(newWindow.location.href); // (*) about:blank, لم يبدأ التحميل بعد

newWindow.onload = function() {
  let html = `<div style="font-size:30px">Welcome!</div>`;
  newWindow.document.body.insertAdjacentHTML('afterbegin', html);
};

لن يكون محتوى النافذة الجديدة قد حُمِّل بعد استدعاء window.open مباشرةً، وقد وضحنا ذلك باستخدام التنبيه alert في السطر "(*)"، لذا عليك الانتظار حتى يعدل التابع المطلوب، كما يمكننا استخدام معالج الحدث DOMContentLoaded مع الخاصية newWin.document.

اقتباس

سياسة الأصل المشترك same origin policy يمكن أن تصل نافذة ما إلى محتوى أخرى إن اشتركتا بالأصل نفسه -أي لهما نفس بروتوكول النقل والنطاق والمنفذ)- فمثلًا: لن تتمكن نافذة من الموقع "site.com" من الوصول إلى محتوى نافذة منبثقة مصدرها "gmail.com" وذلك لحماية المستخدم، وللاطلاع على تفاصيل أكثر يمكن الرجوع إلى فصل "الاتصال بين النوافذ المختلفة".

الوصول إلى نافذة من نافذة منبثقة

يمكن أن تصل النافذة المنبثقة إلى النافذة التي أنتجتها باستخدام المرجع الذي يعيده التابع window.opener، والذي يحمل القيمة "null" لجميع النوافذ عدا النوافذ المنبثقة.

لاستبدال محتوى النافذة التي أنتجت النافذة المنبثقة بالكلمة "Test" نستخدم الشيفرة التالية :

let newWin = window.open("about:blank", "hello", "width=200,height=200");

newWin.document.write(
  "<script>window.opener.document.body.innerHTML = 'Test'<\/script>"
);

إذًا فالعلاقة بين النوافذ الأصلية والمنبثقة ثنائية الاتجاه، لأن كلّا منهما تحمل مرجعًا إلى الأخرى.

إغلاق نافذة منبثقة

  • نفذ الأمر win.close لإغلاق نافذة.
  • نفذ الأمر win.closed للتحقق من إغلاق النافذة.

يمكن عمليًا لأي نافذة استخدام التابع ()close، لكن معظم المتصفحات ستتجاهله عندما لا تُنشأ النافذة عن طريق التابع ()window.open، وبالتالي سيعمل على النوافذ المنبثقة فقط، وستكون قيمة الخاصية closed هي "true" إن أُغلقت النافذة، ولهذه الخاصية فائدتها في التحقق من إغلاق النوافذ والنوافذ المنبثقة، لذلك ينبغي على الشيفرة أن تأخذ في الحسبان احتمال إغلاق المستخدم للنافذة في أي لحظة.

ستحمِّل الشيفرة التالية نافذة ثم ستغلقها:

let newWindow = open('/', 'example', 'width=300,height=300');

newWindow.onload = function() {
  newWindow.close();
  alert(newWindow.closed); // true
};

تحريك النافذة وتغيير حجمها

توجد عدة خيارات لتحريك نافذة أو تغيير حجمها، هي:

  • (win.moveBy(x,y: يحرّك النافذة "x" بيكسل نحو اليمين و"y" بيكسل نحو الأسفل انطلاقًا من الموقع الحالي، ويمكن استخدام قيمة سالبة للتحرك نحو الأعلى واليسار.
  • (win.moveTo(x,y: لنقل النافذة إلى الموقع المحدد بالإحداثيتين (x,y) ضمن الشاشة.
  • (win.resizeBy(width,heigh: تغيير حجم النافذة بتغيير قيمتي الارتفاع والعرض بالنسبة إلى القيمتين الحاليتين، ويُسمح باستخدام قيم سالبة.
  • (win.resizeTo(width,height: تغيير حجم النافذة إلى الأبعاد المحددة.

يمكن الاستفادة أيضًا من الحدث window.onresize عندما يتغير حجم النافذة.

اقتباس

خاص بالنوافذ المنبثقة: تحجب المتصفحات التوابع السابقة لتمنع استخدامها بشكل غير سليم، وستعمل فقط على النوافذ المنبثقة التي نفتحها، والتي لا تحتوي على صفحات متعددة ضمنها.

لا تكبير أو تصغير للنوافذ: لا يوجد في JavaScript أي وسائل لتكبير النافذة أو تصغيرها، حيث يُمنع مبرمجو الواجهة الأمامية من الوصول إلى دوال كهذه على مستوى نظام التشغيل، كما لا تعمل توابع تحريك النافذة وتغيير حجمها عندما تكون النافذة في وضع التكبير الكامل Maximized أو التصغير الكامل Minimized.

تمرير محتويات نافذة Scrolling a window

تحدثنا سابقًا عن الموضوع في فصل "أحجام النوافذ وقابلية التمرير"، والتوابع التي تنفذ المهمة هي:

  • (win.scrollBy(x,y: يمرر محتويات النافذة "x" بكسل إلى اليمين و"y" بكسل إلى الأسفل، ويسمح التابع باستخدام القيم السالبة.
  • (win.scrollTo(x,y: يمرر محتويات النافذة إلى الموقع الذي إحداثياته (x,y).
  • (elem.scrollIntoView(top = true: يمرر محتويات النافذة ليظهر العنصر elem في أعلى الصفحة، وهي الحالة الافتراضية، أو أسفل الصفحة عندما تكون top=false. كما يمكن الاستفادة من الحدث window.onscroll.

نقل تركيز الدخل إلى نافذة وتغشيتها focus/blur

يمكن نظريًا استخدام التابعين()window.blur و()window.focus للتركيز على نافذة أو رفع التركيز عنها، كما يمكن استخدام الحدث focus/blur الذي يسمح بالتقاط اللحظة التي ينقل فيها المستخدم التركيز إلى نافذة محددة أو ينتقل إلى أخرى. لكن لو انتقلنا إلى التطبيق العملي فسنجد محدوديةً كبيرةً في تطبيقهما، لأنّ الصفحات المشبوهة أساءت استخدامهما. لنتأمل الشيفرة التالية مثلًا:

window.onblur = () => window.focus();

فعندما يحاول المستخدم تبديل النافذة والانتقال إلى أخرى (window.onblur)، ستعيد الشيفرة السابقة التركيز إلى نفس النافذة.

لذلك وضعت المتصفحات قيودًا لمنع استخدام الشيفرة بالطريقة السابقة، ولحماية المستخدم من النوافذ الدعائية والصفحات المشبوهة.

مثلًا: يتجاهل متصفح الهواتف المحمولة التابع ()window.focus بشكل كامل، كما لا يمكن التركيز على نافذة منبثقة ستظهر في صفحة جديدة ضمن النافذة نفسها بدلًا من ظهورها في نافذة جديدة.

ومع ذلك يمكننا الاستفادة من التابعين السابقين في بعض الحالات، فمثلًا:

  • من الجيد تنفيذ الأمر ()newWindow.focusعند فتح نافذة منبثقة جديدة، لضمان وصول المستخدم إلى النافذة الجديدة في بعض المتصفحات العاملة على بعض أنظمة التشغيل.
  • يمكن تعقب تنفيذ الحدثين window.onfocus/onblur إذا أردنا التأكد من أنّ زائري الموقع يستخدمون تطبيقنا أم لا، مما سيسمح بإيقاف أو إعادة استخدام بعض محتويات التطبيق كالرسوم المتحركة وغيرها، مع وجوب الانتباه إلى أنّ الحدث blur سيشير إلى مغادرة الزائر للنافذة المحددة، ومع إمكانية ملاحظة الزائر للنافذة إذا بقيت في خلفية الشاشة.

خلاصة

لا تستخدم النوافذ المنبثقة إلا نادرًا، لوجود بدائل عنها، مثل: تحميل وإظهار المعلومات ضمن الصفحة أو استخدام إطارات iframe.

ويعتبر إبلاغ المستخدم بفتح نافذة منبثقة أسلوبًا جيدًا، إذ يسمح وجود أيقونة "فتح نافذة" بجوار أي رابط أو زر أن يتوقع المستخدم انتقال التركيز من النافذة التي يعمل عليها إلى أخرى، وبالتالي سينتبه إلى كلتيهما.

  • يمكن فتح نافذة منبثقة باستدعاء التابع ( open(url, name, params الذي يعيد مرجعًا إلى النافذة الجديدة.
  • تمنع المتصفحات استدعاء التابع من خلال شيفرة خارج نطاق أفعال المستخدم (شيفرة الاستجابة لأحداث)، كما تُظهر المتصفحات عادة تنبيهًا للمستخدم بهذا الأمر، ويكون الخيار له بالسماح بتنفيذ الشيفرة أم لا.
  • تفتح المتصفحات صفحات جديدة ضمن النافذة نفسها بشكل افتراضي، لكن عندما يُحدد حجم للنافذة ستظهر على شكل نافذة منبثقة.
  • يمكن للنافذة الجديدة الوصول إلى الأصلية باستخدام الخاصية window.opener.
  • يمكن للنافذتين الجديدة والأصلية قراءة وتعديل محتويات الأخرى إذا كان لهما الأصل ذاته، ويمكنهما فقط تغيير موقع بعضهما وتبادل الرسائل إذا لم يكن لهما الأصل نفسه .
  • يستخدم التابع ()close لإغلاق نافذة منبثقة، كما يمكن إغلاقها بالطريقة التقليدية، وفي كلا الحالتين ستكون قيمة الخاصية window.closed هي "true".
  • يستخدم التابعان ()foucs و()blur لإعطاء التركيز إلى نافذة او تحويله عنها، لكنهما لا يعملان كما هو مطلوب دائمًا.
  • يستخدم الحدثان focus وblur لتتبع عدد مرات دخول النافذة أو الخروج منها، مع الانتباه إلى أنّ أي نافذة يمكن أن تبقى مرئية في الخلفية بعد تنفيذ الأمر blur.

ترجمة -وبتصرف- للفصل popups and window methods من سلسلة The Modern JavaScript Tutorial.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...