Şimdi Beni Görüyorsun: IntersectionObserver ile Nasıl Ertelenir, Tembel Yüklenir ve Hareket Edilir

Yayınlanan: 2022-03-10
Hızlı özet ↬ Görüntülerin geç yüklenmesi gibi birçok nedenden dolayı kavşak bilgilerine ihtiyaç vardır. Ama çok daha fazlası var. Intersection Observer API'sini daha iyi anlamanın ve farklı bakış açıları edinmenin zamanı geldi. Hazır?

Bir zamanlar, sitelerin tüm tarayıcılarda aynı görünmemesi gerektiğine müşterilerini başarıyla ikna eden, erişilebilirliğe önem veren ve CSS ızgaralarını erken benimseyen bir web geliştiricisi vardı. Ama kalbinin derinliklerinde gerçek tutkusu performanstı: Projelerinde sürekli olarak optimize etti, küçülttü, izledi ve hatta psikolojik hileler kullandı.

Ardından, bir gün, kullanıcılar tarafından hemen görülemeyen ve ekranda anlamlı içerik oluşturmak için gerekli olmayan, geç yüklenen resimler ve diğer varlıklar hakkında bilgi edindi. Şafağın başlangıcıydı: Geliştirici, tembel yüklenen jQuery eklentilerinin şeytani dünyasına (veya belki de async ve defer özelliklerinin o kadar da kötü olmayan dünyasına) girdi. Hatta bazıları, tüm kötülüklerin doğrudan özüne girdiğini söylüyor: scroll olayı dinleyicileri dünyası. Nerede olduğunu asla kesin olarak bilemeyeceğiz, ancak yine de bu geliştirici kesinlikle hayalidir ve herhangi bir geliştiriciyle herhangi bir benzerlik sadece tesadüfidir.

bir web geliştiricisi
Hayali web geliştiricisi

Artık Pandora'nın kutusunun açıldığını ve hayali geliştiricimizin sorunu daha az gerçek yapmadığını söyleyebilirsiniz. Günümüzde, web projelerimizin hem hız hem de sayfa ağırlığı açısından performansı için ekranın üst kısmındaki içeriğe öncelik vermek son derece önemli hale geldi.

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Bu yazıda, scroll karanlığından çıkıp, kaynakları tembel yüklemenin modern yolu hakkında konuşacağız. Yalnızca tembel yükleme görüntüleri değil, aynı zamanda bu konuda herhangi bir varlık yükleme. Dahası, bugün bahsedeceğimiz teknik, varlıkları tembelce yüklemekten çok daha fazlasını yapabilir: Öğelerin kullanıcılara görünürlüğüne dayalı olarak herhangi bir türde ertelenmiş işlevsellik sağlayabileceğiz.

IntersectionObserver: Şimdi Beni Görüyorsun

Bayanlar ve baylar, Kesişme Gözlemcisi API'sinden bahsedelim. Ancak başlamadan önce, bizi IntersectionObserver yönlendiren modern araçların manzarasına bir göz atalım.

2017, tarayıcılarımızda yerleşik olarak bulunan araçlar için çok iyi bir yıldı ve çok fazla çaba harcamadan kod tabanımızın kalitesini ve stilini iyileştirmemize yardımcı oldu. Bu günlerde, web, çok tipik olan ve daha iyi tanımlanmış bir Observer arayüzleri yaklaşımına (veya sadece “Gözlemcilere”) çok farklı çözümlere dayanan düzensiz çözümlerden uzaklaşıyor gibi görünüyor: İyi desteklenmiş MutationObserver, hızlı bir şekilde yeni aile üyelerine sahip oldu. modern tarayıcılarda kabul edildi:

  • Kavşak Gözlemcisi ve
  • PerformanceObserver (Performans Zaman Çizelgesi Düzey 2 belirtiminin bir parçası olarak).

Bir potansiyel aile üyesi daha, FetchObserver, devam eden bir çalışmadır ve bizi bir ağ proxy'sinin topraklarına daha fazla yönlendirir, ancak bugün bunun yerine ön uç hakkında daha fazla konuşmak istiyorum.

IntersectionObserver ve PerformanceObserver, Observers ailesinin yeni üyeleridir.
IntersectionObserver ve PerformanceObserver, Observers ailesinin yeni üyeleridir.

PerformanceObserver ve IntersectionObserver , ön uç geliştiricilerin projelerinin performansını farklı noktalarda iyileştirmelerine yardımcı olmayı amaçlar. İlki bize Gerçek Kullanıcı İzleme için bir araç sağlarken, ikincisi bize somut performans iyileştirmesi sağlayan araçtır. Daha önce de belirtildiği gibi, bu makale tam olarak ikincisine ayrıntılı olarak bakacaktır: IntersectionObserver . Özellikle IntersectionObserver mekaniğini anlamak için, genel bir Observer'ın modern web'de nasıl çalışması gerektiğine bir göz atmalıyız.

Profesyonel İpucu : Teoriyi atlayabilir ve hemen IntersectionObserver'ın mekaniğine dalabilirsiniz veya daha da ilerisi, doğrudan IntersectionObserver olası uygulamalarına geçebilirsiniz.

