scrollIntoView Scrolls çok uzak


114

İçlerinde div bulunan tablo satırlarını içeren bir kaydırma çubuğunun veritabanından dinamik olarak oluşturulduğu bir sayfam var. Her tablo satırı, bir YouTube oynatma listesinde video oynatıcının yanında göreceğiniz gibi bir bağlantı görevi görür.

Bir kullanıcı sayfayı ziyaret ettiğinde, bulunduğu seçeneğin kayan bölmenin en üstüne gitmesi gerekir. Bu işlevsellik çalışıyor. Sorun şu ki, biraz fazla ileri gidiyor. Açık oldukları seçenek yaklaşık 10 piksel fazla yüksek. Böylece, sayfa ziyaret edilir, url hangi seçeneğin seçildiğini belirlemek için kullanılır ve ardından bu seçeneği kayan bölmenin en üstüne kaydırır. Not: Bu, pencere için kaydırma çubuğu değildir, kaydırma çubuğu olan bir div'dir.

Bu kodu, seçilen seçeneği div'in en üstüne taşımak için kullanıyorum:

var pathArray = window.location.pathname.split( '/' );

var el = document.getElementById(pathArray[5]);

el.scrollIntoView(true);

Onu div'in üstüne taşır, ancak yaklaşık 10 piksel fazla yukarı taşır. Bunu nasıl düzelteceğini bilen var mı?


49
İçin ofset yapılandırmasının olmaması scrollIntoViewcan sıkıcı.
mystrdat

Yanıtlar:


59

Yaklaşık 10 piksel ise, sanırım kapsama alanının divkaydırma ofsetini şu şekilde manuel olarak ayarlayabilirsiniz :

el.scrollIntoView(true);
document.getElementById("containingDiv").scrollTop -= 10;

2
Bu, ile aynı animasyon efektine sahip olacak scrollIntoViewmı?
1252748

1
@thomas AFAIK, düz DOM scrollIntoViewişlevi animasyona neden olmaz. ScrollIntoView jQuery eklentisinden mi bahsediyorsunuz?
Lucas Trzesniewski

1
Bu işe yaradı, ama yanlış yöndü, bu yüzden tek yapmam gereken + = 10'dan - = 10'a değiştirmekti ve şimdi tam olarak yükleniyor, yardım için çok teşekkürler !!!!
Matthew Wilson

18
Canlandırabilir, sadece ekleyin el.scrollIntoView({ behavior: 'smooth' });. Destek yine de harika değil!
daveaspinall

1
@kernowcode bunu kastettiğini düşünüyorumblock:'center'
Sanyam Jain

127

Düzgün bir konuma doğru kaydırın

Doğru y koordinatı alın ve kullanınwindow.scrollTo({top: y, behavior: 'smooth'})

const id = 'profilePhoto';
const yOffset = -10; 
const element = document.getElementById(id);
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;

window.scrollTo({top: y, behavior: 'smooth'});

2
Bu en doğru cevap - kaydırma konumunu kolayca bulabilir jQuery('#el').offset().topve sonra kullanabilirizwindow.scrollTo
Alexander Goncharov

1
Bu çözüm için +1. Üst ofset ile göreli olarak konumlandırılmış bir konteynerim olduğunda öğeye doğru şekilde kaydırmama yardımcı oldu.
Liga

React Router ile HashLink kullanarak buraya gelenler, benim için işe yarayan şey buydu. HashLink'inizdeki kaydırma desteğinde scroll={el => { const yCoordinate = el.getBoundingClientRect().top + window.pageYOffset; const yOffset = -80; window.scrollTo({ top: yCoordinate + yOffset, behavior: 'smooth' }); }}- ofset, yalnızca daha iyi boşluk için başlığınızın boyutundan 10-15 piksel daha fazladır
Rob B

87

Bunu iki adımda yapabilirsiniz:

el.scrollIntoView(true);
window.scrollBy(0, -10); // Adjust scrolling with a negative value here

