scrollIntoView Прокручується занадто далеко


107

У мене є сторінка, де смуга прокрутки, що містить рядки таблиці з divs в них, динамічно генерується з бази даних. Кожен рядок таблиці діє як посилання, подібно до того, як ви бачили б у списку відтворення YouTube поруч із відеопрогравачем.

Коли користувач відвідує сторінку, опція, яку він має, повинна переходити у верхню частину прокручуваного div. Ця функціональність працює. Справа в тому, що це заходить трохи трохи далеко. Як і варіант, який вони використовують, приблизно 10px занадто високий. Отже, сторінку відвідують, за допомогою URL-адреси визначають, який параметр було обрано, а потім прокручують цю опцію до верхньої частини прокручуваного div. Примітка: Це не смуга прокрутки вікна, це div з смугою прокрутки.

Я використовую цей код, щоб змусити перемістити вибрану опцію у верхню частину div:

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

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

el.scrollIntoView(true);

Він переміщує його у верхню частину div, але приблизно на 10 пікселів занадто далеко вгору. Хтось знає, як це виправити?


44
Відсутність конфігурації зсуву для scrollIntoViewтурбує.
mystrdat

Відповіді:


57

Якщо це приблизно 10 пікселів, то я думаю, ви можете просто вручну відрегулювати divзміщення прокрутки, що містить , так:

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

1
Чи матиме це такий самий анімований ефект, як scrollIntoView?
1252748,

1
@thomas AFAIK, звичайна scrollIntoViewфункція DOM не викликає анімацію. Ви говорите про плагін scrollIntoView jQuery?
Lucas Trzesniewski

1
Це спрацювало, за винятком того, що це був неправильний напрямок, тому все, що мені потрібно було зробити, змінити його з + = 10 на - = 10, і зараз він завантажується в самий раз, дякую за допомогу !!!!
Matthew Wilson

Так. Я розгубився. Я думав, це анімація. Вибачте!
1252748

17
Це може оживити, просто додайте el.scrollIntoView({ behavior: 'smooth' });. Хоча підтримка не дуже велика!
daveaspinall

108

Плавно прокрутіть до належного положення

Отримайте правильну y координату та використовуйте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'});

2
Це найбільш правильна відповідь - ми можемо легко знайти позицію прокрутки за допомогою, jQuery('#el').offset().topа потім використовуватиwindow.scrollTo
Олександр Гончаров

1
+1 для цього рішення. Це допомогло мені правильно прокрутити до елемента, коли у мене був контейнер, розміщений відносно верхнього зміщення.
Ліга

Хто прийшов сюди, використовуючи HashLink з React Router, це те, що мені вдалося. У вікні прокрутки на вашому HashLink, scroll={el => { const yCoordinate = el.getBoundingClientRect().top + window.pageYOffset; const yOffset = -80; window.scrollTo({ top: yCoordinate + yOffset, behavior: 'smooth' }); }}- де зміщення на 10-15 пікселів більше, ніж розмір вашого заголовка, лише для кращого інтервалу
Роб Б,

86

Ви можете зробити це у два етапи:

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

23
Якщо scrollIntoView()прокрутка не закінчилася перед scrollBy()запуском, остання прокрутка скасує першу. Принаймні на Chrome 71.
Дейв Хьюз,

1
У цьому випадку використовуйте setTimeout, як видно з відповіді нижче: stackoverflow.com/a/51935129/1856258 .. Для мене це працює без часу очікування. Дякую!
leole

чи існує спосіб безпосереднього введення коректора пікселя в scrollIntoView, щоб змусити програму рухатися безпосередньо до відповідного місця? Мовляв scrollIntoView({adjustment:y}), я думаю, це повинно бути можливим за допомогою спеціального коду в кінці
Webwoman

78

Позиціонувати якір за абсолютним методом

