使用带有固定位置标题的 scrollIntoView

问题描述 投票:0回答:11

我有一个标题设置为

position: fixed
的网站。在我的一个页面上,我在一个元素上使用了
scrollIntoView(true)
。我的问题是,当调用
scrollIntoView
时,该元素位于标题下方。我该如何解决这个问题,以便该元素显示在标题下方?

我正在使用 Bootstrap 框架,标题的样式为

navbar navbar-fixed-top
.

javascript twitter-bootstrap
11个回答
134
投票

有点hacky,但这里有一个解决方法。

var node = 'select your element';
var yourHeight = 'height of your fixed header';

// scroll to your element
node.scrollIntoView(true);

// now account for fixed header
var scrolledY = window.scrollY;

if(scrolledY){
  window.scroll(0, scrolledY - yourHeight);
}

编辑:

现代的解决方法是结合使用 CSS

scroll-margin-top
属性和
:target
选择器。详细描述:https://www.bram.us/2020/03/01/prevent-content-from-being-hidden-underneath-a-fixed-header-by-using-scroll-margin-top/


45
投票

滚动边距顶部:$header-height;

scroll-margin-top
css 属性设置为所需的顶部偏移量,在您链接/滚动到的元素上。 (它可能应该被称为 scroll-offset-top。)

* {
  scroll-margin-top: 100px;
}

Docs - 完整的浏览器支持 - Codepen 演示

links = [...document.getElementsByClassName("js-link")]
links.forEach(element => {
  element.addEventListener("click", e => {
    e.preventDefault()
    document.getElementById(e.target.dataset.target).scrollIntoView({
      behavior: "smooth",
      block: "start",
      inline: "nearest"
    })
  })
})
body {
  margin: 0;
}

header {
  position: sticky;
  top: 0;
  left: 0;
  right: 0;
  height: 100px;
  background: #eee;
  display: flex;
  align-items: center;
}

header a {
  padding: 0.5em;
}

h1 {
  padding: 0.7em;
}

* {
  scroll-margin-top: 100px;
}

p {
  padding: 1em;
}
<header>
  <h1>page title</h1>
  <nav>
    <a href="#p1" class="js-link" data-target="p1">1</a>
    <a href="#p2" class="js-link" data-target="p2">2</a>
    <a href="#p3" class="js-link" data-target="p3">3</a>
    <a href="#p4" class="js-link" data-target="p4">4</a>
    <a href="#p5" class="js-link" data-target="p5">5</a>
  </nav>
</header>

<main>
  <p id="p1">
    paragraph 1.
    <br> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
    It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop
    publishing software like Aldus PageMaker including versions of Lorem Ipsu
  </p>
  <p id="p2">
    paragraph 2.
    <br> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
    It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop
    publishing software like Aldus PageMaker including versions of Lorem Ipsu
  </p>
  <p id="p3">
    paragraph 3.
    <br> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
    It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop
    publishing software like Aldus PageMaker including versions of Lorem Ipsu
  </p>
  <p id="p4">
    paragraph 4.
    <br> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
    It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop
    publishing software like Aldus PageMaker including versions of Lorem Ipsu
  </p>
  <p id="p5">
    paragraph 5.
    <br> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
    It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop
    publishing software like Aldus PageMaker including versions of Lorem Ipsu
  </p>
</main>


41
投票

编辑: 尽管兼容性较差(不兼容 IE),

scroll-margin-top
也将解决此问题,如 Arye Eidelman 的回答 中所述。

您可以使用 CSS 解决此问题,方法是对要滚动到的元素应用

padding-top
和负号
margin-top

演示

// For demo only, no JS needed for the solution
document.querySelector('.scroll-to-working-inline').addEventListener('click', function() {
  document.querySelector('.working-inline').scrollIntoView();
});

document.querySelector('.scroll-to-working-block').addEventListener('click', function() {
  document.querySelector('.working-block').scrollIntoView();
});

document.querySelector('.scroll-to-broken').addEventListener('click', function() {
  document.querySelector('.broken').scrollIntoView();
});
/* Relevant styles */
.working-inline {
  padding-top: 60px;
  margin-top: -60px;
}

