Bootstrap Scrollspy, sayfa ya da bir kutu kaydırıldıkça hangi bölümün ekranda olduğunu izler ve menüdeki ilgili bağlantıya otomatik olarak .active sınıfını ekler. Kullanmak için kaydırılan öğeye (çoğunlukla <body>) data-bs-spy="scroll" ve menünün kimliğini gösteren data-bs-target="#menu" verirsiniz; menü bir Bootstrap .nav ya da .list-group olmalı, her bağlantının href değeri de sayfadaki bir bölümün id değerine karşılık gelmelidir. İzlenen öğe position: relative almalıdır.

Bu yazıda Scrollspy’ın çalışma şartlarını, navbar, iç içe nav ve list-group ile kullanımını, kaydırılabilir bir kutu içinde izlemeyi, offset ve method seçeneklerini, refresh() yöntemini ve activate.bs.scrollspy olayını belge ve kaynak koda dayanarak anlatıyoruz. İçerik Bootstrap 5.0 belgelerine ve v5.0.2 kaynak koduna dayanır; sonraki 5.x sürümlerinde Scrollspy’ın davranışı değişmiş olabilir, bu nedenle kullandığınız sürümün belgesini getbootstrap.com üzerinde kontrol edin. Diğer konular için Bootstrap öğrenme rehberine bakabilirsiniz.

Scrollspy’ın çalışma şartları

Scrollspy’ın çalışma şartları

Scrollspy’ın düzgün çalışması için belge üç temel şart sayar. Birincisi, menü bir Bootstrap Bootstrap 5.0: Navs and tabs bileşeni ya da Bootstrap 5.0: List group olmalıdır. Kaynak kod menünün içinde yalnızca .nav-link, .list-group-item ve .dropdown-item sınıflı öğeleri arar; sıradan bir <a> listesi işaretlenmez. İkincisi, izlenen öğe, genellikle <body>, position: relative almalıdır. Üçüncüsü, bağlantılar <a> olmalı ve bir id değerini göstermelidir.

Bu şartlar sağlandığında menü, kaydırma sürdükçe .active sınıfını bir öğeden ötekine taşır. Belge iki önemli ayrıntıyı ayrıca vurgular: bağlantıların hedefleri çözülebilir olmalıdır, yani <a href="#iletisim"> varsa sayfada mutlaka id="iletisim" taşıyan bir öğe bulunmalıdır. Görünür olmayan hedef öğeler ise yok sayılır ve onlara karşılık gelen menü öğeleri hiçbir zaman vurgulanmaz. Kaynak kodda bu, hedefin genişliği ya da yüksekliği sıfırsa listeye alınmaması biçiminde uygulanır; örneğin .d-none ile gizlenmiş bir bölüm Scrollspy için yoktur.

İç içe nav ile Scrollspy

Scrollspy iç içe yerleştirilmiş .nav menülerinde de çalışır. Belgeye göre iç menüdeki bir öğe .active olduğunda, onu içeren üst menü öğesi de .active olur. Böylece “Bölüm 1” altındaki “1.1” etkinken hem “1.1” hem “Bölüm 1” vurgulanır. Dikey bir içindekiler menüsü için .flex-column kullanılır, alt bağlantılara da girinti için .ms-3 verilir:

html
<div class="row">
  <div class="col-4">
    <nav id="icindekiler" class="navbar navbar-light bg-light flex-column align-items-stretch p-3">
      <a class="navbar-brand" href="#">İçindekiler</a>
      <nav class="nav nav-pills flex-column">
        <a class="nav-link" href="#bolum-1">Bölüm 1</a>
        <nav class="nav nav-pills flex-column">
          <a class="nav-link ms-3 my-1" href="#bolum-1-1">1.1 Giriş</a>
          <a class="nav-link ms-3 my-1" href="#bolum-1-2">1.2 Kavramlar</a>
        </nav>
        <a class="nav-link" href="#bolum-2">Bölüm 2</a>
      </nav>
    </nav>
  </div>
  <div class="col-8">
    <div data-bs-spy="scroll" data-bs-target="#icindekiler" data-bs-offset="0" class="position-relative" style="height: 350px; overflow-y: scroll;" tabindex="0">
      <h4 id="bolum-1">Bölüm 1</h4>
      <p>Bölümün genel tanıtımı.</p>
      <h5 id="bolum-1-1">1.1 Giriş</h5>
      <p>Giriş metni.</p>
      <h5 id="bolum-1-2">1.2 Kavramlar</h5>
      <p>Temel kavramlar.</p>
      <h4 id="bolum-2">Bölüm 2</h4>
      <p>İkinci bölüm.</p>
    </div>
  </div>
</div>

