TL;DR
إليك سرّ: قد لا تحتاج إلى أحداث scroll
في تطبيقك التالي. باستخدام
IntersectionObserver
،
سأوضّح لك كيفية بدء حدث مخصّص عندما تصبح عناصر position:sticky
ثابتة أو عندما تتوقف عن الالتصاق. وكل ذلك بدون
استخدام مستمعي الانتقالات. إليك فيديو تجريبي رائع لإثبات ذلك:
لمحة عن حدث sticky-change
من القيود العملية لاستخدام موضع CSS الثابت هو أنّه لا يقدّم إشارة من النظام الأساسي لمعرفة وقت نشاط الإعلان. بعبارة أخرى، لا يتوفّر حدث لمعرفة متى يصبح العنصر ثابتًا أو متى يتوقف عن الثبات.
راجِع المثال التالي الذي يثبت ألن يكون من الأفضل أن يُعلمك المتصفّح عندما تصل العناصر إلى هذه العلامة؟
يبدو أنّني لست الوحيد
الذي يعتقد ذلك. يمكن أن تؤدي إشارة استنادًا إلى حالات الاستخدام هذه، وضعنا هدفًا نهائيًا: إنشاء حدث يتم تفعيله عندما يتم إصلاح عنصر يستخدم الإصدار التجريبي
هذا الحدث لإضافة ظلّ للعناوين عندما تصبح ثابتة. ويؤدي ذلك أيضًا إلى تعديل
العنوان الجديد في أعلى الصفحة. لنوضّح بعض المصطلحات لنتمكّن من الإشارة إلى هذه الأسماء
في بقية المقالة: لمعرفة العنوان الذي يدخل "الوضع المُلصق"، نحتاج إلى طريقة لتحديد
مقدار التمرير في الحاوية التي يمكن التمرير فيها. سيتيح لنا ذلك معرفة كيفية حساب العنوان الذي يظهر حاليًا. ومع ذلك، يصبح من الصعوبة بمكان
تنفيذ ذلك بدون أحداث وبالتالي، بدون أحداث الانتقال إلى أعلى أو أسفل الصفحة، فقدنا إمكانية إجراء
عمليات حسابية ذات صلة بالتنسيق على العناوين. بدلاً من أحداث نحتاج إلى عنصرَين مراقبة لتغطية أربع حالات لتحريك الصفحة للأعلى أو للأسفل: من المفيد مشاهدة تسجيل رقمي للشاشة من الخطوة 1 إلى الخطوة 4 بالترتيب التالي: يتم وضع نقاط التحكّم في أعلى وأسفل كل قسم.
يظهر ترصد Intersection Observers التغييرات بشكل غير متزامن في تقاطع
عنصر مستهدَف وإطار عرض المستند أو حاوية رئيسية. في حالتنا،
نلاحظ تقاطعات مع حاوية رئيسية.
القيمة السحرية هي أولاً، أعددت مراقبين لمراقبي الرأس والتذييل: بعد ذلك، أضفت مراقبًا لإطلاقه عند مرور عناصر تم ضبط المراقب باستخدام تكون العملية مشابهة لمراقب الجزء السفلي ( تم ضبط المراقب باستخدام أخيرًا، إليك الأداتان اللتان أستخدمهما لإطلاق الحدث المخصّص هذا كل شيء! أنشأنا حدثًا مخصّصًا عندما تصبح العناصر التي تحتوي على لقد تساءلت كثيرًا عما إذا كانت ليس فعلاً. ما احتجنا إليه هو طريقة لرصد تغييرات الأنماط في عنصر DOM.
لا تتيح لك واجهات برمجة التطبيقات لمنصّة الويب
مراقبة تغييرات التصميم. قد يكون في المستقبل، قد تكون إضافة
"Style Mutation Observer"
إلى Mutation Observers مفيدة لرصد التغييرات في
أنماط العنصر المحسوبة.
إنّ محتوى هذه الصفحة مرخّص بموجب ترخيص Creative Commons Attribution 4.0 ما لم يُنصّ على خلاف ذلك، ونماذج الرموز مرخّصة بموجب ترخيص Apache 2.0. للاطّلاع على التفاصيل، يُرجى مراجعة سياسات موقع Google Developers. إنّ Java هي علامة تجارية مسجَّلة لشركة Oracle و/أو شركائها التابعين. تاريخ التعديل الأخير: 2017-09-19 (حسب التوقيت العالمي المتفَّق عليه).sticky {
position: sticky;
top: 10px;
}
position:sticky
إلى فتح عدد من حالات الاستخدام:
position:sticky
. لنسمّيه حدث
sticky-change
:document.addEventListener('sticky-change', e => {
const header = e.detail.target; // header became sticky or stopped sticking.
const sticking = e.detail.stuck; // true when header is sticky.
header.classList.toggle('shadow', sticking); // add drop shadow when sticking.
document.querySelector('.who-is-sticking').textContent = header.textContent;
});
تأثيرات الانتقال ضمن الصفحة بدون أحداث الانتقال ضمن الصفحة؟
position:sticky
.position:sticky
على العنصر.scroll
. :) المشكلة الأخرى هي أنّ
position:sticky
تزيل العنصر من التنسيق عندما يصبح ثابتًا.إضافة عنصر DOM وهمي لتحديد موضع الانتقال
scroll
، سنستخدم IntersectionObserver
لتحديد الحالات التي تدخل فيها الرؤوس وضع "العرض الثابت" وتخرج منه. ستؤدي إضافة عقدتَين
(المعروفتَين أيضًا باسم نقاط المراقبة) في كل قسم ثابت، واحدة في الأعلى وأخرى
في الأسفل، إلى العمل كنقاط مرجعية لتحديد موضع الانتقال. وعندما تدخل هذه
العلامات الحاوية وتغادرها، يتغيّر مستوى ظهورها ويُطلق
Intersection Observer طلب استدعاء.
خدمة مقارنة الأسعار
.sticky_sentinel--top
في أعلى العنوان بينما يظهر
.sticky_sentinel--bottom
في أسفل القسم:
:root {
--default-padding: 16px;
--header-height: 80px;
}
.sticky {
position: sticky;
top: 10px; /* adjust sentinel height/positioning based on this position. */
height: var(--header-height);
padding: 0 var(--default-padding);
}
.sticky_sentinel {
position: absolute;
left: 0;
right: 0; /* needs dimensions */
visibility: hidden;
}
.sticky_sentinel--top {
/* Adjust the height and top values based on your on your sticky top position.
e.g. make the height bigger and adjust the top so observeHeaders()'s
IntersectionObserver fires as soon as the bottom of the sentinel crosses the
top of the intersection container. */
height: 40px;
top: -24px;
}
.sticky_sentinel--bottom {
/* Height should match the top of the header when it's at the bottom of the
intersection container. */
height: calc(var(--header-height) + var(--default-padding));
bottom: 0;
}
إعداد Intersection Observers
IntersectionObserver
. يحصل كل عنصر مراقبة على
IntersectionObserver
لمراقبة مستوى رؤية تقاطعه ضمن
حاوية التمرير. عندما يتم تمرير عنصر Sentinel إلى إطار العرض المرئي، نعرف أنّه
أصبح العنوان ثابتًا أو توقّف عن الالتصاق. وبالمثل، عند خروج عنصر مراقبة من
إطار العرض./**
* Notifies when elements w/ the `sticky` class begin to stick or stop sticking.
* Note: the elements should be children of `container`.
* @param {!Element} container
*/
function observeStickyHeaderChanges(container) {
observeHeaders(container);
observeFooters(container);
}
observeStickyHeaderChanges(document.querySelector('#scroll-container'));
.sticky_sentinel--top
من خلال أعلى حاوية التمرير (في أي اتجاه).
تنشئ الدالة observeHeaders
أهم نقاط المراقبة وتضيفها إلى
كل قسم. يحسب المُراقب تقاطع العنصر الحارس مع
أعلى الحاوية ويقرر ما إذا كان يدخل مساحة العرض أو يغادرها. وتحدِّد هذه
المعلومات ما إذا كان عنوان القسم ثابتًا أم لا./**
* Sets up an intersection observer to notify when elements with the class
* `.sticky_sentinel--top` become visible/invisible at the top of the container.
* @param {!Element} container
*/
function observeHeaders(container) {
const observer = new IntersectionObserver((records, observer) => {
for (const record of records) {
const targetInfo = record.boundingClientRect;
const stickyTarget = record.target.parentElement.querySelector('.sticky');
const rootBoundsInfo = record.rootBounds;
// Started sticking.
if (targetInfo.bottom < rootBoundsInfo.top) {
fireEvent(true, stickyTarget);
}
// Stopped sticking.
if (targetInfo.bottom >= rootBoundsInfo.top &&
targetInfo.bottom < rootBoundsInfo.bottom) {
fireEvent(false, stickyTarget);
}
}
}, {threshold: [0], root: container});
// Add the top sentinels to each section and attach an observer.
const sentinels = addSentinels(container, 'sticky_sentinel--top');
sentinels.forEach(el => observer.observe(el));
}
threshold: [0]
حتى يتم تشغيل دالة الاستدعاء الخاصة به فور
ظهور الحارس..sticky_sentinel--bottom
).
يتم إنشاء مراقب ثانٍ لإطلاقه عندما تمر التذييلات من خلال الجزء السفلي
من الحاوية التي يتم التمرير فيها. تنشئ الدالة observeFooters
العقد المراقبة وتُرفقها بكل قسم. يحسب المُراقب نقطة
تقاطع العنصر الحارس مع قاع الحاوية ويقرر ما إذا كان
يدخل أو يخرج. وتحدِّد هذه المعلومات ما إذا كان عنوان القسم
يبقى مرئيًا أم لا./**
* Sets up an intersection observer to notify when elements with the class
* `.sticky_sentinel--bottom` become visible/invisible at the bottom of the
* container.
* @param {!Element} container
*/
function observeFooters(container) {
const observer = new IntersectionObserver((records, observer) => {
for (const record of records) {
const targetInfo = record.boundingClientRect;
const stickyTarget = record.target.parentElement.querySelector('.sticky');
const rootBoundsInfo = record.rootBounds;
const ratio = record.intersectionRatio;
// Started sticking.
if (targetInfo.bottom > rootBoundsInfo.top && ratio === 1) {
fireEvent(true, stickyTarget);
}
// Stopped sticking.
if (targetInfo.top < rootBoundsInfo.top &&
targetInfo.bottom < rootBoundsInfo.bottom) {
fireEvent(false, stickyTarget);
}
}
}, {threshold: [1], root: container});
// Add the bottom sentinels to each section and attach an observer.
const sentinels = addSentinels(container, 'sticky_sentinel--bottom');
sentinels.forEach(el => observer.observe(el));
}
threshold: [1]
لكي يتم تشغيل دالة الاستدعاء الخاصة به عندما تكون
العقدة بأكملها في نطاق العرض.sticky-change
وإنشاء العناصر الحارسة:/**
* @param {!Element} container
* @param {string} className
*/
function addSentinels(container, className) {
return Array.from(container.querySelectorAll('.sticky')).map(el => {
const sentinel = document.createElement('div');
sentinel.classList.add('sticky_sentinel', className);
return el.parentElement.appendChild(sentinel);
});
}
/**
* Dispatches the `sticky-event` custom event on the target element.
* @param {boolean} stuck True if `target` is sticky.
* @param {!Element} target Element to fire the event on.
*/
function fireEvent(stuck, target) {
const e = new CustomEvent('sticky-change', {detail: {stuck, target}});
document.dispatchEvent(e);
}
العرض التجريبي النهائي
position:sticky
ثابتة وأضفنا تأثيرات التمرير بدون استخدام أحداث scroll
.الخاتمة
IntersectionObserver
ستكون أداة مفيدة لاستبدال بعض أنماط واجهة المستخدم المستندة إلى الأحداث في scroll
التي
تم تطويرها على مرّ السنين. تبيّن أنّ الإجابة هي نعم ولا. إنّ الدلالات
لواجهة برمجة التطبيقات IntersectionObserver
تجعل من الصعب استخدامها في كل شيء. ولكن كما هو موضّح في المثال أدناه، يمكنك استخدامها لتنفيذ بعض الأساليب المثيرة للاهتمام.هل هناك طريقة أخرى لرصد تغييرات الأنماط؟
MutationObserver
هو الخيار الأول المنطقي، ولكنّه لا يناسب
معظم الحالات. على سبيل المثال، في العرض التجريبي، سنتلقّى طلب استدعاء عند إضافة الفئة sticky
إلى عنصر، ولكن ليس عند تغيير النمط المحسوب للعنصر.
تذكَّر أنّه سبق أن تمّ الإعلان عن فئة sticky
عند تحميل الصفحة.position: sticky
.