scrollIntoView Scrollt einfach zu weit

105

Ich habe eine Seite, auf der eine Bildlaufleiste mit Tabellenzeilen mit Divs dynamisch aus der Datenbank generiert wird. Jede Tabellenzeile verhält sich wie ein Link, wie Sie ihn auf einer YouTube-Wiedergabeliste neben dem Videoplayer sehen würden.

Wenn ein Benutzer die Seite besucht, sollte die Option, auf der er sich befindet, oben im Bildlaufbereich angezeigt werden. Diese Funktionalität funktioniert. Das Problem ist, dass es nur ein bisschen zu weit geht. Wie die Option, die sie sind, ist etwa 10px zu hoch. Wenn die Seite besucht wird, wird die URL verwendet, um zu identifizieren, welche Option ausgewählt wurde, und diese Option wird dann zum oberen Rand des Bildlauf-Divs gescrollt. Hinweis: Dies ist nicht die Bildlaufleiste für das Fenster, sondern ein Div mit einer Bildlaufleiste.

Ich verwende diesen Code, um die ausgewählte Option an den oberen Rand des div zu verschieben:

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

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

el.scrollIntoView(true);

Es verschiebt es an die Spitze des Div, aber ungefähr 10 Pixel zu weit oben. Weiß jemand, wie man das behebt?

Matthew Wilson
quelle
44
Das Fehlen einer Offset-Konfiguration für scrollIntoViewist besorgniserregend.
Mystrdat

Antworten:

56

Wenn es ungefähr 10px ist, können Sie den Bildlaufversatz des Inhalts einfach divwie folgt manuell anpassen :

el.scrollIntoView(true);
document.getElementById("containingDiv").scrollTop -= 10;
Lucas Trzesniewski
quelle
1
Wird dies den gleichen animierten Effekt haben wie scrollIntoView?
1252748
1
@ Thomas AFAIK, die einfache DOM- scrollIntoViewFunktion verursacht keine Animation. Sprechen Sie über das scrollIntoView jQuery-Plugin?
Lucas Trzesniewski
1
Das hat funktioniert, außer dass es die falsche Richtung war, also musste ich es nur von + = 10 auf - = 10 ändern und jetzt wird es genau richtig geladen, vielen Dank für die Hilfe !!!!
Matthew Wilson
Ja. Ich war verwirrt. Ich fand es animiert. Es tut uns leid!
1252748
16
Es kann animieren, einfach hinzufügen el.scrollIntoView({ behavior: 'smooth' });. Support ist aber nicht so toll!
Daveaspinall
106

Scrollen Sie sanft zu einer richtigen Position

Holen Sie sich die richtige y Koordinate und verwendenwindow.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'});
Arseniy-II
quelle
2
Dies ist die richtigste Antwort - wir können leicht die Position der Schriftrolle finden jQuery('#el').offset().topund dann verwendenwindow.scrollTo
Alexander Goncharov
1
+1 für diese Lösung. Es hat mir geholfen, korrekt zum Element zu scrollen, wenn ich einen Container relativ mit einem oberen Versatz positioniert hatte.
Liga
Jeder, der mit HashLink mit React Router hierher kommt, hat bei mir funktioniert. In der Scroll-Requisite auf Ihrem HashLink scroll={el => { const yCoordinate = el.getBoundingClientRect().top + window.pageYOffset; const yOffset = -80; window.scrollTo({ top: yCoordinate + yOffset, behavior: 'smooth' }); }}- wo der Versatz 10-15 Pixel mehr als die Größe Ihres Headers ist, nur um den Abstand zu verbessern
Rob B
84

Sie können dies in zwei Schritten tun:

el.scrollIntoView(true);
window.scrollBy(0, -10); // Adjust scrolling with a negative value here
fred727
quelle
22
Wenn der scrollIntoView()Bildlauf vor scrollBy()dem Ausführen noch nicht abgeschlossen ist , wird der erstere Bildlauf abgebrochen. Zumindest auf Chrome 71.
Dave Hughes
1
In diesem Fall verwenden Sie das setTimeout wie in der folgenden Antwort gezeigt: stackoverflow.com/a/51935129/1856258 .. Bei mir funktioniert es ohne Timeout. Vielen Dank!
Leole
Gibt es eine Möglichkeit, den Korrektor eines Pixels direkt in scrollIntoView einzufügen, damit die Anwendung direkt an den entsprechenden Ort verschoben wird? Wie scrollIntoView({adjustment:y}), ich denke , es sollte am Ende mit einem benutzerdefinierten Code möglich sein
webwoman
78

Positionieren Sie den Anker nach der absoluten Methode

Eine andere Möglichkeit besteht darin, die Anker genau dort zu positionieren, wo Sie sie auf der Seite haben möchten, anstatt sich auf das Scrollen nach Versatz zu verlassen. Ich finde, es ermöglicht eine bessere Kontrolle für jedes Element (z. B. wenn Sie für bestimmte Elemente einen anderen Versatz wünschen) und ist möglicherweise auch widerstandsfähiger gegen Änderungen / Unterschiede der Browser-API.

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

