scrollIntoView 너무 멀리 스크롤


112

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 픽셀이 너무 멀어집니다. 누구든지 그것을 고치는 방법을 알고 있습니까?


47
에 대한 오프셋 구성의 부족 scrollIntoView이 문제입니다.
mystrdat

답변:


58

약 10px이면 포함하는 div의 스크롤 오프셋을 다음과 같이 수동으로 조정할 수 있다고 생각합니다 .

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

2
이 애니메이션 효과는 scrollIntoView?
1252748 2014-07-10

1
@thomas AFAIK, 일반 DOM scrollIntoView함수는 애니메이션을 일으키지 않습니다. scrollIntoView jQuery 플러그인에 대해 이야기하고 있습니까?
Lucas Trzesniewski 2014

1
그것은 일하는 사람이었습니다. 잘못된 방향 이었기 때문에 내가해야 할 일은 + = 10에서-= 10으로 변경하는 것뿐입니다. 이제 올바르게로드됩니다. 도움을 주셔서 감사합니다 !!!!
매튜 윌슨

네. 나는 혼란 스러웠다. 나는 그것이 애니메이션이라고 생각했다. 죄송합니다!
1252748 2014-07-10

17
애니메이션을 적용 할 수 있습니다 el.scrollIntoView({ behavior: 'smooth' });.. 그래도 지원은 좋지 않습니다!
daveaspinall

115

적절한 위치로 부드럽게 스크롤

정확한 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
이것은 가장 정확하게 답변입니다 - 우리는 easly와 스크롤의 위치를 찾을 수 있습니다 jQuery('#el').offset().top후 사용window.scrollTo
알렉산더 곤차 로프

1
이 솔루션에 +1. 컨테이너가 상단 오프셋과 함께 배치되었을 때 요소로 올바르게 스크롤하는 데 도움이되었습니다.
Liga

React Router와 함께 HashLink를 사용하여 여기에 오는 사람은 이것이 저에게 효과적이었습니다. HashLink의 스크롤 소품에서 scroll={el => { const yCoordinate = el.getBoundingClientRect().top + window.pageYOffset; const yOffset = -80; window.scrollTo({ top: yCoordinate + yOffset, behavior: 'smooth' }); }}-오프셋이 더 나은 간격을 위해 헤더 크기보다 10-15 픽셀 더 많은 경우
Rob B

86

두 단계로 수행 할 수 있습니다.

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

23
실행 scrollIntoView()전에 스크롤을 완료하지 않은 경우 scrollBy()후자의 스크롤은 전자를 취소합니다. 크롬 (71)에 적어도
데이브 휴즈

1
이 경우 아래 답변에서 볼 수 있듯이 setTimeout을 사용하십시오. stackoverflow.com/a/51935129/1856258 .. 저에게는 Timeout없이 작동합니다. 감사!
leole

응용 프로그램을 관련 위치로 직접 이동시키기 위해 픽셀의 교정기를 scrollIntoView에 직접 주입하는 방법이 있습니까? 처럼 scrollIntoView({adjustment:y}), 마지막에 커스텀 코드로 가능해야한다고 생각합니다
Webwoman

80

절대 방법으로 위치 앵커

이를 수행하는 또 다른 방법은 오프셋 별 스크롤에 의존하지 않고 페이지에서 원하는 위치에 앵커를 정확히 배치하는 것입니다. 각 요소를 더 잘 제어 할 수 있고 (예 : 특정 요소에 대해 다른 오프셋을 원하는 경우) 브라우저 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의 오프셋으로 부드러운 스크롤을 위해.


12
이것이 매끄럽고 간단하게 구현하는 가장 좋은 방법입니다.
catch22

2
내가 생각하는 가장 좋고 가장 기본적인 방법.
Даниил Пронин

1
이것은 매우 간단하고 매끄 럽습니다. 다른 많은 사람들은 불필요한 JS를 추가합니다.
RedSands

2
이것은 "페이지 상단"유형 링크가 탐색 모음 아래에 있고 탐색을 표시하고 싶지만 다른 링크 오프셋을 원하지 않을 때 특히 유용합니다
Joe Moon

네거티브 margin-top을 사용하여 동일한 결과를 얻을 수 있습니다. 요소를 위치 상대와 함께 다른 요소와 함께 래핑하는 것을 피할 수 있습니다.
Sergi

17

이 문제는 다음을 사용하여 해결했습니다.

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

이렇게하면 요소가 center스크롤 후에 나타나 므로을 계산할 필요가 없습니다 yOffset.

도움이 되길 바랍니다 ...


1
당신은 사용해야 scrollTo에 있지 element만에window
Arseniy-II

@ Arseniy-II 지적 해 주셔서 감사합니다 !!. 나는 그 부분을 놓쳤다!
Imtiaz Shakil Siddique

블록이 정상에 도움이됩니까?
Ashok kumar Ganesan

12

이것은 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;

이것이 최상의 솔루션입니다 ... 답변으로 표시 되었으면합니다. 몇 시간을 절약했을 것입니다.
Shivam

7

이전 답변을 바탕으로 Angular5 프로젝트에서 이것을 수행하고 있습니다.

시작 :

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

그러나 이것은 몇 가지 문제를 일으키고 다음과 같이 scrollBy ()에 대한 setTimeout이 필요했습니다.

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

MSIE11 및 Chrome 68+에서 완벽하게 작동합니다. 나는 FF에서 테스트하지 않았습니다. 500ms는 제가 할 수있는 가장 짧은 지연이었습니다. 부드러운 스크롤이 아직 완료되지 않았기 때문에 때때로 아래로 내려가는 데 실패했습니다. 자신의 프로젝트에 맞게 조정하십시오.

이 간단하지만 효과적인 솔루션 을 위해 Fred727 에게 +1하십시오 .


6
요소로 부드럽게 스크롤 한 다음 약간 위로 스크롤하려면 약간 버벅 거림이 느껴집니다.
catch22

6

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

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, Element로 스크롤하고 그 위에 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

Arseniy-II의 답변을 기반으로 : 스크롤 엔티티가 창 자체가 아니라 내부 템플릿 (이 경우 div) 인 Use-Case가 있습니다. 이 시나리오에서는 스크롤 컨테이너의 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'
  });

누군가 비슷한 상황에 처할 경우를 대비하여 여기에 두십시오!


0

여기 내 2 센트입니다.

또한 scrollIntoView가 요소를 약간 지나서 스크롤하는 문제가 있었기 때문에 요소를 대상 앞에 추가하고 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>

자바 스크립트 파일 :

(() => {
    '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 기능

저는 Steve Banton의 멋진 솔루션을 가져와 사용하기 더 편리하게 만드는 함수를 작성했습니다. 그냥 사용하기 쉬운 것 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;

그런 경우가 없으면 삭제하십시오.


-1

특정 요소를 아래로 스크롤하기위한 간단한 솔루션

const element = document.getElementById("element-with-scroll");
element.scrollTop = element.scrollHeight - 10;

-1

테이블 행의 요소의 경우 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>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.