Інший спосіб зробити це - розташувати прив’язки точно там, де ви хочете, на сторінці, а не покладатися на прокрутку за допомогою зміщення. Я вважаю, що це дозволяє краще контролювати кожен елемент (наприклад, якщо ви хочете інший зсув для певних елементів), а також може бути більш стійким до змін / відмінностей API браузера.

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

Тепер зміщення вказано як -100px щодо елемента. Створіть функцію для створення цього прив'язки для повторного використання коду, або якщо ви використовуєте сучасний фреймворк JS, такий як React, зробіть це, створивши компонент, який відображає ваш прив'язок, та передайте ім'я прив'язки та вирівнювання для кожного елемента, що може або може не бути однаковим.

Тоді просто використовуйте:

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

Для плавної прокрутки зі зміщенням 100 пікселів.


12
Це, безумовно, найкращий спосіб здійснити це плавно і просто.
catch22

2
Я думаю, найкращий і найрідніший метод.
Даниил Пронин

1
Це так просто і гладко. Багато інших просто додають непотрібні JS.
RedSands

2
Це особливо корисно, коли посилання типу "зверху сторінки" знаходиться під панеллю навігації, і ви хочете показати навігацію, але не хочете, щоб будь-які інші посилання зміщувались
Джо Мун

ви можете досягти того ж, використовуючи негативне поле зверху, ви уникаєте обгортання елемента разом з іншим з відносною позицією
Сергі

15

Я вирішив цю проблему за допомогою,

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

Це змушує елемент з’являтися в centerпісля прокрутки, тому мені не потрібно обчислювати yOffset.

Сподіваюся, це допомагає ...


1
Вам слід використовувати scrollToне на, elementа наwindow
Арсеній-II

@ Arseniy-II Дякую, що вказали на це !!. Я пропустив цю частину!
Імтіаз Шакіл

блок допомагає зверху?
Ашок кумар Ганесан,

11

Це працює у мене в Chrome (з плавною прокруткою та без синхронізації)

Він просто переміщує елемент, ініціює прокрутку, а потім переміщує його назад.

Немає видимих ​​"спливаючих", якщо елемент вже є на екрані.

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;

Це найкраще рішення ... Хоч би це було позначено як відповідь. Врятував би пару годин.
Шивам

7

Спираючись на попередню відповідь, я роблю це в проекті Angular5.

Почав з:

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

Але це дало деякі проблеми і потрібно було встановитиTimeout для scrollBy () так:

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

І це чудово працює в MSIE11 та Chrome 68+. Я не тестував у FF. 500 мс - це найкоротша затримка, на яку я наважуся. Опускатися нижче часом не вдавалося, оскільки плавне прокручування ще не завершилося. Налаштуйте, як потрібно для власного проекту.

+1 до Fred727 за це просте, але ефективне рішення.


6
Це здається трохи незграбним, щоб плавно прокручувати елемент, а потім трохи прокручувати вгору після.
catch22

6

Якщо припустити, що ви хочете прокрутити до div, які знаходяться на одному рівні в DOM і мають назву класу "scroll-with-offset", цей CSS вирішить проблему:

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

Зсув від верхньої частини сторінки становить 100 пікселів. Він працюватиме лише за призначенням із блоком: 'start':

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

Що відбувається, так це те, що верхня точка divs знаходиться в звичайному місці, але їх внутрішній вміст починається на 100 пікселів нижче звичайного місця. Для цього призначений padding-top: 100px. margin-bottom: -100px - це компенсація нижчого додаткового поля div. Щоб зробити рішення повним, також додайте цей CSS, щоб компенсувати поля / відступи для найвищих та найнижчих div:

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

6

Це рішення належить @ Arseniy-II , я щойно спростив його до функції.

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

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

Використання (ви можете відкрити консоль прямо тут, у StackOverflow і протестувати її):

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


3

У мене це є, і це чудово працює для мене:

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

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

Сподіваюся, це допомагає.


2

Іншим рішенням є використання "offsetTop", наприклад:

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

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

2

Тож, можливо, це трохи незграбно, але поки що так добре. Я працюю в кутовій 9.