.working-block {
  padding-top: 60px;
  margin-top: -60px;
}

/* Allow scrolling to the top */
body {
  padding-top: 60px;
}

/* Only for the demo */
body { margin: 0; }

header {
  position: fixed;
  top: 0;
  background-color: tomato;
  color: white;
  width: 100%;
  height: 60px;
  line-height: 60px;
  text-align: center;
}

[class^='working'],
[class^='broken'] {
  font-size: 3rem;
}
<header>
  scroll to...
  <button class="scroll-to-working-inline">working inline element</button>
  <button class="scroll-to-working-block">working block element</button>
  <button class="scroll-to-broken">broken element</button>
</header>

<main>
  <p>Sql daemon epoch all your base are belong to us packet system perl semaphore. Interpreter warez pragma kilo worm back door baz continue chown blob unix Dennis Ritchie stack mutex bar throw fopen man pages linux. Sql suitably small values bit infinite loop pwned rm -rf.</p>
  
  <a class="working-inline">Working inline</a>
  
  <p>Syn baz man pages unix vi crack leapfrog semaphore fail pwned afk null socket cd long leet emacs Donald Knuth bin grep todo pragma stdio.h January 1, 1970. Alloc gc system new finally sql stack trace syn mainframe cat machine code memory leak server salt flood tunnel in back door thread. Bytes fatal throw ctl-c Dennis Ritchie over clock eof tera perl regex.</p>
  
  <div class="working-block">Working block element</div>
  
  <p>Public injection class unix malloc error script kiddies packet less fail int I'm sorry Dave, I'm afraid I can't do that. Tarball memory leak double rsa pwned public all your base are belong to us. False bytes bang bar tarball semaphore warez cd port daemon exception mountain dew sql mainframe gcc ifdef chown private.</p>
  
  <div class="broken">Broken element</div>
  
  <p>Daemon bubble sort protected mutex overflow grep snarf crack warez I'm compiling bit if memory leak Starcraft nak script kiddies long it's a feature. Hello world public server James T. Kirk injection terminal wannabee race condition syn alloc. Gobble leapfrog finally bypass concurrently while irc gurfle do back door blob man pages sql over clock.</p>
  
  <p>Char hello world then man pages ascii long salt while char fatal do boolean tunnel in system else foo packet sniffer float terminal int default. Trojan horse ssh ifdef /dev/null chown cache error protocol afk todo rm -rf mainframe piggyback pwned regex xss warez Starcraft try catch stdio.h bubble sort. It's a feature I'm sorry Dave, I'm afraid I can't do that *.* port bypass ip.</p>
  
  <p>Stdio.h epoch mutex flood wannabee do race condition sql access exception. Bar pragma man pages dereference flush todo highjack while buffer bit nak big-endian syn xss salt for d00dz. Leslie Lamport linux server error hexadecimal snarf tunnel in rm -rf firewall then shell all your base are belong to us.</p>

  <p>Ascii gcc grep int flood kilo linux access mailbomb hash *.* fork semaphore frack else win bar ssh Leslie Lamport. Man pages strlen cache gnu segfault tarball race condition perl packet sniffer root cookie private chown d00dz January 1, 1970. Rsa public crack bit warez throw for void concurrently ip mutex.</p>
  
  <p>Char hello world then man pages ascii long salt while char fatal do boolean tunnel in system else foo packet sniffer float terminal int default. Trojan horse ssh ifdef /dev/null chown cache error protocol afk todo rm -rf mainframe piggyback pwned regex xss warez Starcraft try catch stdio.h bubble sort. It's a feature I'm sorry Dave, I'm afraid I can't do that *.* port bypass ip.</p>
  
  <p>Stdio.h epoch mutex flood wannabee do race condition sql access exception. Bar pragma man pages dereference flush todo highjack while buffer bit nak big-endian syn xss salt for d00dz. Leslie Lamport linux server error hexadecimal snarf tunnel in rm -rf firewall then shell all your base are belong to us.</p>

  <p>Ascii gcc grep int flood kilo linux access mailbomb hash *.* fork semaphore frack else win bar ssh Leslie Lamport. Man pages strlen cache gnu segfault tarball race condition perl packet sniffer root cookie private chown d00dz January 1, 1970. Rsa public crack bit warez throw for void concurrently ip mutex.</p>
  
  <p>Char hello world then man pages ascii long salt while char fatal do boolean tunnel in system else foo packet sniffer float terminal int default. Trojan horse ssh ifdef /dev/null chown cache error protocol afk todo rm -rf mainframe piggyback pwned regex xss warez Starcraft try catch stdio.h bubble sort. It's a feature I'm sorry Dave, I'm afraid I can't do that *.* port bypass ip.</p>
  
  <p>Stdio.h epoch mutex flood wannabee do race condition sql access exception. Bar pragma man pages dereference flush todo highjack while buffer bit nak big-endian syn xss salt for d00dz. Leslie Lamport linux server error hexadecimal snarf tunnel in rm -rf firewall then shell all your base are belong to us.</p>

  <p>Ascii gcc grep int flood kilo linux access mailbomb hash *.* fork semaphore frack else win bar ssh Leslie Lamport. Man pages strlen cache gnu segfault tarball race condition perl packet sniffer root cookie private chown d00dz January 1, 1970. Rsa public crack bit warez throw for void concurrently ip mutex.</p>