Jetzt wird der Versatz als -100px relativ zum Element angegeben. Erstellen Sie eine Funktion zum Erstellen dieses Ankers für die Wiederverwendung von Code. Wenn Sie ein modernes JS-Framework wie React verwenden, erstellen Sie dazu eine Komponente, die Ihren Anker rendert, und übergeben Sie den Ankernamen und die Ausrichtung für jedes Element, das möglicherweise oder möglicherweise nicht gleich sein.

Dann benutze einfach:

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

Für flüssiges Scrollen mit einem Versatz von 100px.

Steve Banton
quelle
12
Dies ist bei weitem der beste Weg, dies reibungslos und einfach umzusetzen.
catch22
2
Die beste und einheimischste Methode, denke ich.
17аниил Пронин
1
Das ist so einfach und schick. Viele der anderen fügen nur nicht benötigtes JS hinzu.
RedSands
2
Dies ist besonders nützlich, wenn sich ein Link vom Typ "Seite oben" unter einer Navigationsleiste befindet und Sie die Navigation anzeigen möchten, aber keine anderen Links versetzen möchten
Joe Moon,
Sie können dasselbe mit einem negativen Rand oben erreichen. Sie vermeiden es, das Element zusammen mit einem anderen Element mit relativer Position zu verpacken
Sergi
14

Ich habe dieses Problem gelöst mit:

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

Dadurch wird das Element centernach dem Scrollen angezeigt, sodass ich nicht berechnen muss yOffset.

Ich hoffe es hilft...

Imtiaz Shakil Siddique
quelle
1
Sie sollten scrollTonicht am, elementsondern amwindow
Arseniy-II
@ Arseniy-II Danke, dass du darauf hingewiesen hast !! Ich habe diesen Teil verpasst!
Imtiaz Shakil Siddique
Hilft der Block für oben?
Ashok Kumar Ganesan
10

Dies funktioniert bei mir in Chrome (mit reibungslosem Scrollen und ohne Timing-Hacks)

Es bewegt nur das Element, leitet den Bildlauf ein und bewegt es dann zurück.

Es gibt kein sichtbares "Knallen", wenn sich das Element bereits auf dem Bildschirm befindet.

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;
Ryan Wie
quelle
7

Aufbauend auf einer früheren Antwort mache ich dies in einem Angular5-Projekt.

Begann mit:

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

Dies gab jedoch einige Probleme und musste Timeout für scrollBy () wie folgt festlegen:

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

Und es funktioniert perfekt in MSIE11 und Chrome 68+. Ich habe nicht in FF getestet. 500 ms war die kürzeste Verzögerung, die ich wagen würde. Das Absenken schlug manchmal fehl, da die glatte Schriftrolle noch nicht abgeschlossen war. Passen Sie die Anforderungen für Ihr eigenes Projekt an.

+1 an Fred727 für diese einfache, aber effektive Lösung.

PrimaryW
quelle
6
Dies fühlt sich etwas ruckelig an, wenn Sie sanft zu einem Element scrollen und danach etwas nach oben scrollen.
catch22
6

Angenommen, Sie möchten zu den Divs scrollen, die sich alle in DOM auf derselben Ebene befinden und den Klassennamen "Scroll-with-Offset" haben, dann löst dieses CSS das Problem:

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

Der Versatz vom oberen Rand der Seite beträgt 100 Pixel. Es funktioniert nur wie vorgesehen mit Block: 'start':

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

Was passiert ist, dass sich der oberste Punkt der Divs am normalen Ort befindet, ihr innerer Inhalt jedoch 100 Pixel unter dem normalen Ort beginnt. Dafür ist Padding-Top: 100px da. Rand-unten: -100px dient zum Ausgleich des zusätzlichen Randes des unteren Div. Um die Lösung zu vervollständigen, fügen Sie auch dieses CSS hinzu, um die Ränder / Auffüllungen für die obersten und untersten Divs zu versetzen:

.top-div {
  padding-top: 0;
}
.bottom-div {
  margin-bottom: 0;
}
Georgy Petukhov
quelle
6

Diese Lösung gehört zu @ Arseniy-II , ich habe sie gerade zu einer Funktion vereinfacht.

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

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

Verwendung (Sie können die Konsole hier in StackOverflow öffnen und testen):

_scrollTo('#question-header', 0);
Diego Fortes
quelle
4

Sie können auch die element.scrollIntoView() Optionen verwenden

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

welche die meisten Browser unterstützen

Nicholas Murray
quelle
33
Dies unterstützt jedoch keine Offset-Option.
Adriendenat
3

Ich habe das und es funktioniert hervorragend für mich:

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

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

Ich hoffe es hilft.

Fernando Brandao
quelle
2

Eine andere Lösung besteht darin, "offsetTop" wie folgt zu verwenden:

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

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

Also, vielleicht ist das ein bisschen klobig, aber soweit so gut. Ich arbeite in Winkel 9.

Datei .ts

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

Datei .html

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

Ich passe den Versatz mit Rand und Polsterung oben an.

Saludos!

Ignacio Basti
quelle
1

