برنامج IntersectionObserver قيد العرض

تتيح لك IntersectionObservers معرفة متى يدخل عنصر خاضع للمراقبة إلى إطار عرض المتصفّح أو يخرج منه.

Browser Support

  • Chrome: 51.
  • Edge: 15.
  • Firefox: 55.
  • Safari: 12.1.

Source

لنفترض أنّك تريد تتبُّع وقت دخول عنصر في نموذج DOM إلى إطار العرض المرئي. قد ترغب في القيام بذلك حتى تتمكن من تحميل الصور في الوقت المناسب أو لأنك بحاجة إلى معرفة ما إذا كان المستخدم ينظر بالفعل إلى إعلان بانر معين. يمكنك إجراء ذلك من خلال ربط حدث الانتقال أو باستخدام موقّت دوري واستدعاء getBoundingClientRect() على هذا العنصر.

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

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

إمكانية ظهور إطار iframe

كيفية إنشاء IntersectionObserver

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

const io = new IntersectionObserver(entries => {
  console.log(entries);
}, {
  /* Using default options. Details below */
});

// Start observing an element
io.observe(element);

// Stop observing an element
// io.unobserve(element);

// Disable entire IntersectionObserver
// io.disconnect();

باستخدام الخيارات التلقائية لـ IntersectionObserver، سيتم استدعاء دالة الاستدعاء عندما يظهر العنصر جزئيًا وعندما يغادر إطار العرض بالكامل.

إذا كنت بحاجة إلى تتبُّع عناصر متعددة، يمكنك تتبُّعها باستخدام مثيل IntersectionObserver نفسه من خلال استدعاء observe() عدة مرات، ويُنصح بذلك.

يتم تمرير مَعلمة entries إلى دالة رد الاتصال، وهي مصفوفة من عناصر IntersectionObserverEntry. يحتوي كل عنصر من هذه العناصر على بيانات تقاطع معدَّلة لأحد العناصر التي تتم مراقبتها.

🔽[IntersectionObserverEntry]
    time: 3893.92
    🔽rootBounds: ClientRect
        bottom: 920
        height: 1024
        left: 0
        right: 1024
        top: 0
        width: 920
    🔽boundingClientRect: ClientRect
    // ...
    🔽intersectionRect: ClientRect
    // ...
    intersectionRatio: 0.54
    🔽target: div#observee
    // ...

rootBounds هي نتيجة استدعاء getBoundingClientRect() على العنصر الجذر، وهو إطار العرض تلقائيًا. boundingClientRect هي نتيجة getBoundingClientRect() التي تم استدعاؤها على العنصر الذي تتم مراقبته. intersectionRect هو تقاطع هذين المستطيلَين ويخبرك بفعالية أي جزء من العنصر الذي تتم ملاحظته يكون مرئيًا. intersectionRatio هو مقياس مرتبط بشكل وثيق، ويخبرك بمقدار ظهور العنصر. باستخدام هذه المعلومات، يمكنك الآن تنفيذ ميزات مثل تحميل مواد العرض في الوقت المناسب قبل أن تصبح مرئية على الشاشة. بفعالية

نسبة التقاطع

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

عناصر div قابلة للتمرير

أنا لست من محبي التمرير داخل عنصر، ولكنني لست هنا لأحكم، ولا IntersectionObserver. يأخذ العنصر options الخيار root الذي يتيح لك تحديد بديل لإطار العرض كجذر. من المهم أن تضع في اعتبارك أنّ root يجب أن يكون عنصرًا رئيسيًا لجميع العناصر التي تتم مراقبتها.

تقاطع كل الأشياء!

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

التمرير اللا نهائي

المزيد من التحديثات، من فضلك

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