24
Eğer scrollIntoView()daha önce kaydırma bitirmedi scrollBy()ishal, ikincisi kaydırma eski iptal edecektir. En azından Chrome 71'de.
Dave Hughes

1
Bu durumda setTimeout'u aşağıdaki yanıtta görüldüğü gibi kullanın: stackoverflow.com/a/51935129/1856258 .. Benim için Zaman Aşımı olmadan çalışıyor. Teşekkürler!
leole

Uygulamanın doğrudan ilgili yere taşınmasını sağlamak için bir piksel düzelticisini scrollIntoView'a doğrudan enjekte etmenin bir yolu var mı? Gibi scrollIntoView({adjustment:y}), ben sonunda özel bir kod ile mümkün olması gerektiğini düşünüyorum
Webwoman

83

Çapayı Mutlak Yönteme Göre Konumlandırma

Bunu yapmanın başka bir yolu da, ofset ile kaydırmaya güvenmek yerine bağlantılarınızı sayfada tam olarak istediğiniz yere yerleştirmektir. Her öğe için daha iyi kontrole izin verdiğini düşünüyorum (örneğin, belirli öğeler için farklı bir ofset istiyorsanız) ve ayrıca tarayıcı API değişikliklerine / farklılıklarına daha dayanıklı olabilir.

<div id="title-element" style="position: relative;">
  <div id="anchor-name" style="position: absolute; top: -100px; left: 0"></div>
</div>

Şimdi ofset, öğeye göre -100px olarak belirtildi. Kodun yeniden kullanımı için bu çapayı oluşturmak için bir işlev oluşturun veya React gibi modern bir JS çerçevesi kullanıyorsanız, bunu çapanızı oluşturan bir bileşen oluşturarak yapın ve her öğe için çapa adını ve hizalamasını iletin. aynı olmayacak.

O zaman şunu kullan:

const element = document.getElementById('anchor-name')
element.scrollIntoView({ behavior: 'smooth', block: 'start' });

100px'lik bir ofset ile düzgün kaydırma için.


12
Bu, bunu sorunsuz ve basit bir şekilde gerçekleştirmenin açık ara en iyi yoludur.
catch22

2
Bence en iyi ve en doğal yöntem.
Даниил Пронин

1
Bu çok basit ve düzgün. Diğerlerinin çoğu sadece gereksiz JS ekler.
RedSands

2
Bu, özellikle "sayfa başı" türü bir bağlantı bir gezinme çubuğunun altındayken ve gezinmeyi göstermek istediğiniz halde başka bağlantılardan uzaklaşmak istemediğinizde kullanışlıdır
Joe Moon

aynı şeyi negatif margin-top kullanarak elde edebilirsiniz, öğeyi konum göreceli bir başkasıyla sarmalamaktan kaçınırsınız
Sergi

22

Bu sorunu kullanarak çözdüm

element.scrollIntoView({ behavior: 'smooth', block: 'center' });

Bu, öğenin centerkaydırmadan sonra görünmesini sağlar , böylece hesaplamama gerek kalmaz yOffset.

Umarım yardımcı olur...


1
Sen kullanmalısınız scrollTodeğil elementama üzerindewindow
Arseniy-II

@ Arseniy-II Bunu işaret ettiğiniz için teşekkürler !!. O kısmı kaçırdım!
Imtiaz Shakil Siddique

blok zirveye yardımcı oluyor mu?
Ashok kumar Ganesan

16

Bu benim için Chrome'da çalışıyor (Düzgün kaydırma ve zamanlama kesmeleri olmadan)

Yalnızca öğeyi hareket ettirir, kaydırmayı başlatır ve ardından geri taşır.

Öğe zaten ekranda ise görünür bir "patlama" yoktur.

pos = targetEle.style.position;
top = targetEle.style.top;
targetEle.style.position = 'relative';
targetEle.style.top = '-20px';
targetEle.scrollIntoView({behavior: 'smooth', block: 'start'});
targetEle.style.top = top;
targetEle.style.position = pos;