Es wurde eine Problemumgehungslösung gefunden. Angenommen, Sie möchten zu einem div scrollen, z. B. Element hier, und Sie möchten einen Abstand von 20 Pixel darüber haben. Setzen Sie den Ref auf ein erstelltes Div darüber:

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

Dadurch wird der gewünschte Abstand erstellt.

Wenn Sie eine Kopfzeile haben, erstellen Sie auch hinter der Kopfzeile ein leeres div und weisen Sie ihm eine Höhe zu, die der Höhe der Kopfzeile entspricht, und verweisen Sie darauf.

Ibrahim Al Masri
quelle
0

Meine Hauptidee ist es, ein tempDiv über der Ansicht zu erstellen, zu der wir scrollen möchten. Es funktioniert gut, ohne in meinem Projekt zu verzögern.

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

Beispiel mit

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

Hoffe es hilft

Phan Van Linh
quelle
0

Basierend auf der Antwort von Arseniy-II: Ich hatte den Anwendungsfall, bei dem die scrollende Entität nicht das Fenster selbst war, sondern eine innere Vorlage (in diesem Fall ein div). In diesem Szenario müssen wir eine ID für den Bildlaufcontainer festlegen und diese über getElementByIddie Bildlauffunktion abrufen:

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

Lassen Sie es hier, falls jemand in eine ähnliche Situation gerät!

kounex
quelle
0

Hier sind meine 2 Cent.

Ich hatte auch das Problem, dass scrollIntoView ein wenig über das Element hinaus scrollt, also habe ich ein Skript (natives Javascript) erstellt, das dem Ziel ein Element voranstellt, es mit CSS etwas nach oben positioniert und zu diesem gescrollt. Nach dem Scrollen entferne ich die erstellten Elemente wieder.

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

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

Hoffe das hilft jedem!

Rubeus
quelle
0

Ich füge diese CSS-Tipps für diejenigen hinzu, die dieses Problem mit den oben genannten Lösungen nicht gelöst haben:

#myDiv::before {
  display: block;
  content: " ";
  margin-top: -90px; // adjust this with your header height
  height: 90px; // adjust this with your header height
  visibility: hidden;
}
Jérémy MARQUER
quelle
0

UPD: Ich habe ein npm-Paket erstellt , das besser als die folgende Lösung funktioniert und einfacher zu verwenden ist.

Meine SmoothScroll-Funktion

Ich habe die wunderbare Lösung von Steve Banton genommen und eine Funktion geschrieben, die die Verwendung bequemer macht. Es wäre einfacher, es einfach zu benutzen window.scroll()oder sogar window.scrollBy(), wie ich es zuvor versucht habe, aber diese beiden haben einige Probleme:

  • Alles wird Junkie, nachdem man sie mit einem reibungslosen Verhalten verwendet hat.
  • Sie können sie sowieso nicht verhindern und müssen bis zum und der Schriftrolle warten. Ich hoffe also, dass meine Funktion für Sie nützlich sein wird. Außerdem gibt es eine leichte Polyfüllung , mit der es in Safari und sogar im Internet Explorer funktioniert.

Hier ist der Code

Kopieren Sie es einfach und machen Sie es fertig, wie immer Sie wollen.

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

Um ein Linkelement zu erstellen, fügen Sie einfach das folgende Datenattribut hinzu:

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

Sie können auch ein anderes Attribut als Ergänzung festlegen

data-smooth-scroll-block="center"

Es repräsentiert die blockOption der scrollIntoView()Funktion. Standardmäßig ist es start. Lesen Sie mehr über MDN .

Schließlich

Passen Sie die SmoothScroll-Funktion an Ihre Bedürfnisse an.

Wenn Sie beispielsweise einen festen Header haben (oder ich nenne ihn mit dem Wort masthead), können Sie Folgendes tun:

const mastheadEl = document.querySelector(someMastheadSelector);

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

const EXTRA_OFFSET = mastheadEl.offsetHeight - 3;

Wenn Sie einen solchen Fall nicht haben, löschen Sie ihn einfach. Warum nicht :-D.

Dmitry Zakharov
quelle
0

Verwenden Sie für ein Element in einer Tabellenzeile JQuery, um die Zeile über der gewünschten Zeile abzurufen , und scrollen Sie stattdessen einfach zu dieser Zeile.

Angenommen, ich habe mehrere Zeilen in einer Tabelle, von denen einige von und admin überprüft werden sollten. Jede zu überprüfende Zeile enthält sowohl einen Aufwärts- als auch einen Abwärtspfeil, mit dem Sie zum vorherigen oder nächsten Element zur Überprüfung gelangen.

Hier ist ein vollständiges Beispiel, das nur ausgeführt werden sollte, wenn Sie ein neues HTML-Dokument im Editor erstellen und speichern. Es gibt zusätzlichen Code, um die Ober- und Unterseite unserer Artikel zur Überprüfung zu erkennen, damit wir keine Fehler auslösen.

<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>
Martin Francis
quelle
-1

Einfache Lösung zum Scrollen eines bestimmten Elements nach unten

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