我有一个页面,其中包含从数据库动态生成的带有 div 的表行的滚动条。每个表格行就像一个链接,有点像您在视频播放器旁边的 YouTube 播放列表中看到的。
当用户访问该页面时,他们所在的选项应该转到滚动 div 的顶部。此功能正在运行。问题是它走得太远了。就像他们打开的选项一样,大约 10px 太高了。因此,页面被访问,url 用于识别选择了哪个选项,然后将该选项滚动到滚动 div 的顶部。注意:这不是窗口的滚动条,它是一个带有滚动条的 div。
我正在使用此代码使其将所选选项移动到 div 的顶部:
var pathArray = window.location.pathname.split( '/' );
var el = document.getElementById(pathArray[5]);
el.scrollIntoView(true);
它将它移动到 div 的顶部,但向上移动了大约 10 个像素。有谁知道如何解决这个问题?
scrollIntoView
缺少偏移配置令人不安。
scroll-margin
和 scroll-padding
。
平滑滚动到合适的位置
获取正确 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'});
绝对法定位锚
另一种方法是将锚点准确定位在页面上您想要的位置,而不是依靠偏移量滚动。我发现它可以更好地控制每个元素(例如,如果您希望某些元素具有不同的偏移量),并且还可能更能抵抗浏览器 API 更改/差异。
<div id="title-element" style="position: relative;">
<div id="anchor-name" style="position: absolute; top: -100px; left: 0"></div>
</div>
现在偏移量指定为相对于元素的 -100px。创建一个函数来创建此锚点以进行代码重用,或者如果您正在使用诸如 React 之类的现代 JS 框架,请通过创建一个呈现锚点的组件来执行此操作,并为每个元素传递锚点名称和对齐方式,这可能是也可能是不一样。
然后只需使用:
const element = document.getElementById('anchor-name')
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
用于偏移 100px 的平滑滚动。
您可以分两步完成:
el.scrollIntoView(true);
window.scrollBy(0, -10); // Adjust scrolling with a negative value here
scrollBy()
运行之前 scrollIntoView()
尚未完成滚动,则后者的滚动将取消前者。至少在 Chrome 71 上。
scrollIntoView({adjustment:y})
一样,我认为应该可以在最后使用自定义代码
如果它大约是 10 像素,那么我想您可以像这样简单地手动调整包含 div
的滚动偏移量:
el.scrollIntoView(true);
document.getElementById("containingDiv").scrollTop -= 10;
scrollIntoView
相同的动画效果吗?
scrollIntoView
函数不会产生动画。你在谈论 scrollIntoView jQuery 插件吗?
el.scrollIntoView({ behavior: 'smooth' });
。虽然支持不是很好!
我通过使用解决了这个问题,
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
这使得滚动后元素出现在center
中,因此我不必计算yOffset
。
希望能帮助到你...
element
上而是在 window
上使用 scrollTo
这在 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;
CSS 滚动边距和滚动填充
您可能想看看新的 CSS 属性 scroll-padding
和 scroll-margin
。您可以将 scroll-padding
用于滚动容器(在本例中为 html
),将 scroll-margin
用于容器内的元素。
对于您的示例,您需要为要滚动到视图中的元素添加 scroll-margin-top
,如下所示:
.example {
scroll-margin-top: 10px;
}
这会影响 scrollIntoView
代码,例如以下代码:
const el = document.querySelector(".example");
el.scrollIntoView({block: "start", behavior: "smooth"});
这将导致视口滚动以将视口的顶部边框与元素的顶部边框对齐,但有 10px 的额外空间。换句话说,元素的这些属性被考虑在内:
填充顶部
边框顶部
滚动边距顶部
(而不是边缘顶部)
此外,如果 html
元素设置了 scroll-padding-top
,那么这也会被考虑在内。
scrollIntoView
之前完成对 DOM 的所有更改,方法是使用类似 Vue.nextTick(() => el.scrollIntoView())
id
或 name
的元素,但不是在所有 AJAX 之前已完成加载。如果您在页面加载后导致布局发生变化,则导航到带有片段的 URL 可能不会滚动到预期位置。
在 20 秒内修复它:
此解决方案属于@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);
我目前正在生产中使用它,它工作得很好。
const yOffset = document.getElementById('nav_id').getBoundingClientRect().height
传入我还必须稍微修改为 -
yOffset
而不是 +
,因为我的内容滚动得太远向上页面(在导航下)
另一种解决方案是使用“offsetTop”,如下所示:
var elementPosition = document.getElementById('id').offsetTop;
window.scrollTo({
top: elementPosition - 10, //add your necessary value
behavior: "smooth" //Smooth transition to roll
});
基于较早的答案,我在 Angular5 项目中执行此操作。
开始于:
// el.scrollIntoView(true);
el.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
window.scrollBy(0, -10);
但这带来了一些问题,需要像这样为 scrollBy() 设置超时时间:
//window.scrollBy(0,-10);
setTimeout(() => {
window.scrollBy(0,-10)
}, 500);
它在 MSIE11 和 Chrome 68+ 中完美运行。我没有在FF中测试过。 500 毫秒是我敢冒险的最短延迟。由于平滑滚动尚未完成,下降有时会失败。根据您自己的项目的需要进行调整。
为这个简单但有效的解决方案 +1 到 Fred727。
假设您想滚动到 DOM 中所有处于同一级别且类名为“scroll-with-offset”的 div,那么这个 CSS 将解决问题:
.scroll-with-offset {
padding-top: 100px;
margin-bottom: -100px;
}
与页面顶部的偏移量为 100px。它只会按预期使用 block: 'start':
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
发生的情况是 div 的顶点位于正常位置,但它们的内部内容从正常位置下方 100px 开始。这就是 padding-top:100px 的用途。 margin-bottom: -100px 是为了抵消下面 div 的额外边距。为了使解决方案完整,还添加此 CSS 以抵消最顶部和最底部 div 的边距/填充:
.top-div {
padding-top: 0;
}
.bottom-div {
margin-bottom: 0;
}
我有这个,它对我来说非常有用:
// add a smooth scroll to element
scroll(el) {
el.scrollIntoView({
behavior: 'smooth',
block: 'start'});
setTimeout(() => {
window.scrollBy(0, -40);
}, 500);}
希望能帮助到你。
所以,也许这有点笨拙,但到目前为止还不错。我在角度 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>
我用边距和填充顶部调整偏移量。
萨鲁多斯!
如果您使用的是 Ionic Capacitor、Angular Material 并且需要支持 iOS 11 的解决方案。
document.activeElement.parentElement.parentElement.scrollIntoView({block: 'center', behavior: 'smooth'});
关键是滚动到父级的父级,它是输入的包装器。此包装器包含现在不再被切断的输入标签。
如果您只需要支持 iOS 14,“块”中心参数实际上可以工作,所以这就足够了:
document.activeElement.scrollIntoView({block: 'center', behavior: 'smooth'});
最简单的方法是,
html, body {
scroll-behavior: smooth;
}
html [id], body [id] {
scroll-margin: 50px !important;
}
使用您提供的代码
var pathArray = window.location.pathname.split( '/' );
var el = document.getElementById(pathArray[5]);
el.style.scrollMargin = 50px !important;
el.scrollIntoView(true);
找到了解决方法。假设您要滚动到一个 div,例如此处的 Element,并且您希望在其上方有 20px 的间距。将 ref 设置为在其上方创建的 div:
<div ref={yourRef} style={{position: 'relative', bottom: 20}}/> <Element />
这样做将创建您想要的间距。
如果您有标题,请在标题后面创建一个空 div,并为其分配一个等于标题高度的高度并引用它。
我的主要想法是在我们想要滚动到的视图上方创建一个 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);
}
希望有帮助
根据 Arseniy-II 的回答:我有一个用例,其中滚动实体不是窗口本身,而是一个内部模板(在这种情况下是一个 div)。在这种情况下,我们需要为滚动容器设置一个 ID,并通过 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'
});
把它留在这里以防有人面临类似的情况!
这是我的 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;
}
希望这对任何人都有帮助!
我为那些没有通过上述解决方案解决此问题的人添加此 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;
}
UPD:我创建了一个 npm 包,它比以下解决方案效果更好且更易于使用。
我的 smoothScroll 功能
我采用了 Steve Banton 的绝妙解决方案,并编写了一个函数,使其使用起来更方便。正如我之前尝试过的那样,仅使用 window.scroll()
甚至 window.scrollBy()
会更容易,但这两个存在一些问题:
使用它们并具有流畅的行为后,一切都变得垃圾。
无论如何您都无法阻止它们,而必须等到卷轴的和。所以我希望我的功能对你有用。此外,还有一个轻量级的 polyfill,使它可以在 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"
它表示 scrollIntoView()
函数的 block
选项。默认情况下,它是 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。
我尝试使用上面的一些答案,但滚动不起作用。经过一番挖掘,我注意到滚动位置实际上非常奇怪(甚至是负值),这是由 Angular 如何渲染 router-outlet
中的页面造成的。
我的解决方案是从页面顶部开始计算元素的滚动位置。我在模板的开头放置了一个 id 为 start-of-page
的元素,并通过减去目标片段位置获得滚动量。
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'});
}
一种解决方案是创建一个相对于目标偏移的不可见临时元素,滚动到它,然后将其删除。这可能并不理想,但在我工作的环境中,其他解决方案并不适合我。例如:
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();
};
对于表格行中的元素,使用 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>
不定期副业成功案例分享
jQuery('#el').offset().top
轻松找到滚动位置,然后使用window.scrollTo
scroll={el => { const yCoordinate = el.getBoundingClientRect().top + window.pageYOffset; const yOffset = -80; window.scrollTo({ top: yCoordinate + yOffset, behavior: 'smooth' }); }}
- 偏移量比标题大小多 10-15 像素,只是为了更好的间距