نافذة ضمن النافذة لأي عنصر، وليس فقط <video>

François Beaufort
François Beaufort

Browser Support

  • Chrome: 116.
  • Edge: 116.
  • Firefox: not supported.
  • Safari: not supported.

Source

تتيح واجهة برمجة التطبيقات "نافذة ضمن النافذة للمستندات" فتح نافذة تظهر دائمًا في المقدّمة ويمكن ملؤها بمحتوى HTML عشوائي. وهي توسّع واجهة برمجة التطبيقات الحالية Picture-in-Picture API for <video> التي تسمح فقط بوضع عنصر HTML <video> في نافذة "نافذة ضمن النافذة".

تشبه نافذة "نافذة ضمن النافذة" في Document Picture-in-Picture API نافذة فارغة من المصدر نفسه يتم فتحها من خلال window.open()، مع بعض الاختلافات:

  • تظهر نافذة "نافذة ضمن النافذة" بشكل عائم فوق النوافذ الأخرى.
  • لا يمكن أن تبقى نافذة "نافذة ضمن النافذة" مفتوحة بعد إغلاق النافذة الأصلية.
  • لا يمكن التنقّل في نافذة "نافذة ضمن النافذة".
  • لا يمكن للموقع الإلكتروني ضبط موضع نافذة "نافذة ضمن النافذة".
نافذة &quot;نافذة ضمن النافذة&quot; تعرض فيديو الإعلان الترويجي لفيلم Sintel
نافذة "صورة داخل صورة" تم إنشاؤها باستخدام Document Picture-in-Picture API (عرض توضيحي).

الوضع الحالي

الخطوة الحالة
1. إنشاء شرح مكتمل
2. إنشاء مسودة أولية للمواصفات قيد التقدم
3- جمع الملاحظات وتكرار التصميم قيد التقدم
4. مرحلة التجربة والتقييم مكتمل
5- تشغيل مكتمل (على الكمبيوتر)

حالات الاستخدام

مشغّل فيديو مخصّص

يمكن للموقع الإلكتروني توفير تجربة فيديو "نافذة ضمن النافذة" باستخدام واجهة برمجة التطبيقات الحالية "نافذة ضمن النافذة" في <video>، ولكنها محدودة جدًا. تقبل نافذة "نافذة ضمن النافذة" الحالية عددًا قليلاً من الإدخالات، كما أنّ إمكانية تنسيقها محدودة. باستخدام ميزة "المستند في نافذة ضمن النافذة" الكاملة، يمكن للموقع الإلكتروني توفير عناصر تحكّم وإدخالات مخصّصة (مثل الترجمة والشرح وقوائم التشغيل وشريط التمرير السريع للإطار الزمني والإعجاب بالفيديوهات وعدم الإعجاب بها) لتحسين تجربة المستخدم في مشاهدة الفيديوهات باستخدام ميزة "نافذة ضمن النافذة".

اجتماعات الفيديو

من الشائع أن يغادر المستخدمون علامة تبويب المتصفّح أثناء جلسة مؤتمر فيديو لأسباب مختلفة (مثل عرض علامة تبويب أخرى في المكالمة أو تنفيذ مهام متعددة) مع الرغبة في مواصلة مشاهدة المكالمة، لذا تُعدّ هذه الحالة من حالات الاستخدام الأساسية لميزة "نافذة ضمن النافذة". مرّة أخرى، إنّ التجربة الحالية التي يمكن أن يوفّرها موقع إلكتروني لعقد مؤتمرات عبر الفيديو من خلال Picture-in-Picture API لـ <video> محدودة من حيث الأسلوب والإدخال. باستخدام ميزة "مستند كامل في نافذة ضمن النافذة"، يمكن للموقع الإلكتروني بسهولة دمج عدّة بث مباشر للفيديو في نافذة واحدة ضمن النافذة بدون الحاجة إلى استخدام اختراقات لوحة الرسم وتوفير عناصر تحكّم مخصّصة، مثل إرسال رسالة أو كتم صوت مستخدم آخر أو رفع اليد.

الإنتاجية

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

الواجهة

الخصائص

documentPictureInPicture.window
تعرض هذه السمة نافذة "نافذة ضمن النافذة" الحالية، إذا كانت متاحة. بخلاف ذلك، تعرض null.

الطُرق

documentPictureInPicture.requestWindow(options)

تعرض هذه الطريقة وعدًا يتم تنفيذه عند فتح نافذة "نافذة ضمن النافذة". يتم رفض الوعد إذا تم استدعاؤه بدون إيماءة من المستخدم. يحتوي قاموس options على الأعضاء الاختياريين التاليين:

width
تضبط هذه السمة العرض الأولي لنافذة "نافذة ضمن النافذة".
height
تضبط هذه السمة الارتفاع الأولي لنافذة "نافذة ضمن النافذة".
disallowReturnToOpener
يخفي زر "الرجوع إلى علامة التبويب" في نافذة "النافذة ضمن النافذة" إذا كانت القيمة صحيحة. القيمة التلقائية هي "خطأ".
preferInitialWindowPlacement
فتح نافذة "نافذة ضمن النافذة" في موضعها وحجمها التلقائيَين إذا كانت القيمة صحيحة تكون القيمة التلقائية هي "خطأ".

الفعاليات

documentPictureInPicture.onenter
يتم تنشيطها في documentPictureInPicture عند فتح نافذة "نافذة ضمن النافذة".

أمثلة

يُعدّ رمز HTML التالي مشغّل فيديو مخصّصًا وعنصر زر لفتح مشغّل الفيديو في نافذة "النافذة ضمن النافذة".

<div id="playerContainer">
  <div id="player">
    <video id="video"></video>
  </div>
</div>
<button id="pipButton">Open Picture-in-Picture window</button>

فتح نافذة "نافذة ضمن النافذة"

تستدعي JavaScript التالية documentPictureInPicture.requestWindow() عندما ينقر المستخدم على الزر لفتح نافذة فارغة في وضع "نافذة ضمن النافذة". يتم حلّ الوعد الذي تم إرجاعه باستخدام كائن JavaScript لنافذة "نافذة ضمن النافذة". يتم نقل مشغّل الفيديو إلى تلك النافذة باستخدام append().

pipButton.addEventListener('click', async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

ضبط حجم نافذة ميزة "نافذة ضمن النافذة"

لضبط حجم نافذة "نافذة ضمن النافذة"، اضبط الخيارَين width وheight من documentPictureInPicture.requestWindow() على حجم نافذة "نافذة ضمن النافذة" المطلوب. قد يحدّ Chrome من قيم الخيار إذا كانت كبيرة جدًا أو صغيرة جدًا بحيث لا تتناسب مع حجم نافذة سهل الاستخدام.

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window whose size is
  // the same as the player's.
  const pipWindow = await documentPictureInPicture.requestWindow({
    width: player.clientWidth,
    height: player.clientHeight,
  });

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

إخفاء زر "الرجوع إلى علامة التبويب" في نافذة "نافذة ضمن النافذة"

لإخفاء الزر في نافذة "صورة داخل صورة" الذي يتيح للمستخدم الرجوع إلى علامة التبويب التي فتحت النافذة، اضبط الخيار disallowReturnToOpener الخاص بـ documentPictureInPicture.requestWindow() على true.

pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window which hides the "back to tab" button.
  const pipWindow = await documentPictureInPicture.requestWindow({
    disallowReturnToOpener: true,
  });
});

فتح نافذة "نافذة ضمن النافذة" في موضعها وحجمها التلقائيَّين

لعدم إعادة استخدام موضع أو حجم نافذة "النافذة ضمن النافذة" السابقة، اضبط الخيار preferInitialWindowPlacement في documentPictureInPicture.requestWindow() على true.

pipButton.addEventListener("click", async () => {
  // Open a Picture-in-Picture window in its default position / size.
  const pipWindow = await documentPictureInPicture.requestWindow({
    preferInitialWindowPlacement: true,
  });
});

نسخ أوراق الأنماط إلى نافذة ميزة "نافذة ضمن النافذة"

لنسخ جميع أوراق أنماط CSS من النافذة الأصلية، يجب تكرار styleSheets المرتبط بشكل صريح أو المضمّن في المستند وإلحاقه بنافذة "نافذة ضمن النافذة". يُرجى العِلم أنّ هذه نسخة تُستخدم لمرة واحدة.

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Copy style sheets over from the initial document
  // so that the player looks the same.
  [...document.styleSheets].forEach((styleSheet) => {
    try {
      const cssRules = [...styleSheet.cssRules].map((rule) => rule.cssText).join('');
      const style = document.createElement('style');

      style.textContent = cssRules;
      pipWindow.document.head.appendChild(style);
    } catch (e) {
      const link = document.createElement('link');

      link.rel = 'stylesheet';
      link.type = styleSheet.type;
      link.media = styleSheet.media;
      link.href = styleSheet.href;
      pipWindow.document.head.appendChild(link);
    }
  });

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);
});

التعامل مع حالة إغلاق نافذة ميزة "نافذة ضمن النافذة"

استمِع إلى حدث النافذة "pagehide" لمعرفة وقت إغلاق نافذة "نافذة ضمن النافذة" (إما لأنّ الموقع الإلكتروني بدأها أو لأنّ المستخدم أغلقها يدويًا). معالج الأحداث هو المكان المناسب لاسترداد العناصر من نافذة "صورة داخل صورة" كما هو موضّح هنا.