Bu en iyi çözüm ... Cevap olarak işaretlenmesini isterdim. Birkaç saat kazandırırdı.
Shivam

Test edildi ve kutudan çıkar çıkmaz çalışıyor. Hack değil ve harika çalışıyor! Buna oy verin!
Jens Tornell

7

Daha önceki bir cevaba dayanarak, bunu bir Angular5 projesinde yapıyorum.

İle başladı:

// el.scrollIntoView(true);
el.scrollIntoView({
   behavior: 'smooth',
   block: 'start'
});
window.scrollBy(0, -10); 

Ancak bu bazı problemlere neden oldu ve scrollBy () için şu şekildeTimeout ayarlaması gerekiyordu:

//window.scrollBy(0,-10);
setTimeout(() => {
  window.scrollBy(0,-10)
  }, 500);

Ve MSIE11 ve Chrome 68+ ile mükemmel çalışır. FF'de test etmedim. 500ms, girebileceğim en kısa gecikmeydi. Düzgün kaydırma henüz tamamlanmadığı için bazen aşağı inme başarısız oldu. Kendi projeniz için gerektiği gibi ayarlayın.

Bu basit ama etkili çözüm için +1 ile Fred727 arasında .


7
Bu, bir öğeye yumuşak kaydırma yapmak ve ardından biraz sonra yukarı kaydırmak için biraz sarsıntılı geliyor.
22

7

DOM'da hepsi aynı seviyede olan ve sınıf adı "kaydırmalı kaydırma" olan div'lere kaydırmak istediğinizi varsayarsak, bu CSS sorunu çözecektir:

.scroll-with-offset {    
  padding-top: 100px;
  margin-bottom: -100px;
}

Sayfanın üst kısmından uzaklık 100 pikseldir. Yalnızca block: 'start' ile amaçlandığı gibi çalışacaktır:

element.scrollIntoView({ behavior: 'smooth', block: 'start' });

Olan şey, div'lerin en üst noktasının normal konumda olması, ancak iç içeriklerinin normal konumun 100 piksel altında başlamasıdır. Padding-top: 100px bunun içindir. margin-bottom: -100px, aşağıdaki div'in ekstra marjını dengelemek içindir. Çözümün tamamlanması için ayrıca en üstteki ve en alttaki div'lerin kenar boşluklarını / dolgularını dengelemek için bu CSS'yi ekleyin:

.top-div {
  padding-top: 0;
}
.bottom-div {
  margin-bottom: 0;
}

6

Bu çözüm @ Arseniy-II'ye ait , onu bir fonksiyona sadeleştirdim.

function _scrollTo(selector, yOffset = 0){
  const el = document.querySelector(selector);
  const y = el.getBoundingClientRect().top + window.pageYOffset + yOffset;

  window.scrollTo({top: y, behavior: 'smooth'});
}