Gözlemci ve Olay

Adından da anlaşılacağı gibi bir "Gözlemci", bir sayfa bağlamında gerçekleşen bir şeyi gözlemlemeyi amaçlar. Gözlemciler, DOM değişiklikleri gibi bir sayfada olup bitenleri izleyebilir. Ayrıca sayfanın yaşam döngüsü olaylarını da izleyebilirler. Gözlemciler ayrıca bazı geri arama işlevlerini de çalıştırabilir. Şimdi dikkatli okuyucu buradaki sorunu hemen fark edebilir ve “Peki, amaç ne? Zaten bu amaçla etkinliklerimiz yok mu? Observers'ı farklı kılan nedir?” Çok iyi nokta! Daha yakından bakalım ve çözelim.

Gözlemci ve Olay: fark nedir?
Gözlemci ve Olay: Fark nedir?

Normal Olay ve Gözlemci arasındaki en önemli fark, varsayılan olarak, birincisinin Olay'ın her oluşumu için eşzamanlı olarak tepki vermesi ve ana iş parçacığının yanıt verme hızını etkilemesi, ikincisinin ise performansı çok fazla etkilemeden eşzamansız olarak tepki vermesidir. En azından, bu şu anda sunulan Gözlemciler için geçerlidir: Hepsi asenkron olarak davranır ve bunun gelecekte değişeceğini sanmıyorum.

Bu, yeni başlayanların kafasını karıştırabilecek Gözlemcilerin geri aramalarının ele alınmasında ana farklılığa yol açar: Gözlemcilerin zaman uyumsuz yapısı, aynı anda birkaç gözlemlenebilirin bir geri arama işlevine geçirilmesine neden olabilir. Bu nedenle, geri arama işlevi tek bir giriş değil, bir Array giriş beklemelidir (bazen Dizi içinde yalnızca bir giriş içerse bile).

Ayrıca, bazı Gözlemciler (özellikle bugün bahsettiğimiz), çok kullanışlı önceden hesaplanmış özellikler sağlar, aksi takdirde düzenli olayları kullanırken pahalı (performans açısından) yöntemler ve özellikler kullanarak kendimizi hesaplardık. Bu noktayı açıklığa kavuşturmak için, makalenin biraz ilerisinde bir örneğe geçeceğiz.

Bu nedenle, Olay paradigmasından ayrılmak biri için zorsa, Gözlemcilerin steroidlerle ilgili olaylar olduğunu söyleyebilirim. Başka bir tanım şöyle olacaktır: Gözlemciler, olayların üstünde yeni bir yaklaşım düzeyidir. Ancak hangi tanımı tercih ederseniz edin, Gözlemcilerin olayların yerini alması amaçlanmadığını söylemeye gerek yok (en azından henüz değil); her ikisi için de yeterli kullanım durumu vardır ve yan yana mutlu bir şekilde yaşayabilirler.

Gözlemcilerin Etkinliklerin yerini alması amaçlanmamıştır: Her ikisi de birlikte mutlu bir şekilde yaşayabilir.
Gözlemcilerin Etkinliklerin yerini alması amaçlanmamıştır: Her ikisi de birlikte mutlu bir şekilde yaşayabilir.

Genel Gözlemcinin Yapısı

