web-dev-qa-db-ja.com

要素へのスクロールがJavascriptで行われていることを知る方法は?

JavaScriptメソッドElement.scrollIntoView()を使用しています
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

スクロールが終了したときに知ることができる方法はありますか。アニメーションがあったか、または{behavior: smooth}

私はスクロールが非同期であると仮定しており、それに対するメカニズムのようなコールバックがあるかどうかを知りたいです。

17
Sushant Gupta

IntersectionObserver を使用して、要素 .isIntersecting at IntersectionObserverコールバック関数

const element = document.getElementById("box");

const intersectionObserver = new IntersectionObserver((entries) => {
  let [entry] = entries;
  if (entry.isIntersecting) {
    setTimeout(() => alert(`${entry.target.id} is visible`), 100)
  }
});
// start observing
intersectionObserver.observe(box);

element.scrollIntoView({behavior: "smooth"});
body {
  height: calc(100vh * 2);
}

#box {
  position: relative;
  top:500px;
}
<div id="box">
box
</div>
11
guest271314

scrollEndイベントはありませんが、scrollイベントをリッスンして、まだウィンドウをスクロールしているかどうかを確認できます。

var scrollTimeout;
addEventListener('scroll', function(e) {
    clearTimeout(scrollTimeout);
    scrollTimeout = setTimeout(function() {
        console.log('Scroll ended');
    }, 100);
});
6
niutech

私はJavaScriptの専門家ではありませんが、jQueryで作成しました。私はそれが役立つことを願っています

$("#mybtn").click(function() {
    $('html, body').animate({
        scrollTop: $("div").offset().top
    }, 2000);
});

$( window ).scroll(function() {
  $("div").html("scrolling");
  if($(window).scrollTop() == $("div").offset().top) {
    $("div").html("Ended");
  }
})
body { height: 2000px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<button id="mybtn">Scroll to Text</button>
<br><br><br><br><br><br><br><br>
<div>example text</div>
2
MajiD

この「滑らかな」動作の場合、すべての 仕様

ユーザーエージェントがスクロールボックスボックスのスムーズスクロールを実行して配置する場合、ユーザーエージェント定義の方法でボックスのスクロール位置を更新する必要がありますユーザーエージェント定義の時間.

(私を強調する)

そのため、一度完了すると発生する単一のイベントがないだけでなく、異なるブラウザー間で安定した動作を想定することもできません。

実際、現在のFirefoxとChromeはすでに動作が異なります:

  • Firefoxには固定期間が設定されているようで、スクロールする距離に関係なく、この固定期間(〜500ms)でそれを行います
  • Chrome 一方、速度を使用します。つまり、操作の継続時間はスクロールする距離に基づいて変化し、ハード制限は3秒です。

そのため、これにより、この問題に対するすべてのタイムアウトベースのソリューションが既に失格となります。

さて、ここでの答えの1つは、MutationObserverを使用することを提案しています。MutationObserverはあまり悪い解決策ではありませんが、移植性が低く、inlineblockオプションを取りません。アカウント。

そのため、実際にスクロールを停止したかどうかを定期的に確認するのが最善の方法かもしれません。非侵襲的な方法でこれを行うには、requestAnimationFrameパワードループを開始して、フレームごとに1回だけチェックが実行されるようにします。

このような実装の1つは、スクロール操作が終了すると解決されるPromiseを返します。
注:このコードは、操作が成功したかどうかを確認する方法を見逃しています。ページで他のスクロール操作が発生すると、現在の操作がすべてキャンセルされますが、これを読者向けの演習としてみましょう。

const buttons = [ ...document.querySelectorAll( 'button' ) ];

document.addEventListener( 'click', ({ target }) => {
  // handle delegated event
  target = target.closest('button');
  if( !target ) { return; }
  // find where to go next
  const next_index =  (buttons.indexOf(target) + 1) % buttons.length;
  const next_btn = buttons[next_index];
  const block_type = target.dataset.block;

  // make it red
  document.body.classList.add( 'scrolling' );
  
  smoothScroll( next_btn, { block: block_type })
    .then( () => {
      // remove the red
      document.body.classList.remove( 'scrolling' );
    } )
});


/* 
 *
 * Promised based scrollIntoView( { behavior: 'smooth' } )
 * @param { Element } elem
 **  ::An Element on which we'll call scrollIntoView
 * @param { object } [options]
 **  ::An optional scrollIntoViewOptions dictionary
 * @return { Promise } (void)
 **  ::Resolves when the scrolling ends
 *
 */
function smoothScroll( elem, options ) {
  return new Promise( (resolve) => {
    if( !( elem instanceof Element ) ) {
      throw new TypeError( 'Argument 1 must be an Element' );
    }
    let same = 0; // a counter
    let lastPos = null; // last known Y position
    // pass the user defined options along with our default
    const scrollOptions = Object.assign( { behavior: 'smooth' }, options );

    // let's begin
    elem.scrollIntoView( scrollOptions );
    requestAnimationFrame( check );
    
    // this function will be called every painting frame
    // for the duration of the smooth scroll operation
    function check() {
      // check our current position
      const newPos = elem.getBoundingClientRect().top;
      
      if( newPos === lastPos ) { // same as previous
        if(same ++ > 2) { // if it's more than two frames
/* @todo: verify it succeeded
 * if(isAtCorrectPosition(elem, options) {
 *   resolve();
 * } else {
 *   reject();
 * }
 * return;
 */
          return resolve(); // we've come to an halt
        }
      }
      else {
        same = 0; // reset our counter
        lastPos = newPos; // remember our current position
      }
      // check again next painting frame
      requestAnimationFrame(check);
    }
  });
}
p {
  height: 400vh;
  width: 5px;
  background: repeat 0 0 / 5px 10px
    linear-gradient(to bottom, black 50%, white 50%);
}
body.scrolling {
  background: red;
}
<button data-block="center">scroll to next button <code>block:center</code></button>
<p></p>
<button data-block="start">scroll to next button <code>block:start</code></button>
<p></p>
<button data-block="nearest">scroll to next button <code>block:nearest</code></button>
<p></p>
<button>scroll to top</button>
0
Kaiido

上記のこれらの回答は、スクロールが完了した後でもイベントハンドラーを所定の場所に残します(ユーザーがスクロールすると、メソッドが呼び出され続けるように)。また、スクロールが不要な場合は通知されません。少し良い答えがあります:

$("#mybtn").click(function() {
    $('html, body').animate({
        scrollTop: $("div").offset().top
    }, 2000);

    $("div").html("Scrolling...");

    callWhenScrollCompleted(() => {
        $("div").html("Scrolling is completed!");
    });
});

// Wait for scrolling to stop.
function callWhenScrollCompleted(callback, checkTimeout = 200, parentElement = $(window)) {
  const scrollTimeoutFunction = () => {
    // Scrolling is complete
    parentElement.off("scroll");
    callback();
  };
  let scrollTimeout = setTimeout(scrollTimeoutFunction, checkTimeout);

  parentElement.on("scroll", () => {
    clearTimeout(scrollTimeout);
    scrollTimeout = setTimeout(scrollTimeoutFunction, checkTimeout);
  });
}
body { height: 2000px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<button id="mybtn">Scroll to Text</button>
<br><br><br><br><br><br><br><br>
<div>example text</div>
0