overflow-anchorCSS中的属性相对较新,2017 年首次推出 Chrome ¹,2019 年推出 Firefox,现在 Edge在 2020 年推出 Chrome 过渡。幸运的是,它的使用在很大程度上是一种增强。这个想法是浏览器在默认情况下确实尝试不允许位置移动。然后,如果您不喜欢它的处理方式,您可以使用 将其关闭。所以一般来说,你从不碰它。overflow-anchor 但正如你可能怀疑的那样,我们可以利用这个小美来做一点 CSS 小把戏。即使我们添加新内容,我们也可以强制滚动元素保持固定在底部。
您需要检测何时使用 JavaScript 添加新内容并将滚动元素强制到底部。这是一种MutationObserver
在 JavaScript 中使用的技术来监视新内容并强制滚动:
- const scrollingElement = document.getElementById("scroller");
-
- const config = { childList: true };
-
- const callback = function (mutationsList, observer) {
- for (let mutation of mutationsList) {
- if (mutation.type === "childList") {
- window.scrollTo(0, document.body.scrollHeight);
- }
- }
- };
-
- const observer = new MutationObserver(callback);
- observer.observe(scrollingElement, config);
但我发现一个纯 CSS 的解决方案更有吸引力!上面的版本有一些我们稍后会介绍的用户体验缺陷。
这里的诀窍是浏览器默认已经做了滚动锚定。但是浏览器试图做的不是改变你的页面。因此,当添加可能会改变页面视觉位置的新内容时,他们会尝试防止这种情况发生。在这种不寻常的情况下,我们有点想要相反的东西。我们希望页面卡在页面底部并让视觉页面在视觉上移动,因为它被迫保持卡在底部。
下面是这个技巧的工作原理。首先是滚动父元素中的一些 HTML:
- <div id="scroller">
- <!-- new content dynamically inserted here -->
- <div id="anchor"></div>
- </div>
所有元素自然都有一个overflow-anchor: auto;
,这意味着当它们在屏幕上时,它们会试图阻止页面移动,但我们可以使用overflow-anchor: none;
. 所以诀窍是针对所有动态插入的内容并将其关闭:
- #scroller * {
- overflow-anchor: none;
- }
然后仅强制该锚元素具有滚动锚定,仅此而已:
- #anchor {
- overflow-anchor: auto;
- height: 1px;
- }
现在,一旦该锚在页面上可见,浏览器将被迫将该滚动位置固定到它,并且由于它是该滚动元素中的最后一件事,因此保持固定在底部。
- <div id="scroller">
- <!-- new content dynamically inserted here -->
- <div id="anchor"></div>
- </div>
-
- <style>
- #scroller * {
- overflow-anchor: none;
- }
-
- #anchor {
- overflow-anchor: auto;
- height: 1px;
- }
-
- html {
- font-family: system-ui, sans-serif;
- }
- body {
- background-color: #7FDBFF;
- /* In case you need to kick off effect immediately, this plus JS */
- /* height: 100.001vh; */
- }
-
- .message {
- padding: 0.5em;
- border-radius: 1em;
- margin: 0.5em;
- line-height: 1.1em;
- background-color: white;
- }
- </style>
-
- <script>
- let scroller = document.querySelector('#scroller');
- let anchor = document.querySelector('#anchor');
-
- // https://ajaydsouza.com/42-phrases-a-lexophile-would-love/
- let messages = [
- 'I wondered why the baseball was getting bigger. Then it hit me.',
- 'Police were called to a day care, where a three-year-old was resisting a rest.',
- 'Did you hear about the guy whose whole left side was cut off? He’s all right now.',
- 'The roundest knight at King Arthur’s round table was Sir Cumference.',
- 'To write with a broken pencil is pointless.',
- 'When fish are in schools they sometimes take debate.',
- 'The short fortune teller who escaped from prison was a small medium at large.',
- 'A thief who stole a calendar… got twelve months.',
- 'A thief fell and broke his leg in wet cement. He became a hardened criminal.',
- 'Thieves who steal corn from a garden could be charged with stalking.',
- 'When the smog lifts in Los Angeles , U. C. L. A.',
- 'The math professor went crazy with the blackboard. He did a number on it.',
- 'The professor discovered that his theory of earthquakes was on shaky ground.',
- 'The dead batteries were given out free of charge.',
- 'If you take a laptop computer for a run you could jog your memory.',
- 'A dentist and a manicurist fought tooth and nail.',
- 'A bicycle can’t stand alone; it is two tired.',
- 'A will is a dead giveaway.',
- 'Time flies like an arrow; fruit flies like a banana.',
- 'A backward poet writes inverse.',
- 'In a democracy it’s your vote that counts; in feudalism, it’s your Count that votes.',
- 'A chicken crossing the road: poultry in motion.',
- 'If you don’t pay your exorcist you can get repossessed.',
- 'With her marriage she got a new name and a dress.',
- 'Show me a piano falling down a mine shaft and I’ll show you A-flat miner.',
- 'When a clock is hungry it goes back four seconds.',
- 'The guy who fell onto an upholstery machine was fully recovered.',
- 'A grenade fell onto a kitchen floor in France and resulted in Linoleum Blownapart.',
- 'You are stuck with your debt if you can’t budge it.',
- 'Local Area Network in Australia : The LAN down under.',
- 'He broke into song because he couldn’t find the key.',
- 'A calendar’s days are numbered.',
- ];
-
- function randomMessage() {
- return messages[(Math.random() * messages.length) | 0];
- }
-
- function appendChild() {
- let msg = document.createElement('div');
- msg.className = 'message';
- msg.innerText = randomMessage();
- scroller.insertBefore(msg, anchor);
-
- }
- setInterval(appendChild, 1000);
-
- // To kick off effect immediately, this plus CSS
- // document.scrollingElement.scroll(0, 1);
- </script>
这里有两个小警告......
height
这里使用以确保它不是没有大小的折叠/空元素,这会阻止它工作。第二个更难。一种选择是根本不处理它,您可以等待用户滚动到底部,效果从那里开始起作用。实际上这很好,因为如果他们从底部滚动,效果就会停止工作,这就是你想要的。在上面的 JavaScript 版本中,请注意即使您尝试向上滚动,它也会迫使您进入底部,这就是 Ryan 的意思,这并不是一件容易正确的事情。
如果您需要立即启动效果,诀窍是让滚动元素立即可滚动,例如:
- body {
- height: 100.001vh;
- }
然后立即触发一个非常轻微的滚动:
document.scrollingElement.scroll(0, 1);
这应该可以解决问题。这些行可在上面的演示中取消注释并尝试。