Bir Gözlemcinin genel yapısı (yazma sırasında mevcut olanlardan herhangi biri) şuna benzer:

 /** * Typical Observer's registration */ let observer = new YOUR-TYPE-OF-OBSERVER(function (entries) { // entries: Array of observed elements entries.forEach(entry => { // Here we can do something with each particular entry }); }); // Now we should tell our Observer what to observe observer.observe(WHAT-TO-OBSERVE);

Yine, entries tek bir giriş değil, bir değerler Array olduğunu unutmayın.

Bu genel yapıdır: Belirli Gözlemcilerin uygulamaları, observe() iletilen argümanlarda ve geri çağrısına iletilen argümanlarda farklılık gösterir. Örneğin MutationObserver , DOM'da hangi değişikliklerin gözlemleneceği hakkında daha fazla bilgi edinmek için bir yapılandırma nesnesi de almalıdır. PerformanceObserver , DOM'deki düğümleri gözlemlemez, bunun yerine gözlemleyebileceği özel giriş türleri kümesine sahiptir.

Burada, bu tartışmanın "genel" kısmını bitirelim ve bugünün makalesinin konusu olan IntersectionObserver daha derinlerine inelim.

IntersectionObserver'ı Deconstructing

IntersectionObserver'ı Deconstructing
IntersectionObserver'ı Deconstructing

Öncelikle IntersectionObserver ne olduğunu çözelim.

MDN'ye göre:

Intersection Observer API, bir hedef öğenin bir üst öğeyle veya bir üst düzey belgenin görünüm penceresiyle kesişimindeki değişiklikleri eşzamansız olarak gözlemlemek için bir yol sağlar.

Basitçe söylemek gerekirse, IntersectionObserver bir öğenin başka bir öğeyle örtüşmesini eşzamansız olarak gözlemler. Şimdi bu öğelerin IntersectionObserver ne işe yaradığından bahsedelim.

IntersectionObserver Başlatma

Önceki paragraflardan birinde, genel bir Observer'ın yapısını gördük. IntersectionObserver bu yapıyı biraz genişletir. Her şeyden önce, bu tür bir Gözlemci, üç ana öğeye sahip bir yapılandırma gerektirir:

  • root : Bu, gözlem için kullanılan kök elemandır. Gözlenebilir öğeler için temel "yakalama çerçevesini" tanımlar. Varsayılan olarak, root , tarayıcınızın görünüm alanıdır, ancak gerçekten DOM'nizdeki herhangi bir öğe olabilir (o zaman root , document.getElementById('your-element') gibi bir şeye ayarlarsınız). Ancak, bu durumda gözlemlemek istediğiniz öğelerin root DOM ağacında “yaşaması” gerektiğini unutmayın.
IntersectionObserver'ın yapılandırmasının kök özelliği
root özelliği, öğelerimiz için 'çerçeve yakalama' için temel tanımlar.
  • rootMargin : root öğenizin boyutları yeterli esneklik sağlamadığında "yakalama çerçevesini" genişleten veya daraltan root öğenizin etrafındaki kenar boşluğunu tanımlar. Bu konfigürasyonun değerleri için seçenekler, rootMargin: '50px 20px 10px 40px' (üst, sağ alt, sol) gibi, CSS'deki margin değerlerine benzer. Değerler kısaltılabilir ( rootMargin: '50px' gibi) ve px veya % olarak ifade edilebilir. Varsayılan olarak rootMargin: '0px' .
IntersectionObserver'ın yapılandırmasının rootMargin özelliği
rootMargin özelliği, root tarafından tanımlanan 'yakalama çerçevesini' genişletir/daraltır.
  • threshold : Gözlenen bir öğe "yakalama çerçevesinin" ( root ve rootMargin birleşimi olarak tanımlanır) bir sınırıyla kesiştiğinde anında tepki vermek her zaman istenmez. threshold , Observer'ın tepki vermesi gereken bu tür kesişme yüzdesini tanımlar. Tek bir değer veya bir dizi değer olarak tanımlanabilir. threshold etkisini daha iyi anlamak için (bazen kafa karıştırıcı olabileceğini biliyorum), işte bazı örnekler:
    • threshold: 0 : IntersectionObserver varsayılan değeri , gözlemlenen bir öğenin en ilk veya en son pikseli "yakalama çerçevesinin" sınırlarından biriyle kesiştiğinde tepki vermelidir. IntersectionObserver yönden bağımsız olduğunu, yani her iki senaryoda da tepki vereceğini unutmayın: a) öğe girdiğinde ve b) "yakalama çerçevesinden" ayrıldığında .
    • threshold: 0.5 : Gözlenen öğenin %50'si “yakalama çerçevesi” ile kesiştiğinde, gözlemci tetiklenmelidir;
    • threshold: [0, 0.2, 0.5, 1] ​​: Gözlemci 4 durumda tepki vermelidir:
      • Gözlenen bir öğenin ilk pikseli "yakalama çerçevesine" girer: öğe hala gerçekten o karenin içinde değildir veya gözlenen öğenin en son pikseli "yakalama çerçevesini" terk eder: öğe artık çerçeve içinde değildir;
      • Elemanın %20'si “yakalama çerçevesi” içindedir (yine, IntersectionObserver için yön önemli değildir);
      • Öğenin %50'si "yakalama çerçevesi" içindedir;
      • Öğenin %100'ü "yakalama çerçevesi" içindedir. Bu, threshold: 0 .
IntersectionObserver'ın yapılandırmasının eşik özelliği
threshold özelliği, Observer tetiklenmeden önce öğenin 'yakalama çerçevemizle' ne kadar kesişmesi gerektiğini tanımlar.