هنا يأتي دور خيار threshold. تتيح لك هذه السمة تحديد مصفوفة من حدود intersectionRatio. سيتم استدعاء دالة الاستدعاء في كل مرة يتجاوز فيها intersectionRatio إحدى هذه القيم. القيمة التلقائية لـ threshold هي [0]، ما يفسّر السلوك التلقائي. إذا غيّرنا threshold إلى [0, 0.25, 0.5, 0.75, 1]، سنتلقّى إشعارًا في كل مرة يظهر فيها ربع إضافي من العنصر:

صورة متحركة توضّح الحد الأدنى

هل هناك أي خيارات أخرى؟

في الوقت الحالي، يتوفّر خيار إضافي واحد فقط إلى جانب الخيارات المذكورة أعلاه. تتيح لك السمة rootMargin تحديد هوامش الجذر، ما يتيح لك زيادة أو تقليل المساحة المستخدَمة للتقاطعات. يتم تحديد هذه الهوامش باستخدام سلسلة نمط CSS، مثل "10px 20px 30px 40px"، مع تحديد الهامش العلوي والأيمن والسفلي والأيسر على التوالي. باختصار، توفّر بنية خيارات IntersectionObserver الخيارات التالية:

new IntersectionObserver(entries => {/* … */}, {
  // The root to use for intersection.
  // If not provided, use the top-level document's viewport.
  root: null,
  // Same as margin, can be 1, 2, 3 or 4 components, possibly negative lengths.
  // If an explicit root element is specified, components may be percentages of the
  // root element size.  If no explicit root element is specified, using a
  // percentage is an error.
  rootMargin: "0px",
  // Threshold(s) at which to trigger callback, specified as a ratio, or list of
  // ratios, of (visible area / total area) of the observed element (hence all
  // entries must be in the range [0, 1]).  Callback will be invoked when the
  // visible ratio of the observed element crosses a threshold in the list.
  threshold: [0],
});

<iframe> سحرية

تم تصميم IntersectionObservers خصيصًا مع وضع الخدمات الإعلانية وأدوات الشبكات الاجتماعية في الاعتبار، والتي تستخدم عناصر <iframe> بشكل متكرر ويمكن أن تستفيد من معرفة ما إذا كانت معروضة. إذا كان <iframe> يراقب أحد عناصره، سيؤدي كل من الانتقال في <iframe> والانتقال في النافذة التي تحتوي على <iframe> إلى تشغيل دالة الاستدعاء في الأوقات المناسبة. ومع ذلك، في الحالة الأخيرة، سيتم ضبط rootBounds على null لتجنُّب تسرُّب البيانات من مصادر مختلفة.

ما هو الموضوع الذي لا يتناوله "IntersectionObserver"؟

يُرجى العِلم أنّ IntersectionObserver لا يوفّر دقة مثالية ولا وقت استجابة منخفضًا، وذلك عن قصد. إنّ استخدامها لتنفيذ مهام مثل الصور المتحركة التي تعتمد على الانتقال إلى أسفل الصفحة سيؤدي حتمًا إلى الفشل، لأنّ البيانات ستكون قديمة بحلول وقت استخدامها. يحتوي المستند التوضيحي على مزيد من التفاصيل حول حالات الاستخدام الأصلية لـ "IntersectionObserver".

ما هو حجم العمل الذي يمكنني تنفيذه في دالة الاستدعاء؟

باختصار: سيؤدي قضاء وقت طويل في عمليات الاستدعاء إلى تأخّر تطبيقك، لذا يجب تطبيق جميع الممارسات الشائعة.

انطلق واجمع العناصر المتشابهة

يتوافق المتصفّح مع IntersectionObserver بشكل جيد، لأنّه متاح في جميع المتصفّحات الحديثة. إذا لزم الأمر، يمكن استخدام polyfill في المتصفّحات القديمة، وهو متاح في مستودع WICG. من الواضح أنّك لن تحصل على مزايا الأداء باستخدام polyfill التي يوفّرها لك التنفيذ الأصلي.

يمكنك البدء في استخدام IntersectionObserver الآن. أخبِرنا بما توصّلت إليه.