scrollIntoView Scrollt net te ver

Ik heb een pagina waar een schuifbalk met tabelrijen met divs erin dynamisch wordt gegenereerd vanuit de database. Elke tabelrij fungeert als een link, een beetje zoals je zou zien in een YouTube-afspeellijst naast de videospeler.

Wanneer een gebruiker de pagina bezoekt, wordt verondersteld dat de optie die ze gebruiken naar de bovenkant van de scrollende div gaat. Deze functionaliteit werkt. Het probleem is dat het net iets te ver gaat. Alsof de optie waarop ze staan ​​ongeveer 10px te hoog is. Dus de pagina wordt bezocht, de url wordt gebruikt om te identificeren welke optie is geselecteerd en schuift die optie vervolgens naar de bovenkant van de scrollende div. Opmerking: dit is niet de schuifbalk voor het venster, het is een div met een schuifbalk.

Ik gebruik deze code om de geselecteerde optie naar de bovenkant van de div te verplaatsen:

var pathArray = window.location.pathname.split( '/' );
var el = document.getElementById(pathArray[5]);
el.scrollIntoView(true);

Het verplaatst het naar de bovenkant van de div, maar ongeveer 10 pixels te ver naar boven.
Weet iemand hoe je dat oplost?


Antwoord 1, autoriteit 100%

Vloeiend scrollen naar de juiste positie

Verkrijg correctey-coördinaat en gebruik window.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'});

Antwoord 2, autoriteit 49%

Plaats anker volgens absolute methode

Een andere manier om dit te doen, is door uw ankers precies daar te plaatsen waar u wilt op de pagina, in plaats van te vertrouwen op verschuiving van scrollen. Ik vind dat het een betere controle biedt voor elk element (bijvoorbeeld als je een andere offset wilt voor bepaalde elementen), en mogelijk ook beter bestand is tegen wijzigingen/verschillen in de browser-API.

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

De offset is nu gespecificeerd als -100px ten opzichte van het element. Maak een functie om dit anker te maken voor hergebruik van code, of als u een modern JS-framework zoals React gebruikt, doe dit door een component te maken die uw anker weergeeft, en geef de ankernaam en uitlijning door voor elk element, wat al dan niet kan niet hetzelfde zijn.

Gebruik dan gewoon :

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

Voor soepel scrollen met een offset van 100px.


Antwoord 3, autoriteit 45%

Je kunt het in twee stappen doen:

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

Antwoord 4, autoriteit 28%

Als het ongeveer 10px is, dan denk ik dat je de scroll-offset van de bevattende diveenvoudig zo handmatig kunt aanpassen:

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

Antwoord 5, autoriteit 23%

Ik heb dit probleem opgelost door te gebruiken,

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

Hierdoor verschijnt het element in het centerna het scrollen, zodat ik yOffsetniet hoef te berekenen.

Hopelijk helpt het…


Antwoord 6, autoriteit 15%

Dit werkt voor mij in Chrome (met soepel scrollen en geen timing-hacks)

Het verplaatst alleen het element, start het scrollen en verplaatst het vervolgens terug.

Er is geen “knallen” zichtbaar als het element al op het scherm staat.

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;

Antwoord 7, autoriteit 3%

Een andere oplossing is om “offsetTop” als volgt te gebruiken:

var elementPosition = document.getElementById('id').offsetTop;
window.scrollTo({
  top: elementPosition - 10, //add your necessary value
  behavior: "smooth"  //Smooth transition to roll
});

Antwoord 8, autoriteit 2%

U kunt ook de element.scrollIntoView()opties

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

die de meeste browsers ondersteunen


Antwoord 9, autoriteit 2%

Ik heb dit en het werkt uitstekend voor mij:

// add a smooth scroll to element
scroll(el) {
el.scrollIntoView({
  behavior: 'smooth',
  block: 'start'});
setTimeout(() => {
window.scrollBy(0, -40);
}, 500);}

Hopelijk helpt het.


Antwoord 10, autoriteit 2%