IntersectionObserver istediğimiz konfigürasyonu bildirmek için, config nesnemizi Observer'ın yapıcısına aşağıdaki gibi geri çağırma fonksiyonumuzla birlikte iletiyoruz:

 const config = { root: null, // avoiding 'root' or setting it to 'null' sets it to default value: viewport rootMargin: '0px', threshold: 0.5 }; let observer = new IntersectionObserver(function(entries) { … }, config);

Şimdi, IntersectionObserver gözlemlenecek asıl öğeyi vermeliyiz. Bu, yalnızca öğeyi observe() işlevine geçirerek yapılır:

 … const img = document.getElementById('image-to-observe'); observer.observe(image);

Bu gözlemlenen öğe hakkında not edilmesi gereken birkaç şey:

  • Daha önce bahsedildi ama tekrar değinmekte fayda var: DOM'da bir element olarak root ayarladıysanız, gözlemlenen element root DOM ağacında yer almalıdır.
  • IntersectionObserver , bir seferde gözlem için yalnızca bir öğeyi kabul edebilir ve gözlemler için toplu tedariki desteklemez. Bu, birkaç öğeyi (bir sayfada birkaç resim diyelim) gözlemlemeniz gerekiyorsa, hepsini yinelemeniz ve her birini ayrı ayrı gözlemlemeniz gerektiği anlamına gelir:
 … const images = document.querySelectorAll('img'); images.forEach(image => { observer.observe(image); });
  • Observer yerindeyken bir sayfa yüklerken, IntersectionObserver geri çağrısının gözlemlenen tüm öğeler için aynı anda tetiklendiğini fark edebilirsiniz. Sağlanan konfigürasyona uymayanlar bile. “Pekala… gerçekten beklediğim gibi değil”, bunu ilk kez yaşarken olağan düşüncedir. Ancak burada kafanız karışmasın: bu, gözlenen öğelerin sayfa yüklenirken bir şekilde “yakalama çerçevesi” ile kesiştiği anlamına gelmez.
IntersectionObserver'ın tüm öğeler için aynı anda tetiklendiği DevTools'un ekran görüntüsü.
Kaydedildikten sonra gözlemlenen tüm öğeler için IntersectionObserver tetiklenir, ancak bu, hepsinin bizim 'yakalama çerçevemizle' kesiştiği anlamına gelmez.

Bunun anlamı, bu öğenin girişinin başlatıldığı ve şimdi IntersectionObserver tarafından kontrol edildiğidir. Ancak bu, geri arama işlevinize gereksiz gürültü ekleyebilir ve hangi öğelerin gerçekten "yakalama çerçevesi" ile kesiştiğini ve hangilerini hesaba katmamız gerekmediğini saptamak sizin sorumluluğunuzdadır. Bu saptamanın nasıl yapıldığını anlamak için, geri arama işlevimizin anatomisine biraz daha derine inelim ve bu tür girişlerin nelerden oluştuğuna bir göz atalım.

KavşakGözlemci Geri Arama

Her şeyden önce, bir IntersectionObserver için geri çağırma işlevi iki argüman alır ve bunlar hakkında ikinci argümandan başlayarak ters sırada konuşacağız. Yukarıda bahsedilen gözlemlenen girdiler Array ile birlikte, "yakalama çerçevemiz" ile kesişen geri arama işlevi, ikinci argüman olarak Gözlemcinin kendisini alır.

Gözlemcinin Kendisine Referans

 new IntersectionObserver(function(entries, SELF) {…});

Observer'ın kendisine referansı almak, IntersectionObserver tarafından ilk kez tespit edildikten sonra bazı elementleri gözlemlemeyi durdurmak istediğinizde birçok senaryoda kullanışlıdır. Görüntülerin geç yüklenmesi, diğer varlıkların ertelenmiş getirilmesi vb. gibi senaryolar bu türdendir. Bir öğeyi gözlemlemeyi durdurmak istediğinizde, IntersectionObserver , gözlemlenen öğe üzerinde bazı eylemler gerçekleştirdikten sonra (örneğin bir görüntünün gerçek tembel yüklenmesi gibi) geri çağırma işlevinde çalıştırılabilen bir unobserve(element-to-stop-observing) yöntemi sağlar. ).

Bu senaryolardan bazıları makalede daha ayrıntılı olarak incelenecektir, ancak bu ikinci argüman yolumuzdan çekilince, hadi bu geri çağırma oyununun ana aktörlerine geçelim.

KavşakGözlemciGirişi

 new IntersectionObserver(function(ENTRIES, self) {…});

Bir Array olarak geri çağırma fonksiyonumuzda aldığımız entries özel tiptedir: IntersectionObserverEntry . Bu arayüz bize, gözlemlenen her bir elemente ilişkin önceden tanımlanmış ve önceden hesaplanmış bir dizi özellik sağlar. En ilginçlerine bir göz atalım.

Her şeyden önce, IntersectionObserverEntry türündeki girişler, sürece dahil olan öğelerin koordinatlarını ve sınırlarını tanımlayan üç farklı dikdörtgen hakkında bilgi ile birlikte gelir:

  • rootBounds : “yakalama çerçevesi” için bir dikdörtgen ( root + rootMargin );
  • boundingClientRect : Gözlenen öğenin kendisi için bir dikdörtgen;
  • intersectionRect : Gözlenen öğeyle kesişen "yakalama çerçevesi" alanı.
IntersectionObserverEntry'nin Dikdörtgenleri
IntersectionObserverEntry'de yer alan tüm sınırlayıcı dikdörtgenler sizin için hesaplanır.

Bu dikdörtgenlerin bizim için asenkronize olarak hesaplanmasının gerçekten harika yanı, bize getBoundingClientRect() , offsetTop , offsetLeft ve diğer pahalı konumlandırma özelliklerini ve mizanpaj thrashing'i tetikleyen yöntemleri çağırmadan elemanın konumlandırmasıyla ilgili önemli bilgiler vermesidir. Performans için saf galibiyet!

IntersectionObserverEntry arayüzünün bizim için ilginç olan bir başka özelliği de isIntersecting . Bu, gözlenen elemanın şu anda "yakalama çerçevesi" ile kesişip kesişmediğini gösteren bir uygunluk özelliğidir. Elbette bu bilgiyi Dikdörtgen intersectionRect bakarak alabiliriz (eğer bu dikdörtgen 0×0 değilse, eleman “yakalama çerçevesi” ile kesişiyor) ama bunun önceden hesaplanması bizim için oldukça uygundur.

isIntersecting , gözlemlenen öğenin "yakalama çerçevesine" yeni girip girmediğini veya zaten onu terk edip etmediğini bulmak için kullanılabilir. Bunu bulmak için, bu özelliğin değerini bir genel bayrak olarak kaydedin ve bu öğe için yeni giriş geri arama işlevinize ulaştığında, bunun yeni olduğunu şu genel bayrakla isIntersecting karşılaştırın:

  • Eğer false idiyse ve şimdi true ise, o zaman eleman “yakalama çerçevesine” giriyordur;
  • Tersiyse ve daha önce true şimdi false , öğe "yakalama çerçevesini" terk ediyor demektir.

isIntersecting tam olarak daha önce tartıştığımız sorunu çözmemize yardımcı olan bir özelliktir, yani “yakalama çerçevesi” ile gerçekten kesişen öğeler için girişleri, yalnızca girişin başlatılması olan öğelerin gürültüsünden ayırın.

 let isLeaving = false; let observer = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting) { // we are ENTERING the "capturing frame". Set the flag. isLeaving = true; // Do something with entering entry } else if (isLeaving) { // we are EXITING the "capturing frame" isLeaving = false; // Do something with exiting entry } }); }, config);

NOT : Microsoft Edge 15'te isIntersecting özelliği uygulanmadı, aksi takdirde IntersectionObserver için tam desteğe rağmen undefined olarak döndürüldü. Bu, Temmuz 2017'de düzeltildi ve Edge 16'dan beri mevcut.

IntersectionObserverEntry arabirimi, önceden hesaplanmış bir kolaylık özelliği daha sağlar: intersectionRatio . Bu parametre isIntersecting ile aynı amaçlar için kullanılabilir ancak boole değeri yerine kayan noktalı bir sayı olduğu için daha ayrıntılı kontrol sağlar. kesişimRatio değeri, gözlemlenen öğenin alanının ne kadarının "yakalama çerçevesi" ile intersectionRatio gösterir ( intersectionRect alanının boundingClientRect alanına oranı). Yine, bu hesaplamayı o dikdörtgenlerden gelen bilgileri kullanarak kendimiz yapabilirdik, ama bizim için yapılması iyi oldu.

Zaten tanıdık gelmiyor mu? Evet, <code>intersectionRatio</code> özelliği, Observer'ın yapılandırmasının <code>threshold</code> özelliğine benzer. Aradaki fark, ikincisinin Observer'ı <em>ne zaman</em> çalıştıracağını tanımlamasıdır, ilkinin gerçek kesişimin durumunu belirtmesidir (Bu, Observer'ın asenkron doğası nedeniyle <code>eşik</code>'den biraz farklıdır).
Zaten tanıdık gelmiyor mu? Evet, intersectionRatio özelliği, Observer'ın yapılandırmasının threshold özelliğine benzer. Aradaki fark, ikincisinin Observer'ı * ne zaman* ateşleyeceğini tanımlamasıdır, ilki gerçek kesişimin durumunu belirtir (bu, Observer'ın asenkron doğası nedeniyle threshold biraz farklıdır).

target , IntersectionObserverEntry arabiriminin oldukça sık erişmeniz gerekebilecek bir özelliğidir. Ancak burada kesinlikle sihir yoktur – bu yalnızca Observer'ınızın observe() işlevine geçirilen orijinal öğedir. Tıpkı olaylarla çalışırken alıştığınız event.target gibi.

IntersectionObserverEntry arabiriminin özelliklerinin tam listesini almak için belirtimi kontrol edin.

Olası Uygulamalar

Bu makaleye büyük olasılıkla tam da bu bölüm yüzünden geldiğinizin farkındayım: Sonuçta kopyala ve yapıştır için kod parçacıklarımız varken mekanik kimin umurunda? Bu yüzden şimdi daha fazla tartışma ile sizi rahatsız etmeyeceğiz: kodlar ve örnekler diyarına giriyoruz. Umarım koda dahil edilen yorumlar işleri daha net hale getirir.

Ertelenmiş İşlevsellik

Öncelikle IntersectionObserver fikrinin altında yatan temel ilkeleri ortaya koyan bir örneği inceleyelim. Diyelim ki ekranda bir kez çok fazla hesaplama yapması gereken bir öğeniz var. Örneğin, reklamınız yalnızca bir kullanıcıya gerçekten gösterildiğinde bir görüntüleme kaydetmelidir. Ancak şimdi, sayfanızdaki ilk ekranın altında bir yerde otomatik olarak oynatılan bir dönen öğeniz olduğunu düşünelim.

Uygulamanızın ilk ekranının altındaki Döngü
Uygulamamızın alt kısmında bir atlıkarınca veya başka bir ağır kaldırma işlevi olduğunda, onu hemen önyüklemeye/yüklemeye başlamak kaynak israfıdır.

Bir atlıkarınca çalıştırmak, genel olarak ağır bir iştir. Genellikle, JavaScript zamanlayıcıları, öğeler arasında otomatik olarak gezinmek için hesaplamalar vb. içerir. Bu görevlerin tümü ana iş parçacığını yükler ve otomatik oynatma modunda yapıldığında, ana iş parçacığımızın bu isabeti ne zaman alacağını bilmek bizim için zordur. İlk ekranımızda içeriği önceliklendirmekten bahsettiğimizde ve İlk Anlamlı Boyama ve Etkileşim Zamanı'na bir an önce basmak istediğimizde, bloke edilen ana iş parçacığı performansımız için bir darboğaz haline geliyor.

Sorunu çözmek için, böyle bir atlıkarıncanın oynatılmasını, tarayıcının görüş alanına girene kadar erteleyebiliriz. Bu durumda, bilgimizi ve örneğimizi IntersectionObserverEntry arabiriminin isIntersecting parametresi için kullanacağız.

 const carousel = document.getElementById('carousel'); let isLeaving = false; let observer = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting) { isLeaving = true; entry.target.startCarousel(); } else if (isLeaving) { isLeaving = false; entry.target.stopCarousel(); } }); } observer.observe(carousel);