Kullanım (konsolu burada StackOverflow'da açabilir ve test edebilirsiniz):

_scrollTo('#question-header', 0);


4

Yani, belki bu biraz hantal ama şimdiye kadar çok iyi. Açısal 9'da çalışıyorum.

dosya .ts

scroll(el: HTMLElement) {
  el.scrollIntoView({ block: 'start',  behavior: 'smooth' });   
}

dosya .html

<button (click)="scroll(target)"></button>
<div  #target style="margin-top:-50px;padding-top: 50px;" ></div>

Kenar boşluğu ve padding top ile ofseti ayarlıyorum.

Saludos!


3

Bunu aldım ve benim için harika çalışıyor:

// add a smooth scroll to element
scroll(el) {
el.scrollIntoView({
  behavior: 'smooth',
  block: 'start'});

setTimeout(() => {
window.scrollBy(0, -40);
}, 500);}

Umarım yardımcı olur.


3

Diğer bir çözüm de "offsetTop" kullanmaktır, şöyle:

var elementPosition = document.getElementById('id').offsetTop;

window.scrollTo({
  top: elementPosition - 10, //add your necessary value
  behavior: "smooth"  //Smooth transition to roll
});

1

Geçici bir çözüm buldum. Örneğin, burada bir div öğesine kaydırmak istediğinizi ve üzerinde 20 piksel boşluk olmasını istediğinizi varsayalım. Ref, üstünde oluşturulan bir div olarak ayarlayın:

<div ref={yourRef} style={{position: 'relative', bottom: 20}}/> <Element />

Bunu yapmak, istediğiniz bu aralığı yaratacaktır.

Bir başlığınız varsa, başlığın arkasında da boş bir div oluşturun ve buna başlığın yüksekliğine eşit bir yükseklik atayın ve buna referans verin.


0

Ana fikrim, kaydırmak istediğimiz görünümün üzerinde bir tempDiv oluşturmaktır . Projemde gecikmeden iyi çalışıyor.

scrollToView = (element, offset) => {
    var rect = element.getBoundingClientRect();
    var targetY = rect.y + window.scrollY - offset;

    var tempDiv;
    tempDiv = document.getElementById("tempDiv");
    if (tempDiv) {
        tempDiv.style.top = targetY + "px";
    } else {
        tempDiv = document.createElement('div');
        tempDiv.id = "tempDiv";
        tempDiv.style.background = "#F00";
        tempDiv.style.width = "10px";
        tempDiv.style.height = "10px";
        tempDiv.style.position = "absolute";
        tempDiv.style.top = targetY + "px";
        document.body.appendChild(tempDiv);
    }

    tempDiv.scrollIntoView({ behavior: 'smooth', block: 'start' });
}

Kullanan örnek

onContactUsClick = () => {
    this.scrollToView(document.getElementById("contact-us"), 48);
}

Umarım yardımcı olur


0

Arseniy-II'nin cevabına göre: Kayan varlığın pencerenin kendisi değil, bir iç Şablon olduğu Kullanım Durumuna sahiptim (bu durumda bir div). Bu senaryoda, kaydırma kabı için bir kimlik belirlememiz getElementByIdve kaydırma işlevini kullanmak için onu almamız gerekir :

<div class="scroll-container" id="app-content">
  ...
</div>
const yOffsetForScroll = -100
const y = document.getElementById(this.idToScroll).getBoundingClientRect().top;
const main = document.getElementById('app-content');
main.scrollTo({
    top: y + main.scrollTop + yOffsetForScroll,
    behavior: 'smooth'
  });

Birinin benzer bir durumla karşı karşıya kalması ihtimaline karşı onu burada bırakmak!


0

İşte benim 2 sentim.

Ayrıca, scrollIntoView öğesinin biraz ilerisine kaydırılması sorununu yaşadım, bu yüzden bir öğeyi hedefin başına ekleyen bir komut dosyası (yerel javascript) oluşturdum, onu css ile biraz yukarı konumlandırdım ve buna kaydırdım. Kaydırdıktan sonra oluşturulan öğeleri tekrar kaldırıyorum.

HTML:

//anchor tag that appears multiple times on the page
<a href="#" class="anchors__link js-anchor" data-target="schedule">
    <div class="anchors__text">
        Scroll to the schedule
    </div>
</a>

//The node we want to scroll to, somewhere on the page
<div id="schedule">
    //html
</div>

Javascript dosyası:

(() => {
    'use strict';

    const anchors = document.querySelectorAll('.js-anchor');

    //if there are no anchors found, don't run the script
    if (!anchors || anchors.length <= 0) return;

    anchors.forEach(anchor => {
        //get the target from the data attribute
        const target = anchor.dataset.target;

        //search for the destination element to scroll to
        const destination = document.querySelector(`#${target}`);
        //if the destination element does not exist, don't run the rest of the code
        if (!destination) return;

        anchor.addEventListener('click', (e) => {
            e.preventDefault();
            //create a new element and add the `anchors__generated` class to it
            const generatedAnchor = document.createElement('div');
            generatedAnchor.classList.add('anchors__generated');

            //get the first child of the destination element, insert the generated element before it. (so the scrollIntoView function scrolls to the top of the element instead of the bottom)
            const firstChild = destination.firstChild;
            destination.insertBefore(generatedAnchor, firstChild);

            //finally fire the scrollIntoView function and make it animate "smoothly"
            generatedAnchor.scrollIntoView({
                behavior: "smooth",
                block: "start",
                inline: "start"
            });

            //remove the generated element after 1ms. We need the timeout so the scrollIntoView function has something to scroll to.
            setTimeout(() => {
                destination.removeChild(generatedAnchor);
            }, 1);
        })
    })
})();

CSS:

.anchors__generated {
    position: relative;
    top: -100px;
}

Umarım bu herkese yardımcı olur!


0

Yukarıdaki çözümlerle bu sorunu çözmeyenler için bu css ipuçlarını ekliyorum:

#myDiv::before {
  display: block;
  content: " ";
  margin-top: -90px; // adjust this with your header height
  height: 90px; // adjust this with your header height
  visibility: hidden;
}