Kaynak kod üst öğeleri bulurken bağlantının hangi .nav ya da .list-group öğelerinin içinde olduğuna bakar ve her birinin hemen önündeki kardeş bağlantıyı işaretler. Bu yüzden alt menünün, işaretlenecek üst bağlantıdan hemen sonra gelmesi önemlidir; yukarıdaki yapıda “Bölüm 1” bağlantısından sonra alt <nav> gelir. Bağlantılar bir .nav-item içinde ise o öğenin içindeki .nav-link işaretlenir. Grid yapısı için Bootstrap grid yazısına bakabilirsiniz.

List group ile Scrollspy

List group ile Scrollspy

Menü olarak list-group bileşenini de kullanabilirsiniz. Bağlantılara .list-group-item ve fare ile üzerine gelindiğinde tepki vermesi için .list-group-item-action sınıflarını verirsiniz. Scrollspy etkin bölümün bağlantısına .active ekler ve list group bu öğeyi tema rengiyle vurgular:

html
<div class="row">
  <div class="col-4">
    <div id="adimListesi" class="list-group">
      <a class="list-group-item list-group-item-action" href="#adim-1">1. Hesap oluşturun</a>
      <a class="list-group-item list-group-item-action" href="#adim-2">2. Alan adını bağlayın</a>
      <a class="list-group-item list-group-item-action" href="#adim-3">3. Dosyaları yükleyin</a>
    </div>
  </div>
  <div class="col-8">
    <div data-bs-spy="scroll" data-bs-target="#adimListesi" data-bs-offset="0" class="position-relative" style="height: 200px; overflow-y: scroll;" tabindex="0">
      <h4 id="adim-1">1. Hesap oluşturun</h4>
      <p>Hesap oluşturma adımları.</p>
      <h4 id="adim-2">2. Alan adını bağlayın</h4>
      <p>Alan adı ayarları.</p>
      <h4 id="adim-3">3. Dosyaları yükleyin</h4>
      <p>Dosya yükleme yöntemleri.</p>
    </div>
  </div>
</div>

Bu düzen dokümantasyon sayfaları, uzun kurulum kılavuzları ve tek sayfalık ürün tanıtımları için iyi çalışır. Menünün kaydırma sırasında görünür kalmasını istiyorsanız menüyü .sticky-top ile yapışkan yapabilirsiniz; bu sınıfın davranışı ve tarayıcı sınırları Bootstrap position yazısında anlatılır.

Bütün sayfayı izlemek: body üzerinde Scrollspy

Tek sayfalık sitelerde genellikle kutu değil, sayfanın kendisi kaydırılır. Bu durumda data-bs-spy="scroll" özniteliğini <body> öğesine verirsiniz ve <body> için CSS’te position: relative tanımlarsınız. Belgedeki örneğin uyarlaması, konumu yardımcı sınıfla veren biçimiyle şöyledir:

html
<body class="position-relative" data-bs-spy="scroll" data-bs-target="#sayfaMenu" data-bs-offset="70">
  <nav id="sayfaMenu" class="navbar navbar-light bg-light fixed-top px-3">
    <ul class="nav nav-pills">
      <li class="nav-item"><a class="nav-link" href="#hizmetler">Hizmetler</a></li>
      <li class="nav-item"><a class="nav-link" href="#fiyatlar">Fiyatlar</a></li>
      <li class="nav-item"><a class="nav-link" href="#iletisim">İletişim</a></li>
    </ul>
  </nav>
  <section id="hizmetler">...</section>
  <section id="fiyatlar">...</section>
  <section id="iletisim">...</section>
</body>

Sabit (.fixed-top) bir menü kullandığınızda menü sayfanın üst kısmını örter. data-bs-offset değeri, kaydırma konumu hesaplanırken üstten kaç piksel pay bırakılacağını belirler; burada menü yüksekliğine yakın bir değer vererek bir bölüm menünün altına girdiği anda değil, görünür alana geldiğinde etkinleşmesini sağlarsınız. Örnekteki 70 değeri bizim seçtiğimiz bir örnek değerdir, kendi menünüzün yüksekliğine göre ayarlamalısınız. Varsayılan değer 10’dur.

Kaynak kodda Scrollspy, kaydırma dinleyicisini izlenen öğeye bağlar; izlenen öğe <body> ise dinleyici window üzerine kurulur, çünkü sayfanın kaydırması pencerede gerçekleşir. Kaydırma miktarı da bu durumda pencereden okunur. Bu ayrıntı aşağıda anlatılan olay dinleme biçimini doğrudan etkiler. dispose() çağrıldığında bu kaydırma dinleyicisi kaldırılır ve öğedeki örnek verisi silinir; menüyü sayfadan kaldıracak ya da başka bir hedefle yeniden kuracaksanız önce bu yöntemi çağırmak, eski dinleyicinin boşuna çalışmasını önler. Tek sayfalık sitelerde sabit menü yerine yapışkan bir kenar menüsü kullanıyorsanız mantık aynıdır: menünün kendisi kaymaz, izlenen öğe pencere olur ve etkin bağlantı kaydırma sürdükçe değişir.

