Hedefleme konumu: şu anda "takılı" durumda olan yapışkan öğeler


111

konum: yapışkan şu anda bazı mobil tarayıcılarda çalışıyor, böylece bir menü çubuğunu sayfada kaydırabilir, ancak daha sonra kullanıcı sayfayı kaydırdığında görüntü alanının üst kısmına bağlı kalabilirsiniz.

Peki ya yapışkan menü çubuğunuzu şu anda 'yapışma' halindeyken biraz yeniden biçimlendirmek istiyorsanız? Örneğin, sayfa ile kaydırıldığında çubuğun yuvarlatılmış köşelere sahip olmasını isteyebilirsiniz, ancak daha sonra görüntü alanının üstüne yapışır yapışmaz, üstteki yuvarlatılmış köşelerden kurtulmak ve altına küçük bir gölge eklemek isteyebilirsiniz. o.

Şu anda yapışan ve yapışmakta ::stuckolan öğeleri hedeflemek için herhangi bir tür sözde seçici (örneğin ) var mı? Veya tarayıcı satıcılarının boru hattında buna benzer bir şey var mı? Değilse, nereden talep edebilirim?position: sticky

NB. javascript çözümleri bunun için iyi değildir çünkü mobil cihazlarda genellikle scrollkullanıcı parmağını bıraktığında tek bir olay alırsınız , bu nedenle JS kaydırma eşiğinin tam olarak ne zaman geçtiğini bilemez.

Yanıtlar:


104

Şu anda 'sıkışmış' olan öğeler için önerilen bir seçici yok. Postioned Düzeni modülüposition: sticky tanımlanır de benzer bir seçici söz etmez.

CSS için özellik istekleri, www tarzı posta listesine gönderilebilir . Bir :stucksözde sınıfın ::stucksözde bir öğeden daha mantıklı olduğuna inanıyorum , çünkü bu durumdayken öğelerin kendilerini hedeflemeye çalışıyorsunuz. :stuckHatta bir süre önce sözde bir sınıf tartışılıyordu ; Ana komplikasyon, işlenmiş veya hesaplanmış bir stile göre eşleştirme girişiminde bulunan önerilen herhangi bir seçicinin başına bela olan bir şeydir: döngüsel bağımlılıklar.

Bir durumunda :stucksözde sınıfı, yuvarlık en basit durumda, aşağıdaki CSS ile gerçekleşir:

:stuck { position: static; /* Or anything other than sticky/fixed */ }
:not(:stuck) { position: sticky; /* Or fixed */ }

Ve ele alınması zor olan çok sayıda uç durum olabilir.

Genellikle belirli bir düzen devletler dayalı olarak eşleşen seçiciler sahip olacağını kararlaştırılan Özgür'e , Nice , maalesef büyük sınırlamalar uygulamak için bu önemsiz olmayan bu marka var. Yakın zamanda bu soruna saf bir CSS çözümü için nefesimi tutmayacağım.


14
Bu utanç verici. Ben de bu soruna bir çözüm arıyordum. positionBir :stuckseçici üzerindeki özelliklerin göz ardı edilmesi gerektiğini söyleyen bir kural koymak oldukça kolay olmaz mıydı ? (tarayıcı satıcıları için bir kural demek istiyorum, nasıl leftöncelikli olacağına ilişkin kurallara benzer right))
powerbuoy

5
Sadece pozisyon ... bir hayal değil :stuckdeğiştirir topdeğeri 0için 300px, daha sonra aşağı kaydırın 150px... o sopa veya olmasın? Ya sahip bir öğenin düşünmek position: stickyve bottom: 0nerede :stuckbelki değişir font-size, bu nedenle elemanları boyutunu (bu nedenle sopa hangi anı değiştirme) ... ve
Roma

3
Teklifin, bir şeyin takılıp kaldığını / çözüldüğünü bilmek için JS etkinliklerine sahip olduğu github.com/w3c/csswg-drafts/issues/1660 adresine bakın . Bu, bir sözde seçicinin getirdiği sorunlara sahip olmamalıdır.
Ruben

27
Aynı döngüsel sorunların zaten var olan birçok sözde sınıfla yapılabileceğine inanıyorum (örneğin: fareyle üzerine gelip genişliği değiştiriyor ve: (: vurgulu) tekrar değiştirmiyor). Çok isterim: sözde sınıf sıkışmış ve geliştiricinin kodunda döngüsel sorunların olmamasından sorumlu olması gerektiğini düşünüyorum.
Marek Lisý

12
Pekala ... Bunu gerçekten bir hata olarak anlamıyorum - bu whiledöngü kötü tasarlanmış çünkü sonsuz döngüye izin veriyor :) Ancak bunu açıkladığınız için teşekkürler;)
Marek Lisý