</main>


40
投票

以下代码产生一个平滑滚动到元素顶部的固定标题偏移量:

var topOfElement = document.querySelector('#targetElement').offsetTop - XX;
window.scroll({ top: topOfElement, behavior: "smooth" });

XX 是固定标题的高度。


7
投票

尝试以下操作。它对我很有效:

  const headerHeight = 50; /* PUT HEADER HEIGHT HERE */
  const buffer = 25; /* MAY NOT BE NEEDED */
  const scrollToEl = document.querySelector("#YOUR-ELEMENT-SELECTOR");

  const topOfElement = window.pageYOffset + scrollToEl.getBoundingClientRect().top - headerHeight - buffer;
  window.scroll({ top: topOfElement, behavior: "smooth" });

7
投票

一个非常简单的解决方案(受 Sanyam Jain 的评论启发)是使用

{block: 'center'}
像这样垂直居中选择:

scrollIntoView({block: 'center'})

注意 - MDN 文档说这是实验性的,但它已经存在了很长时间。

https://developer.mozilla.org/zh-CN/docs/Web/API/Element/scrollIntoView


1
投票

如果有人在 scrollIntoView 之后遇到容器 div 的上边距被忽略的问题,那么与其将元素滚动到视图中,不如相对于其父滚动容器执行 scrollTop,如下所示:

var topOfElementToView= $('#elementToScroll').position().top;
$('#parentScrollingContainer').scrollTop(topOfElementToView);

从 user113716 得到了这个线程的答案:How to go to a specific element on page?


1
投票

我怀疑对个人有用的东西在很大程度上取决于他们的页面布局,所以这个答案旨在作为一个额外的选择而不是篡夺任何人。

我需要做的就是传递 false 以滚动到视图中

scrollIntoView(false)

