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

François Beaufort
François Beaufort

التوافق مع المتصفح

  • 116
  • 116
  • x
  • x

المصدر

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

إنّ نافذة ميزة "نافذة ضمن النافذة" في واجهة برمجة التطبيقات "نافذة ضمن النافذة" تشبه نافذة فارغة من المصدر نفسه تم فتحها من خلال window.open()، مع بعض الاختلافات:

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

الوضع الحالي

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

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

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

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

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

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

الإنتاجية

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

الواجهة

أماكن إقامة

documentPictureInPicture.window
عرض نافذة ميزة "نافذة ضمن النافذة" الحالية إن توفّرت. وفي حال عدم حدوث ذلك، سيتم عرض null.

الطُرق

documentPictureInPicture.requestWindow(options)

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

width
لضبط العرض الأولي للنافذة ضمن النافذة.
height
لضبط الارتفاع الأولي للنافذة ضمن النافذة.
disallowReturnToOpener
يخفي زر "الرجوع إلى علامة التبويب" في نافذة نافذة ضمن النافذة إذا كان "صحيحًا". يكون false بشكل افتراضي.

فعاليات

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,
  });
});

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

لنسخ جميع أوراق أنماط 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() لتغيير حجم نافذة ميزة "نافذة ضمن النافذة". تتطلب كلتا الطريقتين استخدام إيماءة المستخدم.

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() للتركيز على نافذة الفتح من النافذة ضمن النافذة. تتطلّب هذه الطريقة إيماءة المستخدم.

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

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

يمكنك استخدام وضع عرض CSS picture-in-picture لكتابة قواعد 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

يمكنك تشغيل العرض التوضيحي لمشغّل VideoJS في واجهة برمجة التطبيقات Picture-in-Picture API. احرص على الاطّلاع على رمز المصدر.

بومودورو

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

لقطة شاشة لتطبيق Tomodoro على الإنترنت
نافذة ضمن النافذة في Tomodoro

إضافة ملاحظات

يُرجى الإبلاغ عن المشاكل على GitHub من خلال الاقتراحات والأسئلة.

شكر وتقدير

صورة رئيسية من تصوير جاكوب أوينز