scrollIntoViewスクロールしすぎます


106

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ピクセルほど上に移動します。誰でもそれを修正する方法を知っていますか?


44
のオフセット構成の欠如scrollIntoViewは厄介です。
mystrdat 2017

回答:


56

それが約10pxの場合は、次のdivようにして、含まれているのスクロールオフセットを手動で簡単に調整できます。

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

1
これはと同じアニメーション効果がありscrollIntoViewますか?
1252748 14

1
@thomas AFAIK、プレーンDOM scrollIntoView関数はアニメーションを引き起こしません。scrollIntoView jQueryプラグインについて話していますか?
Lucas Trzesniewski、2014

1
それはうまくいきましたが、方向が間違っていたので、+ = 10から-= 10に変更するだけで問題なくロードされました。
マシューウィルソン

うん。私は混乱していた。アニメだと思った。ごめんなさい!
1252748 14

16
アニメーション化できel.scrollIntoView({ behavior: 'smooth' });ます。追加するだけです。しかし、サポートは素晴らしいものではありません!
daveaspinall

106

適切な位置までスムーズにスクロール

正しい 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
これが最も正確な答えです。スクロール位置を簡単に見つけjQuery('#el').offset().topて使用できますwindow.scrollTo
Alexander Goncharov

1
このソリューションの+1。コンテナが上部のオフセットを基準として相対的に配置されている場合に、要素まで正しくスクロールするのに役立ちました。
リガ

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

84

あなたは2つのステップでそれを行うことができます:

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

22
が実行scrollIntoView()前にスクロールを完了していない場合、scrollBy()後者のスクロールは前者をキャンセルします。クローム71上に、少なくとも
デイブ・ヒューズ

1
:以下の回答で見られるようにこの場合のsetTimeoutを使用stackoverflow.com/a/51935129/1856258それはタイムアウトなしで働く私のため.. ありがとう!
leole

アプリケーションを直接関連する場所に移動させるために、ピクセルのコレクターをscrollIntoViewに直接挿入する方法はありますか?のようにscrollIntoView({adjustment:y})、最後にカスタムコードを使用することで可能になると思います
Webwoman

78

絶対法によるアンカーの配置

これを行う別の方法は、オフセットによるスクロールに依存するのではなく、アンカーをページ上の必要な場所に正確に配置することです。各要素をより適切に制御できるようになり(特定の要素に別のオフセットが必要な場合など)、ブラウザ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

負のマージントップを使用して同じことを達成できます。相対位置で要素を一緒にラップするのを避けます
セルジ

14

私はこの問題を使用して解決しました、

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

これにより、要素がcenterスクロール後に表示されるため、計算する必要はありませんyOffset

それが役に立てば幸い...


1
あなたは使うべきscrollToではないelementが、上window
Arseniy-II

@ Arseniy-II指摘してくれてありがとう!! その部分を逃した!
Imtiaz Shakil Siddique

ブロックはトップに役立ちますか?
Ashok kumar Ganesan

11

これは私にとって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のトップポイントは通常の場所にありますが、その内部コンテンツは通常の場所の100ピクセル下から始まります。それが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)であるユースケースがありました。このシナリオでは、スクロールコンテナーの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が要素を少し超えてスクロールする問題もあるので、宛先に要素を付加するスクリプト(ネイティブ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;
}

これが誰にも役立つことを願っています!


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()が、これら2つにはいくつかの問題があります。

  • スムーズな動作でそれらを使用すると、すべてがジャンクになります。
  • とにかくそれらを防ぐことはできず、巻物と巻物まで待たなければなりません。ですから、私の機能があなたに役立つことを願っています。また、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()。デフォルトではですstartMDNの詳細をご覧ください。

最後に

必要に応じて、smoothScroll関数を調整します。

たとえば、固定ヘッダーがある場合(または私がそれをと呼んでいるmasthead場合)、次のようなことができます。

const mastheadEl = document.querySelector(someMastheadSelector);

// and add it's height to the EXTRA_OFFSET variable

const EXTRA_OFFSET = mastheadEl.offsetHeight - 3;

そのようなケースがない場合は、削除してください。


0

テーブル行の要素の場合、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>

-1

特定の要素を下にスクロールするためのシンプルなソリューション

const element = document.getElementById("element-with-scroll");
element.scrollTop = element.scrollHeight - 10;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.