файл .ts

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

файл .html

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

Я регулюю зміщення за допомогою поля та верхнього відбитка.

Салюдо!


1

Знайдено вирішення проблеми. Скажімо, що ви хочете прокрутити до div, наприклад, тут елемент, і ви хочете мати інтервал 20px над ним. Встановіть посилання на створений div над ним:

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

Це створить потрібний інтервал.

Якщо у вас є заголовок, створіть також порожній div за заголовком і призначте йому висоту, рівну висоті заголовка, і посилайтеся на нього.


0

Моя основна ідея - створити tempDiv над поданням, до якого ми хочемо прокрутити. Це добре працює, не відстаючи в моєму проекті.

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

Приклад використання

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

Сподіваюся, це допоможе


0

На основі відповіді Арсенія-II: у мене був варіант використання, коли суть прокрутки була не самим вікном, а внутрішнім шаблоном (в даному випадку div). У цьому сценарії нам потрібно встановити ідентифікатор для прокручуваного контейнера і отримати його через, getElementByIdщоб використовувати його функцію прокрутки:

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

Залишивши це тут на випадок, якщо хтось зіткнеться з подібною ситуацією!


0

Ось мої 2 центи.

У мене також виникла проблема scrollIntoView, яка трохи прокручується за елементом, тому я створив скрипт (власний javascript), який додає елемент до місця призначення, трохи розміщує його у верхній частині css і прокручує до цього. Після прокрутки я знову видаляю створені елементи.

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:

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

Сподіваюся, це допоможе комусь!


0

Я додаю ці поради щодо css для тих, хто не вирішив цю проблему наведеними вище рішеннями:

#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: Я створив пакет npm, який працює краще, ніж наступне рішення, і простіший у використанні.

Моя функція smoothScroll

Я взяв чудове рішення Стіва Бантона і написав функцію, яка робить його більш зручним у використанні. Це було б простіше просто використовувати window.scroll()або навіть window.scrollBy(), як я вже пробував, але ці два мають деякі проблеми:

  • Після їх використання з плавною поведінкою все стає неприємним.
  • Ви все одно не можете їх запобігти, і вам доведеться чекати, поки прокрутиться і. Тож сподіваюся, моя функція буде для вас корисною. Крім того, існує легкий поліфіл, що змушує його працювати в Safari і навіть IE.

Ось код

Просто скопіюйте його і зіпсуйте це, як завгодно.

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

Щоб створити елемент посилання, просто додайте наступний атрибут даних:

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

Також ви можете встановити інший атрибут як доповнення

data-smooth-scroll-block="center"

Він представляє blockопцію scrollIntoView()функції. За замовчуванням це start. Детальніше про MDN .

Нарешті

Налаштуйте функцію smoothScroll відповідно до ваших потреб.

Наприклад, якщо у вас є якийсь фіксований заголовок (або я називаю його словом masthead), ви можете зробити щось подібне:

const mastheadEl = document.querySelector(someMastheadSelector);

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

const EXTRA_OFFSET = mastheadEl.offsetHeight - 3;

Якщо у вас немає такої справи, то просто видаліть її, чому б ні :-D.


0

Для елемента в рядку таблиці використовуйте JQuery, щоб отримати рядок вище, ніж ви хочете , і просто прокрутіть до цього рядка.

Припустимо, у мене є кілька рядків у таблиці, деякі з яких повинні переглянути адміністратор. Кожен рядок, що вимагає перегляду, має як стрілки вгору, так і стрілку вниз, щоб перейти до попереднього або наступного пункту для перегляду.

Ось повний приклад, який слід просто запустити, якщо ви створюєте новий документ HTML у блокноті та зберігаєте його. Існує додатковий код для виявлення верхньої та нижньої частини наших предметів для перевірки, щоб ми не видавали помилок.

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

-1

Просте рішення для прокрутки певного елемента вниз

const element = document.getElementById("element-with-scroll");
element.scrollTop = element.scrollHeight - 10;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.