0

UPD: Aşağıdaki çözümden daha iyi çalışan ve kullanımı daha kolay bir npm paketi oluşturdum .

SmoothScroll işlevim

Steve Banton'ın harika çözümünü aldım ve kullanımı daha kolay hale getiren bir işlev yazdım. Daha önce denediğim gibi sadece kullanmak window.scroll()veya hatta daha kolay olurdu window.scrollBy(), ancak bu ikisinin bazı sorunları var:

  • Onları pürüzsüz bir davranışla kullandıktan sonra her şey önemsiz hale gelir.
  • Onları zaten engelleyemezsiniz ve parşömenin sonuna kadar beklemeniz gerekir. Umarım işlevim sizin için yararlı olur. Ayrıca, Safari'de ve hatta IE'de çalışmasını sağlayan hafif bir polyfill var.

İşte kod

Sadece kopyalayın ve istediğiniz gibi karıştırın.

import smoothscroll from 'smoothscroll-polyfill';

smoothscroll.polyfill();

const prepareSmoothScroll = linkEl => {
  const EXTRA_OFFSET = 0;

  const destinationEl = document.getElementById(linkEl.dataset.smoothScrollTo);
  const blockOption = linkEl.dataset.smoothScrollBlock || 'start';

  if ((blockOption === 'start' || blockOption === 'end') && EXTRA_OFFSET) {
    const anchorEl = document.createElement('div');

    destinationEl.setAttribute('style', 'position: relative;');
    anchorEl.setAttribute('style', `position: absolute; top: -${EXTRA_OFFSET}px; left: 0;`);

    destinationEl.appendChild(anchorEl);

    linkEl.addEventListener('click', () => {
      anchorEl.scrollIntoView({
        block: blockOption,
        behavior: 'smooth',
      });
    });
  }

  if (blockOption === 'center' || !EXTRA_OFFSET) {
    linkEl.addEventListener('click', () => {
      destinationEl.scrollIntoView({
        block: blockOption,
        behavior: 'smooth',
      });
    });
  }
};

export const activateSmoothScroll = () => {
  const linkEls = [...document.querySelectorAll('[data-smooth-scroll-to]')];

  linkEls.forEach(linkEl => prepareSmoothScroll(linkEl));
};

Bir bağlantı öğesi yapmak için aşağıdaki veri özelliğini eklemeniz yeterlidir:

data-smooth-scroll-to="element-id"

Ayrıca başka bir özniteliği bir eklenti olarak ayarlayabilirsiniz.

data-smooth-scroll-block="center"

İşlevin blockseçeneğini temsil eder scrollIntoView(). Varsayılan olarak start. MDN hakkında daha fazlasını okuyun .

En sonunda

SmoothScroll işlevini ihtiyaçlarınıza göre ayarlayın.

Örneğin, sabit bir başlığınız varsa (veya ben onu bu kelimeyle çağırıyorum masthead) şöyle bir şey yapabilirsiniz:

const mastheadEl = document.querySelector(someMastheadSelector);

// and add it's height to the EXTRA_OFFSET variable

const EXTRA_OFFSET = mastheadEl.offsetHeight - 3;

Böyle bir vakanız yoksa, silin, neden olmasın: -D.


-1

Belirli bir öğeyi aşağı kaydırmak için basit çözüm

const element = document.getElementById("element-with-scroll");
element.scrollTop = element.scrollHeight - 10;

-1

Tablo satırındaki bir öğe için, JQuery'yi kullanarak istediğiniz satırın üstüne gelin ve bunun yerine o satıra kaydırın.

Bir tabloda birden çok satırım olduğunu ve bunların bir kısmının yönetici tarafından incelenmesi gerektiğini varsayalım. İncelenmesi gereken her satırda, gözden geçirilmek üzere sizi önceki veya sonraki öğeye götüren hem yukarı hem de aşağı oklar bulunur.

İşte not defterinde yeni bir HTML belgesi oluşturup kaydettiğinizde çalıştırılması gereken eksiksiz bir örnek. Öğelerimizin üstünü ve altını incelemek üzere tespit etmek için fazladan kod vardır, böylece herhangi bir hata atmayız.

<html>
<head>
    <title>Scrolling Into View</title>
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
    <style>
        div.scroll { height: 6em; width: 20em; overflow: auto; }
        thead th   { position: sticky; top: -1px; background: #fff; }
        .up, .down { cursor: pointer; }
        .up:hover, .down:hover { color: blue; text-decoration:underline; }
    </style>
</head>
<body>
<div class='scroll'>
<table border='1'>
    <thead>
        <tr>
            <th>Review</th>
            <th>Data</th>
        </tr>
    </thead>
    <tbody>
        <tr id='row_1'>
            <th></th>
            <td>Row 1 (OK)</td>
        </tr>
        <tr id='row_2'>
            <th></th>
            <td>Row 2 (OK)</td>
        </tr>
        <tr id='row_3'>
            <th id='jump_1'><span class='up'>UP</span> <span class='down'>DN</span></th>
            <td>Row 3 (REVIEW)</td>
        </tr>
        <tr id='row_4'>
            <th></th>
            <td>Row 4 (OK)</td>
        </tr>
        <tr id='row_5'>
            <th id='jump_2'><span class='up'>UP</span> <span class='down'>DN</span></th>
            <td>Row 5 (REVIEW)</td>
        </tr>
        <tr id='row_6'>
            <th></th>
            <td>Row 6 (OK)</td>
        </tr>
        <tr id='row_7'>
            <th></th>
            <td>Row 7 (OK)</td>
        </tr>
        <tr id='row_8'>
            <th id='jump_3'><span class='up'>UP</span> <span class='down'>DN</span></th>
            <td>Row 8 (REVIEW)</td>
        </tr>
        <tr id='row_9'>
            <th></th>
            <td>Row 9 (OK)</td>
        </tr>
        <tr id='row_10'>
            <th></th>
            <td>Row 10 (OK)</td>
        </tr>
    </tbody>
</table>
</div>
<script>
$(document).ready( function() {
    $('.up').on('click', function() {
        var id = parseInt($(this).parent().attr('id').split('_')[1]);
        if (id>1) {
            var row_id = $('#jump_' + (id - 1)).parent().attr('id').split('_')[1];
            document.getElementById('row_' + (row_id-1)).scrollIntoView({behavior: 'smooth', block: 'start'});
        } else {
            alert('At first');
        }
    });

    $('.down').on('click', function() {
        var id = parseInt($(this).parent().attr('id').split('_')[1]);
        if ($('#jump_' + (id + 1)).length) {
            var row_id = $('#jump_' + (id + 1)).parent().attr('id').split('_')[1];
            document.getElementById('row_' + (row_id-1)).scrollIntoView({behavior: 'smooth', block: 'start'});
        } else {
            alert('At last');
        }
    });
});
</script>
</body>
</html>
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.