Burada, atlıkarıncayı yalnızca görüş alanımıza girdiğinde oynarız. IntersectionObserver başlatma işlemine iletilen config nesnesinin yokluğuna dikkat edin: bu, varsayılan yapılandırma seçeneklerine güvendiğimiz anlamına gelir. Karusel görüş alanımızdan çıktığında, artık önemli olmayan unsurlara kaynak harcamamak için oynamayı bırakmalıyız.

Varlıkların Tembel Yüklenmesi

Bu muhtemelen IntersectionObserver için en belirgin kullanım durumudur: Kullanıcının şu anda ihtiyaç duymadığı bir şeyi indirmek için kaynak harcamak istemiyoruz. Bu, kullanıcılarınıza büyük bir fayda sağlayacaktır: kullanıcıların indirme yapmasına gerek kalmayacak ve mobil cihazlarının, şu anda ihtiyaç duymadıkları pek çok işe yaramaz bilgiyi ayrıştırıp derlemesi gerekmeyecek. Şaşırtıcı olmayan bir şekilde, uygulamanızın performansına da yardımcı olacaktır.

Ekranın altında tembel yüklenen resimler
İlk ekranın altında bulunan görüntüler gibi tembel yüklenen varlıklar – IntersectionObserver'ın en belirgin uygulaması.