it('should be able to click a button selector', function () {
    let EC = protractor.ExpectedConditions;
    let button = element(by.css('.my-button-css));

    browser.executeScript('arguments[0].scrollIntoView(false)', button.getWebElement()).then(function () {
        browser.wait(EC.elementToBeClickable(button), 3000).then(function () {
            expect(button.isDisplayed()).toBeTruthy();
            button.click();

            // more test logic here

        });
    });
});

感谢reboot jeff


0
投票

如果有人修复了滚动后隐藏 Header\Title 的导航栏,这里是解决方案(基于

@coco puffs
的答案和这个):

let anchorLinks = document.querySelectorAll('a[href^="#"]')

for (let item of anchorLinks) {
  item.addEventListener('click', (e) => {
    let hashVal = item.getAttribute('href')
    let topOfElement = document.querySelector(hashVal).offsetTop - 70

    window.scroll({ top: topOfElement, behavior: "smooth" })
    history.pushState(null, null, hashVal)
    e.preventDefault()
  })
}

在代码中

70px
被使用。


0
投票

对我来说,将元素滚动到窗口的中心就可以了。

scrollIntoViewIfNeeded({ block: "center" })
做得很好,但不幸的是只有有限的浏览器兼容性(https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoViewIfNeeded)。它不适用于 IE 和 Firefox。

但是,您可以自己轻松构建此功能:

function scrollIntoCenter(element) {
  // first scroll element into view. This means element is at the very top.
  element.scrollIntoView();
  
  // calculate new scrollYPosition by subtracting half of window-height
  let y = window.scrollY - window.innerHeight/2;
  
  // scroll up by half of window height
  window.scroll(0, y);
}

0
投票

我是这样做的

var messageContainer
var message
var autoScroll

var messageCounter = 0
var messageQuantity = 20
while(messageQuantity >1){
        messageQuantity--
        messageContainer = document.createElement('div')
        messageContainer.className = 'messageContainer'

        message = document.createElement('div')
        message.className = 'message'

        message.innerHTML = `message: ${messageCounter++}`
        messageContainer.append(message)

        document.getElementsByClassName('overflow_section')[0].append(messageContainer)
        
        document.getElementById('messageQuantity').textContent = `counter: ${messageCounter}`
}

var appendMessage = document.getElementsByClassName('appendMessage')[0]
        appendMessage.addEventListener("click", ()=>{
            autoScroll = document.getElementById('autoScroll')
                    
            messageContainer = document.createElement('div')
            messageContainer.className = 'messageContainer'
            
            message = document.createElement('div')
            message.className = 'message'
        
            message.innerHTML = `message: ${messageCounter++}`
            messageContainer.append(message)
            
            document.getElementsByClassName('overflow_section')[0].append(messageContainer)
            
            if(autoScroll.checked){
                var count = document.getElementsByClassName('overflow_section')[0].getElementsByClassName('messageContainer')
                document.getElementsByClassName('messageContainer')[count.length - 1].scrollIntoView({
                    behavior: 'smooth', block: 'nearest', inline: 'start' 
                })
            }
            document.getElementById('messageQuantity').textContent = `counter: ${messageCounter}`
                
        })
        
        var scrollUp = document.getElementsByClassName('scrollUp')[0]       
        scrollUp.addEventListener("click", ()=>{
            var count = document.getElementsByClassName('overflow_section')[0].getElementsByClassName('messageContainer')   
                if(count.length > 0){
                   document.getElementsByClassName('messageContainer')[0].scrollIntoView({
                        behavior: 'smooth', block: 'center', inline: 'start' 
                    })  
                }           
                        
        })
        
        var scrollDown = document.getElementsByClassName('scrollDown')[0]       
        scrollDown.addEventListener("click", ()=>{
            var count = document.getElementsByClassName('overflow_section')[0].getElementsByClassName('messageContainer')   
                if(count.length > 0){
                     document.getElementsByClassName('messageContainer')[count.length - 1].scrollIntoView({
                        behavior: 'smooth', block: 'nearest', inline: 'start' 
                    })
                }           
                            
        })
button{
    background-color: rgb(0 0 0 / 80%);
    border-radius:5px;
    padding:10px;
    border:none;
    color:white;
}

input{
   background-color: rgb(179 229 249 / 50%);
   position:relative;
   top:10px;
    width:30px;
    height:30px;
}

.overflow_section{
    overflow: auto;
    font-size:20px;
    background-color: rgb(179 229 249 / 50%);
    width:50%;
    height:200px;
    margin:15px;
    margin-left:auto;
    margin-right:auto;
}

.messageContainer{
    background-color: rgb(255 120 0 / 50%);
    border-radius:5px;
    margin:5px;
    padding:15px;
}


#messageQuantity{
    background-color: rgb(255 120 0 / 100%);
    font-weight:700;
    border-radius:5px;
    padding:10px;
    color:black;
}
<div align="center">
    <span align="center" id='messageQuantity'></span>
    <button class='appendMessage'>Append message</button>
    <button class='scrollUp'>Scroll Up</button>
    <button class='scrollDown'>Scroll Down</button>

    <input type="checkbox" id="autoScroll" />
    <label for="vehicle1">Auto scroll</label>
</div>
<div class='overflow_section'></div>

© www.soinside.com 2019 - 2024. All rights reserved.