Etkin bölüm nasıl seçilir: hesaplamanın mantığı

Scrollspy’ın hangi bağlantıyı neden işaretlediğini anlamak, beklenmedik sonuçları çözmeyi kolaylaştırır. Bootstrap v5.0.2 kaynak kodu: scrollspy.js dosyasındaki işleyiş kabaca şöyledir. Başlangıçta ve her refresh() çağrısında, menüdeki her bağlantının gösterdiği hedef bulunur, hedefin üst kenarının konumu ölçülür ve bu konumlar küçükten büyüğe sıralanır. Görünmeyen ya da sayfada bulunmayan hedefler listeye girmez.

Her kaydırma olayında, mevcut kaydırma miktarına offset değeri eklenir. Ardından bu değerin hangi iki hedef konumunun arasına düştüğüne bakılır: üst kenarı bu değere ulaşmış olan son bölüm etkin sayılır. Sayfa henüz ilk bölümün üstündeyken, ilk bölüm sayfanın en başında değilse hiçbir bağlantı etkin olmaz ve önceki işaretler temizlenir. Kaydırma ulaşılabilecek en alt noktaya geldiğinde ise, son bölüm kısa olup üst kenarı hiç o noktaya çıkamasa bile son bağlantı etkinleştirilir. Bu sonuncu kural, sayfanın en altındaki kısa bir “İletişim” bölümünün de menüde vurgulanabilmesini sağlar.

Küçük bir örnekle düşünelim: üç bölümün üst kenarları 0, 600 ve 1400 piksel konumunda olsun ve offset varsayılan değer olan 10 kalsın. Kullanıcı 595 piksel kaydırdığında hesaplanan değer 605 olur; 600’ü geçtiği için ikinci bölüm etkinleşir. Yani bir bölüm, üst kenarı görünür alanın tepesine 10 piksel kala etkin sayılır. Sabit bir menü 70 piksel yüksekliğindeyse, bölüm başlığı menünün altına girmeden önce etkinleşmesi için offset değerini buna yakın bir sayıya çıkarırsınız. Buradaki piksel değerleri yalnızca hesaplamayı göstermek için seçilmiş örnek değerlerdir.

Scrollspy menü öğelerine yalnızca .active sınıfı ekler, içerik göstermez ya da gizlemez. İçerik panelleri arasında geçiş yapan sekmeler istiyorsanız aradığınız bileşen Tab eklentisidir; nav tabs ve pills yazısı bu konuyu anlatır.

JavaScript ile başlatma ve seçenekler

JavaScript ile başlatma ve seçenekler: offset, method, target

Veri öznitelikleri yerine JavaScript tercih ediyorsanız, CSS’te position: relative verdikten sonra Scrollspy’ı şöyle başlatırsınız:

javascript
var scrollSpy = new bootstrap.ScrollSpy(document.body, {
  target: '#navbar-example'
})

Veri özniteliği ile başlatılan Scrollspy örnekleri, kaynak koda göre pencerenin load olayında oluşturulur; yani sayfadaki görseller ve diğer kaynaklar yüklenip bölümlerin konumları netleştikten sonra devreye girer. Seçenekler veri öznitelikleriyle de verilebilir; seçenek adının başına data-bs- eklenir:

SeçenekTürVarsayılanNe yapar
offsetnumber10Kaydırma konumu hesaplanırken üstten bırakılacak piksel payı
methodstringautoBölüm konumlarının nasıl hesaplanacağı: auto, offset veya position
targetstring, jQuery nesnesi veya DOM öğesiboşİzlenecek menünün seçicisi ya da öğesi

method seçeneğinde offset, konumları getBoundingClientRect() ile; position ise offsetTop ve offsetLeft özellikleriyle hesaplar. auto en uygun yöntemi kendisi seçer: kaynak kodda izlenen öğe pencere ise offset, bir kutu ise position kullanılır. Çoğu durumda varsayılanı değiştirmeniz gerekmez.

refresh() yöntemi ve activate olayı

Scrollspy bölümlerin konumlarını başlangıçta hesaplar. Sayfaya sonradan bölüm eklediğinizde ya da sildiğinizde, örneğin sonsuz kaydırmalı bir listede veya bir akordeon açıldığında, konumların yeniden hesaplanması için refresh() çağırmanız gerekir. Belgedeki kalıp, sayfadaki bütün Scrollspy örneklerini bulup yeniler:

javascript
var dataSpyList = [].slice.call(document.querySelectorAll('[data-bs-spy="scroll"]'))
dataSpyList.forEach(function (dataSpyEl) {
  bootstrap.ScrollSpy.getInstance(dataSpyEl)
    .refresh()
})

Kaynak kod her kaydırma olayında içerik yüksekliğinin değişip değişmediğine de bakar ve değiştiyse refresh() işlemini kendisi yapar; ancak yükseklik aynı kalıp bölümlerin yeri değiştiyse bu kontrol yetmez, bu yüzden DOM değişikliklerinden sonra yöntemi açıkça çağırmak güvenli yoldur. Diğer yöntemler dispose() (öğedeki Scrollspy verisini siler), getInstance() ve getOrCreateInstance() statik yöntemleridir.

Yeni bir menü öğesi her etkinleştiğinde activate.bs.scrollspy olayı tetiklenir. Belgeye göre olay kaydırılan öğe üzerinde yayınlanır. Kaynak koda bakınca önemli bir ayrıntı görülür: izlenen öğe <body> ise kaydırma öğesi olarak window kullanılır ve olay da window üzerinde tetiklenir. Olay nesnesindeki relatedTarget özelliği etkinleşen hedefin seçicisini (örneğin #fiyatlar) taşır:

javascript
// Kaydirilabilir kutu izleniyorsa
var kutu = document.querySelector('[data-bs-spy="scroll"]')
kutu.addEventListener('activate.bs.scrollspy', function (event) {
  console.log('Etkin bolum:', event.relatedTarget)
})

// body izleniyorsa olay window uzerinde tetiklenir
window.addEventListener('activate.bs.scrollspy', function (event) {
  document.title = 'Okunan bolum: ' + event.relatedTarget
})

Bu olayla örneğin adres çubuğunu güncelleyebilir, bir ilerleme göstergesini ilerletebilir ya da analitik amaçlı hangi bölümün okunduğunu kaydedebilirsiniz. Olay modelinin genel kuralları için Bootstrap JavaScript API yazısına bakın.

Sık yapılan hatalar

  • position: relative vermemek: İzlenen öğe göreli konumlu değilse konum hesapları yanlış çıkar ve yanlış bağlantı etkinleşir.
  • Kutuya yükseklik vermemek: Kendi içinde kaymayan bir kutuda Scrollspy’ın izleyeceği bir kaydırma yoktur.
  • Menüde Bootstrap sınıfı kullanmamak: Bağlantılar .nav-link, .list-group-item ya da .dropdown-item olmalıdır.
  • Hedef id değerinin bulunmaması: href="#iletisim" için sayfada id="iletisim" olmalıdır.
  • Gizli bölümler: Görünmeyen hedefler yok sayılır; bölümü gösterdikten sonra refresh() çağırın.
  • Olayı body üzerinde dinlemek: Body izleniyorsa activate.bs.scrollspy olayını window üzerinde dinleyin.
  • Bootstrap 4 öznitelikleri: data-spy ve data-target v5’te çalışmaz; data-bs-spy ve data-bs-target yazın. Ayrıntı geçiş yazısında.

Bootstrap JavaScript dosyasının nasıl ekleneceği CDN kurulum yazısında, genel sorunlar sık hatalar yazısında anlatılır.

Sık Sorulan Sorular

Scrollspy neden hiçbir bağlantıyı işaretlemiyor?

En yaygın nedenler; menünün .nav veya .list-group olmaması, data-bs-target değerinin menünün kimliğiyle uyuşmaması, izlenen öğede position: relative bulunmaması ve kutunun yüksekliği olmadığı için kaymamasıdır.

Sabit menü bölüm başlıklarını örtüyor, ne yapmalıyım?

data-bs-offset değerini menü yüksekliğine yakın bir değere ayarlayın. Bu değer, etkin bölümün hesaplanmasında üstten bırakılan payı belirler.

Scrollspy yalnızca menü ile mi çalışır?

Evet. Bootstrap 5.0’da işaretlenen öğeler nav bağlantıları, list group öğeleri ve dropdown öğeleriyle sınırlıdır. Başka bir öğeyi güncellemek için activate.bs.scrollspy olayını dinleyip kendi kodunuzu yazabilirsiniz.

İçerik değişince Scrollspy’ı nasıl güncellerim?

bootstrap.ScrollSpy.getInstance(oge).refresh() çağırın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Bootstrap öğrenme rehberi · Bootstrap navbar · Nav, tabs ve pills · List group · JavaScript API · JavaScript öğrenme rehberi