26

Bazı durumlarda IntersectionObserver, durum düzgün bir şekilde hizalamak yerine kök kapsayıcısının dışındaki bir veya iki piksele yapışmaya izin veriyorsa, basit bir hile yapabilir. Bu şekilde, kenarın hemen ötesine oturduğunda, gözlemci ateş eder ve biz gidip koşuyoruz.

const observer = new IntersectionObserver( 
  ([e]) => e.target.toggleAttribute('stuck', e.intersectionRatio < 1),
  {threshold: [1]}
);

observer.observe(document.querySelector('nav'));

Öğeyi kabının hemen dışına yapıştırın top: -2pxve ardından stucközniteliğiyle hedefleyin ...

nav {
  background: magenta;
  height: 80px;
  position: sticky;
  top: -2px;
}
nav[stuck] {
  box-shadow: 0 0 16px black;
}

Örnek burada: https://codepen.io/anon/pen/vqyQEK


1
Bir stucksınıfın özel bir özellikten daha iyi olacağını düşünüyorum ... Seçiminizin belirli bir nedeni var mı?
collimarco

Bir sınıf da iyi çalışıyor, ancak bu, türetilmiş bir özellik olduğu için bundan biraz daha yüksek bir seviye gibi görünüyor. Bir nitelik bana daha uygun görünüyor, ama her iki durumda da bir zevk meselesi.
rafa takılabilir

Zaten sabitlenmiş bir başlık nedeniyle
üstümün 60px olmasına

1
Sıkışan her şeye üst dolgu eklemeyi deneyin, belki padding-top: 60pxsizin durumunuzda :)
Tim Willis

5

Google Developers blogundaki biri, IntersectionObserver ile performatif JavaScript tabanlı bir çözüm bulduğunu iddia ediyor .

Buradaki ilgili kod biti:

/**
 * 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));
}

Kendim kopyalamadım, ama belki bu soruya takılıp kalan birine yardımcı olabilir.


3

Şekillendirme için js hacks kullanma hayranı değilim (yani getBoudingClientRect, kaydırma dinleme, yeniden boyutlandırma dinleme), ama şu anda sorunu bu şekilde çözüyorum. Bu çözüm, simge durumuna küçültülebilir / en üst düzeye çıkarılabilir içeriğe (<ayrıntılar>) veya iç içe kaydırmaya veya gerçekten herhangi bir eğri topuna sahip sayfalarla ilgili sorunlara sahip olacaktır. Bununla birlikte, sorunun basit olduğu zamanlar için de basit bir çözümdür.

let lowestKnownOffset: number = -1;
window.addEventListener("resize", () => lowestKnownOffset = -1);

const $Title = document.getElementById("Title");
let requestedFrame: number;
window.addEventListener("scroll", (event) => {
    if (requestedFrame) { return; }
    requestedFrame = requestAnimationFrame(() => {
        // if it's sticky to top, the offset will bottom out at its natural page offset
        if (lowestKnownOffset === -1) { lowestKnownOffset = $Title.offsetTop; }
        lowestKnownOffset = Math.min(lowestKnownOffset, $Title.offsetTop);
        // this condition assumes that $Title is the only sticky element and it sticks at top: 0px
        // if there are multiple elements, this can be updated to choose whichever one it furthest down on the page as the sticky one
        if (window.scrollY >= lowestKnownOffset) {
            $Title.classList.add("--stuck");
        } else {
            $Title.classList.remove("--stuck");
        }
        requestedFrame = undefined;
    });
})

Kaydırma olay dinleyicisinin ana iş parçacığı üzerinde yürütüldüğünü ve bu da onu bir performans katili haline getirdiğini unutmayın. Bunun yerine Intersection Observer API'yi kullanın.
Şüpheci Jule

if (requestedFrame) { return; }Animasyon karesi gruplaması nedeniyle bir "performans katili" değil. Intersection Observer yine de bir gelişme.
Seph Reed

0

Öğenin üzerinde bir öğeye sahip olduğunuz zaman için kompakt bir yol position:sticky. stuckCSS'de eşleştirebileceğiniz niteliği şu şekilde ayarlar header[stuck]:

HTML:

<img id="logo" ...>
<div>
  <header style="position: sticky">
    ...
  </header>
  ...
</div>

JS:

if (typeof IntersectionObserver !== 'function') {
  // sorry, IE https://caniuse.com/#feat=intersectionobserver
  return
}

new IntersectionObserver(
  function (entries, observer) {
    for (var _i = 0; _i < entries.length; _i++) {
      var stickyHeader = entries[_i].target.nextSibling
      stickyHeader.toggleAttribute('stuck', !entries[_i].isIntersecting)
    }
  },
  {}
).observe(document.getElementById('logo'))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.