Daha önce, indirme ve işleme kaynaklarını kullanıcının ekrana getirebileceği ana kadar ertelemek için, scroll gibi olaylarda olay dinleyicileriyle uğraşıyorduk. Sorun açık: Bu, dinleyicileri çok sık tetikledi. Bu nedenle, geri aramanın yürütülmesini azaltma veya geri çağırma fikrini bulmamız gerekiyordu. Ancak tüm bunlar, en çok ihtiyaç duyduğumuz anda onu engelleyen ana iş parçacığımız üzerinde çok fazla baskı yarattı.

Öyleyse, tembel yükleme senaryosunda IntersectionObserver geri dönersek, nelere dikkat etmeliyiz? Tembel yüklenen görüntülerin basit bir örneğini kontrol edelim.

CodePen'de Denys Mishunov (@mishunov) tarafından IntersectionObserver'da Pen Lazy yüklemesine bakın.

CodePen'de Denys Mishunov (@mishunov) tarafından IntersectionObserver'da Pen Lazy yüklemesine bakın.

Bu sayfayı yavaşça "üçüncü ekrana" kaydırmayı deneyin ve sağ üst köşedeki izleme penceresini izleyin: o ana kadar kaç görüntünün indirildiğini size bildirecektir.

Bu görev için HTML işaretlemesinin özünde basit bir resim dizisi bulunur:

 … <img data-src="https://blah-blah.com/foo.jpg"> …

Gördüğünüz gibi, görüntüler src etiketleri olmadan gelmelidir: bir tarayıcı src özniteliğini gördüğünde, niyetimizin tam tersi olan o görüntüyü hemen indirmeye başlayacaktır. Bu nedenle, HTML'deki resimlerimize bu niteliği koymamalıyız ve bunun yerine, burada data-src gibi bazı data- -özniteliklerine güvenebiliriz.

Bu çözümün bir başka parçası da elbette JavaScript'tir. Buradaki ana bitlere odaklanalım:

 const images = document.querySelectorAll('[data-src]'); const config = { … }; let observer = new IntersectionObserver(function (entries, self) { entries.forEach(entry => { if (entry.isIntersecting) { … } }); }, config); images.forEach(image => { observer.observe(image); });