Dus misschien is dit een beetje onhandig, maar tot nu toe gaat het goed. Ik werk in hoekige 9.

bestand .ts

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

bestand .html

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

Ik pas de offset aan met marge en opvulling bovenaan.

Saludos!


Antwoord 11

Oplossing als u Ionic Capacitor, Angular Material gebruikt en iOS 11 moet ondersteunen.

               document.activeElement.parentElement.parentElement.scrollIntoView({block: 'center', behavior: 'smooth'}); 

De sleutel is om naar de ouder van de ouder te scrollen, de wrapper rond de invoer. Deze wrapper bevat het label voor de invoer die nu niet langer is afgesneden.

Als je alleen iOS 14 hoeft te ondersteunen, werkt de “block” center-param echt, dus dit is voldoende:

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

Antwoord 12

Een tijdelijke oplossing gevonden. Stel dat je naar een div wilt scrollen, Element hier bijvoorbeeld, en je wilt een ruimte van 20px erboven hebben.
Stel de ref in op een gemaakte div erboven:

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

Als u dit doet, wordt de gewenste tussenruimte gecreëerd.

Als je een koptekst hebt, maak dan ook een lege div achter de koptekst en wijs hieraan een hoogte toe die gelijk is aan de hoogte van de koptekst en verwijs ernaar.


Antwoord 13

Mijn belangrijkste idee is het creëren van een tempDivboven de weergave waarnaar we willen scrollen. Het werkt goed zonder achter te blijven in mijn project.

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' });
}

Voorbeeld met

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

Ik hoop dat het helpt


Antwoord 14

Gebaseerd op het antwoord van Arseniy-II: ik had de use-case waarbij de scrollende entiteit niet het venster zelf was, maar een innerlijke sjabloon (in dit geval een div). In dit scenario moeten we een ID voor de schuifcontainer instellen en deze via getElementByIdophalen om de schuiffunctie te gebruiken:

<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'
  });

Laat het hier achter voor het geval iemand in een vergelijkbare situatie zit!


Antwoord 15

Hier is mijn 2 cent.

Ik heb ook het probleem gehad dat de scrollIntoView een beetje voorbij het element scrolde, dus ik heb een script (native javascript) gemaakt dat een element voor de bestemming plaatst, het een beetje naar boven gepositioneerd met css en daarnaartoe gescrolld een. Na het scrollen verwijder ik de gemaakte elementen weer.

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-bestand:

(() => {
    '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;
}

Ik hoop dat dit iemand helpt!


Antwoord 16

Ik voeg deze css-tips toe voor degenen die dit probleem niet hebben opgelost met bovenstaande oplossingen:

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

Antwoord 17

UPD: ik heb een npm-pakketgemaakt dat beter werkt dan het volgende oplossing en gemakkelijker te gebruiken.

Mijn smoothScroll-functie

Ik heb de geweldige oplossing van Steve Banton genomen en een functie geschreven die het gebruik gemakkelijker maakt. Het zou gemakkelijker zijn om gewoon window.scroll()of zelfs window.scrollBy()te gebruiken, zoals ik eerder heb geprobeerd, maar deze twee hebben wat problemen:

  • Alles wordt rommel na gebruik met een soepel gedrag aan.
  • Je kunt ze hoe dan ook niet voorkomen en moet wachten tot de en van de boekrol.
    Dus ik hoop dat mijn functie nuttig voor je zal zijn. Er is ook een lichtgewicht polyfillwaardoor het werkt in Safari en zelfs IE.

Hier is de code

Kopieer het gewoon en verpruts het zoals je wilt.

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

Als u een link-element wilt maken, hoeft u alleen het volgende data-attribuut toe te voegen:

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

U kunt ook een ander kenmerk instellen als toevoeging

data-smooth-scroll-block="center"

Het vertegenwoordigt de optie blockvan de functie scrollIntoView(). Standaard is dit start. Lees meer op MDN.

Eindelijk

Pas de smoothScroll-functie aan uw behoeften aan.

Als je bijvoorbeeld een vaste header hebt (of ik noem het met het woord masthead), dan kun je zoiets als dit doen:

const mastheadEl = document.querySelector(someMastheadSelector);
// and add it's height to the EXTRA_OFFSET variable
const EXTRA_OFFSET = mastheadEl.offsetHeight - 3;

Als je zo’n hoesje niet hebt, verwijder het dan gewoon, waarom niet :-D.


Antwoord 18

Ik heb geprobeerd enkele van de bovenstaande antwoorden te gebruiken, maar scrollen werkte niet. Na wat speurwerk merkte ik dat de scrollposities in feite heel vreemd waren (zelfs negatieve waarden) en dat dit werd veroorzaakt door hoe Angular de pagina’s in de router-outletweergaf.

Mijn oplossing was om de schuifpositie van het element te berekenen vanaf de bovenkant van de pagina. Ik heb een element met id start-of-pageaan het begin van de sjabloon geplaatst en ik heb de hoeveelheid scroll verkregen door de positie van het doelfragment af te trekken.

ngAfterViewInit(): void {
    this.route.fragment.subscribe(fragment => {
      try {
        // @ts-ignore
        // document.querySelector('#hero').scrollIntoView({behavior:smooth, block: "start", inline: "start"});
        // @ts-ignore
        // document.querySelector('#' + fragment).scrollIntoView({behavior:smooth, block: "start", inline: "start"});
        this._scrollTo('#' + fragment,0);
      } catch (e) { };
    });
  }
private _scrollTo(selector: any, yOffset = 0){
    const base = document.querySelector('#start-of-page');
    const el = document.querySelector(selector);
    // @ts-ignore
    const y = el.getBoundingClientRect().top - base.getBoundingClientRect().top; 
    window.scrollTo({top: y, behavior: 'smooth'});
  }

Antwoord 19

Een oplossing is om een ​​onzichtbaar tijdelijk element te maken met een offset ten opzichte van het doel, ernaartoe te scrollen en het vervolgens te verwijderen. Het is misschien niet ideaal, maar andere oplossingen werkten niet voor mij in de context waarin ik werkte. Bijvoorbeeld:

const createOffsetElement = (element) => {
    const offsetElement = document.createElement('div');
    offsetElement.style = `
        position: relative;
        top: -10em;
        height: 1px;
        width: 1px;
        visibility: hidden;
    `;
    const inputWrapper = element.closest('.ancestor-element-class');
    inputWrapper.appendChild(offsetElement);
    return offsetElement;
};
const scrollTo = (element) => {
    const offsetElement = createOffsetElement(element);
    offsetElement.scrollIntoView({
        behavior: 'smooth',
    });
    offsetElement.remove();
};

Antwoord 20

De eenvoudigste manier om dit te doen,

html, body {
    scroll-behavior: smooth;
}
    html [id], body [id] {
        scroll-margin: 50px !important;
    }

met de door u verstrekte code

var pathArray = window.location.pathname.split( '/' );
var el = document.getElementById(pathArray[5]);
el.style.scrollMargin = 50px !important;
el.scrollIntoView(true);

Antwoord 21

Eenvoudige oplossing voor het naar beneden scrollen van een specifiek element

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

Antwoord 22

Gebruik voor een element in een tabelrij JQuery om de rij boven de gewenste rij te krijgenen scrol in plaats daarvan naar die rij.

Stel dat ik meerdere rijen in een tabel heb, waarvan sommige moeten worden beoordeeld door en admin. Elke rij die moet worden beoordeeld, heeft zowel een pijl omhoog als een pijl omlaag om u naar het vorige of volgende item te brengen dat moet worden beoordeeld.

Hier is een compleet voorbeeld dat moet worden uitgevoerd als u een nieuw HTML-document in Kladblok maakt en opslaat. Er is extra code om de boven- en onderkant van onze artikelen te detecteren voor beoordeling, zodat we geen fouten maken.

<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>

Other episodes