Programatik olarak bir bölüme veya öğeye kaydırırken (özellikle behavior: 'smooth' ile) hemen bir takip işlemi tetiklemek (ör. vurgulama durumunu yanıp söndürme, giriş animasyonu başlatma veya odağı taşıma), hedef henüz ekran dışındayken veya kaydırma işlemi devam ederken efektin çalışmasına neden olur.
Daha önce geliştiriciler, setTimeout() ile kaydırma süresini tahmin etmek (uzun kaydırmalarda çok erken, kısa kaydırmalarda ise çok geç tetiklenir) veya bir scrollend etkinlik işleyiciyi yedek zamanlayıcıyla manuel olarak Promise içine sarmak (scrollend, hedef zaten görünür durumdayken ve kaydırma yapılmadığında tetiklenmediği için) zorunda kalıyordu.
Element (scrollIntoView(), scroll(), scrollTo(), scrollBy()) ve Window (scroll(), scrollTo(), scrollBy()) üzerindeki programatik kaydırma yöntemleri, kaydırma işlemi tamamlandığında çözümlenen bir Promise döndürür. Hedef zaten istenen kaydırma konumundaysa ve kaydırma gerekmiyorsa döndürülen Promise, askıda kalmadan hemen çözümlenir.
Uygulama
- İşleyiciyi
asyncolarak işaretleyin: Kaydırmayı tetikleyen etkinlik işleyici veya gezinme işleyicisi içinasyncişlevini kullanın. - Doğrudan kaydırma yöntemini bekleme:
await element.scrollIntoView({ behavior: 'smooth', ... })(veyaawait element.scrollTo(...),await window.scrollTo(...)vb.) yöntemini çağırın. Tarayıcı, kaydırma animasyonunu tamamlayıp kaydırma konumu sabitlenene kadar yürütme duraklatılır. - Kaydırma sonrası efekti uygulama:
awaitifadesi tamamlandıktan sonra vurgulama sınıfını açıp kapatın, animasyonu başlatın veya odağı taşıyın.
Kesintiye Uğrayan Kaydırmalar ve Odak Yönetimi
- Hızlı/kesintili kaydırmaları işleme: Devam eden bir kolay kaydırma işlemi tamamlanmadan önce aynı kaydırma kapsayıcısında yeni bir kaydırma işlemi başlarsa tarayıcı, devam eden kolay kaydırma işlemini iptal eder ve bu kapsayıcıdaki tüm bekleyen kaydırma vaatlerini çözer (reddetmez). Kullanıcı hızlıca tıkladığında, değiştirilen kaydırmalar için kaydırma sonrası efekti engellemeniz gerekiyorsa artan bir işlem kimliğini izleyin ve daha yeni bir kaydırma başlatıldıysa
awaitişleminden sonra erken dönün:
let currentScrollId = 0;
const sections = document.querySelectorAll('section');
async function scrollToAndHighlight(targetElement) {
const scrollId = ++currentScrollId;
sections.forEach((sec) => sec.classList.remove('highlight'));
await targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
// Abort follow-up effect if another scroll was triggered while awaiting
if (scrollId !== currentScrollId) return;
targetElement.classList.add('highlight');
}
- Kaydırdıktan sonra hedefe odaklanma:
await targetElement.scrollIntoView(...)işleminden sonra klavye odağını hedef öğeye (veyatabindex="-1"ile içindeki bir başlığa) taşırken{ preventScroll: true }değerinifocus()öğesine iletin. Böylece odak çağrısı ikinci bir rakip kaydırma işlemi başlatmaz:
await targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
targetHeading.focus({ preventScroll: true });
Kısıtlamalar ve En İyi Uygulamalar
- ZORUNLU:
setTimeout()ile kaydırma sürelerini tahmin etmek yerine doğrudanscrollIntoView(),scroll(),scrollTo()veyascrollBy()tarafından döndürülenPromisedeğerini bekleyin. - Birincil uygulamanız olarak
scrollendöğesini manuel birnew Promise()içine SARMAYIN. Hedef zaten görünür durumdayken ve kaydırma mesafesi kat edilmediğindescrollendtetiklenmez. Kaydırma yöntemi ise bu durumda otomatik olarak çözümlenir. - DO: Bir kaydırma yöntemini bekledikten sonra
.focus()yöntemini çağırırken{ preventScroll: true }değerini iletin. Böylece tarayıcı ikinci kez atlamaz veya kaydırmaz. - DO: Guard post-scroll effects with a request token if rapid user interactions can trigger overlapping smooth scrolls on the same container, since interrupting a smooth scroll resolves (rather than rejects) the previous promise.
- Kök kaydırıcıyla ilgili bilinen Chrome hatası: Chrome'da, dokümanın kök kaydırıcısı kaydırılırken
scrollIntoView()tarafından döndürülenPromiseçok erken çözümlenir. İç içe yerleştirilmiş bir kaydırma kapsayıcısındascrollIntoView()beklenmesi, beklendiği gibi çalışır. Bu hatayı düzeltmek için içeriğinizidivveoverflow-y: autogibi bir kaydırma kapsayıcısına sarmayın.
Yedek stratejiler
Kaydırma yöntemi vaatlerini desteklemeyen tarayıcılarda scrollIntoView(), scroll(), scrollTo() ve scrollBy(), Promise yerine undefined döndürür.
- Progresif geliştirme (varsayılan
awaitdavranışı):await undefined, bir sonraki mikro görevde hemen çözümlenen geçerli bir JavaScript olduğundanawait element.scrollIntoView({ behavior: 'smooth' })yazmak, desteklenmeyen tarayıcılarda hata oluşturmaz. Bunun yerine, kaydırma işlemi başlar başlamaz kaydırma sonrası kodu çalıştırarak sorunsuz bir şekilde çalışır. Geçici vurgulama gibi kritik olmayan görsel süslemeler için bu genellikle yeterlidir. - Güvenilir yedek (
scrollend+ zaman aşımı): Baseline hedefinizscroll-promisesözelliğini desteklemiyorsa ve kaydırma sonrası işlemin eski tarayıcılarda kaydırma işlemi tamamlanana kadar beklemesi gerekiyorsa döndürülen değerinPromise(result instanceof Promise) olup olmadığını kontrol edin.undefinedolduğunda, öğe zaten görünümdeyse ve kaydırma işlemi gerçekleşmiyorsa kısa bir yedek zaman aşımı ile kaydırma kapsayıcısındascrollendetkinliğini bekleyin:
function scrollIntoViewAsync(element, options, scroller = window) {
const result = element.scrollIntoView(options);
// Feature detect: in supporting browsers, scrollIntoView() returns a Promise
if (result instanceof Promise) {
return result;
}
// Fallback for browsers where scrollIntoView() returns undefined
return new Promise((resolve) => {
let timeoutId;
const finish = () => {
clearTimeout(timeoutId);
scroller.removeEventListener('scrollend', finish);
scroller.removeEventListener('scroll', onScroll);
resolve();
};
const onScroll = () => {
// Restart the idle timer on every scroll event. If scrollend never fires
// (browsers without scrollend support), this resolves once scrolling goes idle.
clearTimeout(timeoutId);
timeoutId = setTimeout(finish, 100);
};
scroller.addEventListener('scrollend', finish, { once: true });
scroller.addEventListener('scroll', onScroll, { passive: true });
// If no scroll event fires within the idle window (e.g., already in view), resolve.
// 100ms is an example value; it only needs to exceed the gap between scroll events.
timeoutId = setTimeout(finish, 100);
});
}