Yapısal olarak, burada yeni bir şey yok: bunların hepsini daha önce ele aldık:

  • Tüm mesajları data-src özniteliklerimizle alıyoruz;
  • Set config : bu senaryo için, görünümün altından biraz daha düşük öğeleri algılamak için “yakalama çerçevenizi” genişletmek istiyorsunuz;
  • IntersectionObserver bu yapılandırmayla kaydedin;
  • Görüntülerimizi yineleyin ve hepsini bu IntersectionObserver tarafından gözlemlenecek şekilde ekleyin;

İlginç kısım, girişlerde çağrılan geri arama işlevinde gerçekleşir. İlgili üç temel adım vardır.

  1. Her şeyden önce, yalnızca “yakalama çerçevemizle” gerçekten kesişen öğeleri işliyoruz. Bu snippet'in şimdiye kadar size aşina olması gerekir.

     entries.forEach(entry => { if (entry.isIntersecting) { … } });

  2. Ardından, data-src ile imajımızı gerçek bir <img src="…"> haline dönüştürerek girişi bir şekilde işleriz.

     if (entry.isIntersecting) { preloadImage(entry.target); … }
    Bu, tarayıcıyı nihayet görüntüyü indirmesi için tetikleyecektir. preloadImage() burada bahsedilmeye değmeyen çok basit bir fonksiyondur. Sadece kaynağı oku.

  3. Sonraki ve son adım: Geç yükleme tek seferlik bir işlem olduğundan ve öğe "yakalama çerçevemize" her girdiğinde görüntüyü indirmemiz gerekmediğinden, önceden işlenmiş görüntüyü unobserve . Kodumuzdaki bellek sızıntılarını önlemek için artık gerekli olmadığında normal etkinliklerimiz için bunu element.removeEventListener() ile yapmamız gerektiği gibi.

     if (entry.isIntersecting) { preloadImage(entry.target); // Observer has been passed as self to our callback self.unobserve(entry.target); }

Not. unobserve(event.target) yerine connect( disconnect() de çağırabiliriz: IntersectionObserver bağlantısını tamamen keser ve artık görüntüleri gözlemlemez. Önem verdiğiniz tek şey Observer'ınız için ilk isabetse bu kullanışlıdır. Bizim durumumuzda, görüntüleri izlemeye devam etmek için Observer'a ihtiyacımız var, bu yüzden henüz bağlantıyı kesmemeliyiz.

Örneği çatallamaktan ve farklı ayarlar ve seçeneklerle oynamaktan çekinmeyin. Özellikle görüntüleri tembelce yüklemek istediğinizde bahsetmek için ilginç bir şey var. Gözlenen öğe tarafından oluşturulan kutuyu her zaman aklınızda tutmalısınız! Örneği kontrol ederseniz, 41-47 satırlarındaki resimler için CSS'nin sözde gereksiz stiller içerdiğini fark edeceksiniz. min-height: 100px . Bu, görüntü yer tutucularına ( src özniteliği olmadan <img> ) bir miktar dikey boyut vermek için yapılır. Ne için?

  • Dikey boyutlar olmadan, tüm <img> etiketleri 0×0 kutusu oluşturur;
  • <img> etiketi varsayılan olarak bir tür inline-block kutusu oluşturduğundan, bu 0×0 kutularının tümü aynı satırda yan yana hizalanır;
  • Bu, IntersectionObserver tüm (veya ne kadar hızlı kaydırdığınıza bağlı olarak, neredeyse tüm) görüntüleri bir kerede kaydedeceği anlamına gelir - muhtemelen tam olarak elde etmek istediğiniz şey değil.

Geçerli Bölümün Vurgulanması

IntersectionObserver , elbette tembel yüklemeden çok daha fazlasıdır. İşte scroll olayını bu teknolojiyle değiştirmenin başka bir örneği. Bunda oldukça yaygın bir senaryomuz var: sabit gezinme çubuğunda, belgenin kaydırma konumuna göre geçerli bölümü vurgulamalıyız.

CodePen'de Denys Mishunov (@mishunov) tarafından hazırlanan IntersectionObserver'daki Kalem Vurgulama geçerli bölümüne bakın.

CodePen'de Denys Mishunov (@mishunov) tarafından hazırlanan IntersectionObserver'daki Kalem Vurgulama geçerli bölümüne bakın.

Yapısal olarak, geç yüklenen görüntüler örneğine benzer ve aşağıdaki istisnalar dışında aynı temel yapıya sahiptir:

  • Şimdi görüntüleri değil, sayfadaki bölümleri gözlemlemek istiyoruz;
  • Açıkçası, geri aramamızdaki girişleri işlemek için farklı bir işlevimiz de var ( intersectionHandler(entry) ). Ancak bu ilginç değil: tek yaptığı CSS sınıfını değiştirmek.

Burada ilginç olan config nesnesidir:

 const config = { rootMargin: '-50px 0px -55% 0px' };

Neden rootMargin için varsayılan 0px değeri olmasın, soruyorsunuz? Eh, çünkü mevcut bölümü vurgulamak ve bir görüntüyü tembelce yüklemek, elde etmeye çalıştığımız şeyde oldukça farklı. Tembel yükleme ile, görüntü görünüme girmeden yüklemeye başlamak istiyoruz. Bu amaçla, "yakalama çerçevemizi" altta 50 piksel genişlettik. Aksine, mevcut bölümü vurgulamak istediğimizde, bölümün gerçekten ekranda göründüğünden emin olmalıyız. Ve sadece bu değil: kullanıcının aslında tam olarak bu bölümü okuduğundan veya okuyacağından emin olmalıyız. Bu nedenle, aktif bölüm olarak ilan etmeden önce bir bölümün alttan görünümün yarısından biraz daha fazla gitmesini istiyoruz. Ayrıca, gezinme çubuğunun yüksekliğini hesaba katmak istiyoruz ve bu nedenle çubuğun yüksekliğini "yakalama çerçevesinden" kaldırıyoruz.

Geçerli bölüm için kare yakalama
Observer'ın yalnızca üstten 50 piksel ve alttan görünümün %55'i arasındaki 'yakalama çerçevesine' giren öğeleri algılamasını istiyoruz.

Ayrıca, mevcut gezinme öğesinin vurgulanması durumunda, hiçbir şeyi gözlemlemeyi bırakmak istemediğimizi unutmayın . Burada IntersectionObserver her zaman sorumlu tutmalıyız, dolayısıyla burada ne disconnect() ne de unobserve() bulamazsınız.

Özet

IntersectionObserver çok basit bir teknolojidir. Modern tarayıcılarda oldukça iyi bir desteği var ve bunu hala (veya hiç desteklemeyen) tarayıcılar için uygulamak istiyorsanız, elbette bunun için bir çoklu dolgu var. Ancak sonuçta, bu, gerçekten iyi bir performans artışı elde etmeye yardımcı olurken, bir görünüm penceresindeki öğeleri algılamayla ilgili her türlü şeyi yapmamızı sağlayan harika bir teknolojidir.

IntersectionObserver Neden Sizin İçin İyi?

  • IntersectionObserver , zaman uyumsuz, engellemeyen bir API'dir!
  • IntersectionObserver , scroll veya resize olaylarında pahalı dinleyicilerimizin yerini alır.
  • IntersectionObserver , getClientBoundingRect() gibi tüm pahalı hesaplamaları sizin için yapar, böylece sizin yapmanıza gerek kalmaz.
  • IntersectionObserver , oradaki diğer Observer'ların yapısal modelini takip eder, bu nedenle, teorik olarak, diğer Observer'ların nasıl çalıştığına aşina iseniz, anlaşılması kolay olmalıdır.

Akılda Tutulması Gerekenler

IntersectionObserver'ın yeteneklerini, her şeyin geldiği yerden window.addEventListener('scroll') dünyası ile karşılaştırırsak, bu Observer'da herhangi bir eksilerini görmek zor olacaktır. Öyleyse, bunun yerine akılda tutulması gereken bazı şeyleri not edelim:

  • Evet, IntersectionObserver , zaman uyumsuz, engellemeyen bir API'dir. Bunu bilmek harika! Ancak, geri aramalarınızda çalıştırdığınız kodun, API'nin kendisi zaman uyumsuz olsa bile, varsayılan olarak zaman uyumsuz olarak çalıştırılmayacağını anlamak daha da önemlidir. Bu nedenle, geri çağırma işlevinizin hesaplamaları ana iş parçacığını yanıtsız hale getirirse, IntersectionObserver elde ettiğiniz tüm faydaları ortadan kaldırma şansınız hala vardır. Ama bu farklı bir hikaye.
  • Varlıkları (örneğin resimler gibi) tembelce yüklemek için IntersectionObserver kullanıyorsanız, varlık yüklendikten sonra .unobserve(asset) çalıştırın.
  • IntersectionObserver , yalnızca belgenin biçimlendirme yapısında görünen öğeler için kesişmeleri algılayabilir. Açıklığa kavuşturmak için: gözlemlenebilir öğeler bir kutu oluşturmalı ve bir şekilde düzeni etkilemelidir. İşte size daha iyi bir anlayış sağlamak için bazı örnekler:

    • Gösterilen öğeler display: none söz konusu değildir;
    • opacity: 0 veya visibility:hidden kutuyu oluşturur (görünmez olsa bile) böylece bunlar algılanır;
    • width:0px; height:0px width:0px; height:0px iyidir. Though, it has to be noted that absolutely positioned elements fully positioned outside of parent's borders (with negative margins or negative top , left , etc.) and are cut out by parent's overflow: hidden won't be detected: their box is out of scope for the formatting structure.
IntersectionObserver: Now You See Me
IntersectionObserver: Now You See Me

I know it was a long article, but if you're still around, here are some links for you to get an even better understanding and different perspectives on the Intersection Observer API:

  • Intersection Observer API on MDN;
  • IntersectionObserver polyfill;
  • IntersectionObserver polyfill as npm module;
  • Lazy-Loading Images with IntersectionObserver [video] by amazing Paul Lewis;
  • Basic and short (just 01:39), but very informative introduction to IntersectionObserver [video] by Surma.

With this, I would like to make a pause in our discussion to give you an opportunity to play with this technology and realize all of its convenience. So, go play with it. The article is finally over. This time I really mean it.