У меня есть этот код для моего сайта:
function clickMe() {
var element = document.getElementById('about');
element.scrollIntoView({
block: 'start',
behavior: 'smooth',
});
}
Это работает довольно хорошо, но у меня есть фиксированный заголовок, поэтому, когда код прокручивается до элемента, заголовок мешает.
Есть ли способ иметь смещение и плавно прокручивать его?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Is there a way to have an offset and make it scroll smoothly?
scrollIntoViewOptions из Элемент.scrollIntoView () do нет позволяет вам использовать смещение. Это исключительно полезно, когда вы хотите прокрутить до позиции точный элемента.
Однако вы можете использовать Window.scrollTo () с опциями как для прокрутки до позиции компенсировать, так и для этого плавно.
Если у вас есть заголовок с высотой, например, 30px, вы можете сделать следующее:
function scrollToTargetAdjusted(){
var element = document.getElementById('targetElement');
var headerOffset = 45;
var elementPosition = element.getBoundingClientRect().top;
var offsetPosition = elementPosition - headerOffset;
window.scrollTo({
top: offsetPosition,
behavior: "smooth"
});
}
Это будет плавно прокручиваться к вашему элементу, чтобы он не был заблокирован вашим заголовком.
Примечание: вы вычитаете смещение, потому что хотите остановиться, прежде чем прокручивать заголовок по своему элементу.
Вы можете сравнить оба варианта в приведенном ниже фрагменте.
<script type = "text/javascript">
function scrollToTarget() {
var element = document.getElementById('targetElement');
element.scrollIntoView({
block: "start",
behavior: "smooth",
});
}
function scrollToTargetAdjusted() {
var element = document.getElementById('targetElement');
var headerOffset = 45;
var elementPosition = element.getBoundingClientRect().top;
var offsetPosition = elementPosition - headerOffset;
window.scrollTo({
top: offsetPosition,
behavior: "smooth"
});
}
function backToTop() {
window.scrollTo(0, 0);
}
</script>
<div id = "header" style = "height:30px; width:100%; position:fixed; background-color:lightblue; text-align:center;"> <b>Fixed Header</b></div>
<div id = "mainContent" style = "padding:30px 0px;">
<button type = "button" onclick = "scrollToTarget();">element.scrollIntoView() smooth, header blocks view</button>
<button type = "button" onclick = "scrollToTargetAdjusted();">window.scrollTo() smooth, with offset</button>
<div style = "height:1000px;"></div>
<div id = "targetElement" style = "background-color:red;">Target</div>
<br/>
<button type = "button" onclick = "backToTop();">Back to top</button>
<div style = "height:1000px;"></div>
</div>У меня это сработало очень хорошо, но для упрощения замените значение headerOffset (45) на document.getElementById ('заголовок'). offsetHeight. Это компенсирует любую отзывчивую высоту.
@coreyward +1 за подсказку window.pageYOffset
Ваше решение прокручивается в случайные места в зависимости от того, где сейчас находится представление.
Мой контейнер имеет фиксированную высоту 100vh, поэтому я не могу использовать прокрутку окна, и мне нужно использовать scrollIntoView, однако есть липкий заголовок, который нависает над элементом, в который он прокручивается. какой подход я могу использовать в этом случае?
Ответ Сёрена Д. Птеуса направил меня на правильный путь, но у меня были проблемы с getBoundingClientRect(), когда он не был наверху window.
Мое решение добавляет к нему немного больше, чтобы getBoundingClientRect() работал более стабильно и с большей универсальностью. Я использовал подход, описанный в здесь, и реализовал его, чтобы все работало так, как задумано.
const element = document.getElementById('targetElement');
const offset = 45;
const bodyRect = document.body.getBoundingClientRect().top;
const elementRect = element.getBoundingClientRect().top;
const elementPosition = elementRect - bodyRect;
const offsetPosition = elementPosition - offset;
window.scrollTo({
top: offsetPosition,
behavior: 'smooth'
});
Не забудьте включить полифил при реализации этого!
Я надеюсь, что ваш const определен в пространстве имен функции, а не глобально, потому что он будет вычислять только расстояния относительно вашего окна просмотра при загрузке страницы.
У меня сработало, спасибо. Была проблема с ответом Сорен Д. Птеус. Вы должны были быть на вершине, чтобы это работало.
Главный ответ здесь
Спасибо за это, у меня была такая же проблема, так как я использовал липкую панель навигации, это все исправило, еще раз спасибо!
Это сработало для меня очень хорошо, спасибо
Я пробовал другие решения, но у меня было странное поведение. Однако у меня это сработало.
function scrollTo(id) {
var element = document.getElementById(id);
var headerOffset = 60;
var elementPosition = element.offsetTop;
var offsetPosition = elementPosition - headerOffset;
document.documentElement.scrollTop = offsetPosition;
document.body.scrollTop = offsetPosition; // For Safari
}
и стиль:
html {
scroll-behavior: smooth;
}
Простое, но элегантное решение если элемент имеет небольшую высоту (короче окна просмотра):
element.scrollIntoView({ behavior: 'instant', block: 'center' });
block: center будет прокручивать элемент таким образом, чтобы центр элемента находился в вертикальном центре области просмотра, поэтому верхний заголовок его не закрывает.
Лучший и простой ответ здесь
Прекрасная скрытая жемчужина :)
поведение Необязательно Определяет анимацию перехода. Один из авто или плавный. По умолчанию авто. Откуда взялось «мгновение»?
Просто и точно. Любить это :)
Ого, сенсация!
Это решило примерно 3-летние проблемы с обычным scrollIntoView. Спасибо!
Я знаю, что это взлом и определенно то, что вы должны использовать с осторожностью, но вы действительно можете добавить к элементу padding и отрицательный margin. Я не могу гарантировать, что это сработает для вас, поскольку у меня нет вашей разметки и кода, но у меня была аналогичная проблема, и я использовал этот обходной путь для ее решения.
Скажем, ваш заголовок - 30px, и вам нужно смещение 15px, тогда:
#about {
padding-top: 45px; // this will allow you to scroll 15px below your 30px header
margin-top: -45px; // and this will make sure that you don't change your layout because of it
}
Хакер, но умно!
Спасибо! Надеюсь, поможет!
Вот функция, которую я написал на основе Ответ @ekfuhrmann.
Он принимает элемент, который необходимо прокрутить, в качестве первого параметра, а другие параметры в форме объекта - в качестве второго параметра, аналогично тому, как работает функция window.scrollTo().
function scrollToTarget(element, options) {
if (options.headerHeight === undefined) {
options.headerHeight = 0;
}
var elementRect = element.getBoundingClientRect();
// If an element has 0 height, then it is hidden, do not scroll
if (elementRect.height == 0) {
return;
}
var offset = elementRect.top - options.headerHeight;
if (options.block == 'center') {
// If an element's height is smaller, than the available screen height (without the height of the header), then add the half of the available space
// to scroll to the center of the screen
var availableSpace = window.innerHeight - options.headerHeight;
if (elementRect.height < availableSpace) {
offset -= (availableSpace - elementRect.height) / 2;
}
}
var optionsToPass = {
top: offset
};
if (options.behavior !== undefined) {
optionsToPass.behavior = options.behavior
}
window.scrollBy(optionsToPass);
}
Основное отличие состоит в том, что он использует функцию window.scrollBy() вместо window.scrollTo(), поэтому нам не нужно вызывать .getBoundingClientRect() на body.
Параметр options может содержать поле headerHeight - он может содержать высоту фиксированного элемента на экране, который необходимо игнорировать при прокрутке к элементу.
Эта функция также может иметь опцию block, которая пока может принимать только одно значение "center". Если установлено, элемент, к которому выполняется прокрутка, будет отображаться в центре экрана, за исключением фиксированной высоты элемента. По умолчанию прокрутка будет применена к верхней части элемента.
Здесь у нас есть два перекрывающихся элемента с фиксированным положением. Представим, что самый большой из них не виден на некоторых ширинах области просмотра, поэтому нам нужно динамически получать доступную высоту области просмотра за вычетом высоты фиксированного элемента.
В следующем примере показано, что элемент появится в центре доступной высоты области просмотра, если для параметра block установлено значение "center", аналогично тому, как работает функция Element.scrollIntoView().
function scrollToTarget(element, options) {
if (options.headerHeight === undefined) {
options.headerHeight = 0;
}
var elementRect = element.getBoundingClientRect();
if (elementRect.height == 0) {
return;
}
var offset = elementRect.top - options.headerHeight;
if (options.block == 'center') {
var availableSpace = window.innerHeight - options.headerHeight;
if (elementRect.height < availableSpace) {
offset -= (availableSpace - elementRect.height) / 2;
}
}
var optionsToPass = {
top: offset
};
if (options.behavior !== undefined) {
optionsToPass.behavior = options.behavior
}
window.scrollBy(optionsToPass);
}
var headerElements = [
document.querySelector('.header__wrap'),
document.getElementById('wpadminbar')
];
var maxHeaderHeight = headerElements.reduce(function (max, item) {
return item ? Math.max(max, item.offsetHeight) : max;
}, 0);
document.getElementById('click-me').addEventListener('click', function() {
scrollToTarget(document.querySelector('.scroll-element'), {
headerHeight: maxHeaderHeight,
block: 'center',
behavior: 'smooth'
});
});body {
margin: 0;
height: 1000px;
}
#wpadminbar, .header__wrap {
position: fixed;
top: 0;
left: 0;
right: 0;
}
#wpadminbar {
height: 32px;
background-color: #1d2327;
z-index: 2;
opacity: 0.8;
}
.header__wrap {
margin: 0 15px;
height: 74px;
background-color: #436c50;
z-index: 1;
}
.scroll-element {
margin-top: 500px;
padding: 1em;
text-align: center;
background-color: #d7d7d7;
}
#click-me {
margin: 100px auto 0;
padding: 0.5em 1em;
display: block;
}<div id = "wpadminbar"></div>
<div class = "header__wrap"></div>
<button id = "click-me">Click me!</button>
<!-- Some deeply nested HTML element -->
<div class = "scroll-element">
You scrolled to me and now I am in the visual center of the screen. Nice!
</div>Также есть поля прокрутки и прокрутки.
Для меня прокрутка наиболее полезна для такого рода вещей.
https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-padding-top
https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-margin-top
/* Keyword values */
scroll-padding-top: auto;
/* <length> values */
scroll-padding-top: 10px;
scroll-padding-top: 1em;
scroll-padding-top: 10%;
/* Global values */
scroll-padding-top: inherit;
scroll-padding-top: initial;
scroll-padding-top: unset;
Однако, скорее всего, он несовместим с Internet Explorer.
Только что видел это в новом видео Fireship! Для плавной прокрутки используйте scroll-behavior: smooth;
Сорен Д. Птеус почти прав, но он работает только тогда, когда пользователь находится сверху. Это потому, что getBoundingClientRect всегда будет получать относительную высоту, а использование window.scrollTo с относительной высотой не работает.
Ekfuhrmann улучшил ответ, получив общую высоту от элемента тела и вычислив реальную высоту. Однако я думаю, что это может быть проще, мы можем просто использовать относительное положение и использовать window.scrollBy.
Примечание. Основное отличие - window.scrollBy.
const HEADER_HEIGHT = 45;
function scrollToTargetAdjusted(){
const element = document.getElementById('targetElement');
const elementPosition = element.getBoundingClientRect().top;
const offsetPosition = elementPosition - HEADER_HEIGHT;
window.scrollBy({
top: offsetPosition,
behavior: "smooth"
});
}
Спасибо. Это должен быть принятый ответ.
Собственно, это должен быть принятый.
С помощью очень небольшого взлома вы можете заставить его работать с scrollIntoView ()
<section id = "about">
<p>About title</p>
<p>About description</p>
</section>
<section id = "profile">
<p>About title</p>
<p>About description</p>
</section>
<section>
<span className = "section-offset" id = "about"></span>
<!-- or <span className = "section-offset" id = "about" /> for React -->
<p>About title</p>
<p>About description</p>
</section>
<section>
<span className = "section-offset" id = "profile"></span>
<p>Profile title</p>
<p>Profile description</p>
</section>
.section-offset {
position: relative;
bottom: 60px; // <<< your offset here >>>
}
Переместите селектор элемента в диапазон внутри раздела, затем вы можете использовать position: relative в диапазоне (размещение сверху / снизу не влияет на другие элементы на странице), чтобы установить необходимое смещение. Если вам нужно смещение снизу, поместите элемент span в конце раздела (например, перед </section>).
getBoundingClientRect().topотносится к расстоянию между элементом и верхней частью области просмотра. Это более полезно, когда вы хотите использоватьscrollBy, который прокручивается относительно текущей позиции, чемscrollTo, который является абсолютным. Если элемент не находится в относительно позиционируемом родителе, рассмотрите возможность использования вместо негоoffsetTop. В противном случае вам нужно добавитьwindow.pageYOffsetкgetBoundingClientRect().top, чтобы получить правильное значение дляscrollTo.