pipButton.addEventListener("click", async () => {
  const player = document.querySelector("#player");

  // Open a Picture-in-Picture window.
  const pipWindow = await documentPictureInPicture.requestWindow();

  // Move the player to the Picture-in-Picture window.
  pipWindow.document.body.append(player);

  // Move the player back when the Picture-in-Picture window closes.
  pipWindow.addEventListener("pagehide", (event) => {
    const playerContainer = document.querySelector("#playerContainer");
    const pipPlayer = event.target.querySelector("#player");
    playerContainer.append(pipPlayer);
  });
});

أغلِق النافذة الخاصة بميزة "نافذة ضمن النافذة" آليًا باستخدام الطريقة close().

// Close the Picture-in-Picture window programmatically. 
// The "pagehide" event will fire normally.
pipWindow.close();

الاستماع إلى إشعار عند فتح الموقع الإلكتروني في وضع "نافذة ضمن النافذة"

استمع إلى حدث "enter" على documentPictureInPicture لمعرفة وقت فتح نافذة "نافذة ضمن النافذة". يحتوي الحدث على عنصر window للوصول إلى نافذة ميزة "نافذة ضمن النافذة".

documentPictureInPicture.addEventListener("enter", (event) => {
  const pipWindow = event.window;
});

الوصول إلى العناصر في نافذة ميزة "نافذة ضمن النافذة"

يمكن الوصول إلى العناصر في نافذة "نافذة ضمن النافذة" إما من العنصر الذي تم عرضه بواسطة documentPictureInPicture.requestWindow() أو باستخدام documentPictureInPicture.window كما هو موضّح أدناه.

const pipWindow = documentPictureInPicture.window;
if (pipWindow) {
  // Mute video playing in the Picture-in-Picture window.
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
}

التعامل مع الأحداث من نافذة "نافذة ضمن النافذة"

يمكنك إنشاء أزرار وعناصر تحكّم والاستجابة لأحداث إدخال المستخدم، مثل "click"، كما تفعل عادةً في JavaScript.

// Add a "mute" button to the Picture-in-Picture window.
const pipMuteButton = pipWindow.document.createElement("button");
pipMuteButton.textContent = "Mute";
pipMuteButton.addEventListener("click", () => { 
  const pipVideo = pipWindow.document.querySelector("#video");
  pipVideo.muted = true;
});
pipWindow.document.body.append(pipMuteButton);

تغيير حجم "نافذة ضمن النافذة"

استخدِم طريقتَي resizeBy() وresizeTo() Window لتغيير حجم نافذة "نافذة ضمن النافذة". تتطلّب كلتا الطريقتَين إيماءة من المستخدم.

const resizeButton = pipWindow.document.createElement('button');
resizeButton.textContent = 'Resize';
resizeButton.addEventListener('click', () => {
  // Expand the Picture-in-Picture window's width by 20px and height by 30px.
  pipWindow.resizeBy(20, 30);
});
pipWindow.document.body.append(resizeButton);

التركيز على نافذة الفتح

استخدِم طريقة focus() Window للتركيز على نافذة الفتح من نافذة "نافذة ضمن النافذة". تتطلّب هذه الطريقة إيماءة مستخدم.

const returnToTabButton = pipWindow.document.createElement("button");
returnToTabButton.textContent = "Return to opener tab";
returnToTabButton.addEventListener("click", () => {
  window.focus();
});
pipWindow.document.body.append(returnToTabButton);

وضع العرض "نافذة ضمن النافذة" في CSS

استخدِم وضع العرض picture-in-picture CSS لكتابة قواعد CSS معيّنة لا يتم تطبيقها إلا عندما يتم عرض (جزء من) تطبيق الويب في وضع "نافذة ضمن النافذة".

@media all and (display-mode: picture-in-picture) {
  body {
    margin: 0;
  }
  h1 {
    font-size: 0.8em;
  }
}

رصد الميزات

للتحقّق مما إذا كانت واجهة برمجة التطبيقات Document Picture-in-Picture API متوافقة، استخدِم ما يلي:

if ('documentPictureInPicture' in window) {
  // The Document Picture-in-Picture API is supported.
}

العروض التوضيحية

مشغّل VideoJS

يمكنك تجربة Document Picture-in-Picture API من خلال العرض التوضيحي لمشغّل VideoJS.

Pomodoro

يستفيد تطبيق الويب Tomodoro الذي يعتمد على تقنية بومودورو أيضًا من واجهة Document Picture-in-Picture API عندما تكون متاحة. اطّلِع على طلب السحب على GitHub.

‫Tomodoro، وهو تطبيق ويب لتقنية بومودورو
نافذة "نافذة ضمن النافذة" في تطبيق Tomodoro

مشاركة ملاحظاتك

الإبلاغ عن مشاكل في GitHub مع اقتراحات وأسئلة