質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Q&A

1回答

3333閲覧

JavaScript「Intersection Observer」で要素が見えていないのにイベントが発火してしまう

sofa

総合スコア5

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

1グッド

1クリップ

投稿2020/04/30 14:01

編集2020/05/01 00:28

###実現したいこと
「Intersection Observer」を使い、要素が見えてからイベントを発火させたいです。

$(window).scroll(function(){});の代わりに「Intersection Observer」を使ってスクロールイベントを実装したのですが、要素が見えていないのにイベントが発火してしまう現象にぶつかってしまいました。

発生している問題

以下ソースコードは
.Blueが見えた段階でconsole.log('.Blueが表示されました');を実行するという処理と、
.Redが見えた段階でconsole.log('.Redが表示されました');を実行するという処理を実装しています。

しかし読み込み直後の段階にまでconsole.log('.Redが表示されました');が実行されてしまうのです。

###該当のソースコード
↓サンプル
https://jsfiddle.net/3syn1apv/

↓サンプルと同じコード

html

1<header><div>menu1</div><div>menu2</div></header> 2 3<section class="Gray"> 4 <div>contentsGray</div> 5</section> 6 7<section class="Red"> 8 <div>contentsRed</div> 9</section> 10 11<section class="Blue"> 12 <div>contentsBlue</div> 13</section>

css

1body { 2 background: #fff; 3 padding: 0; 4 margin: 0; 5} 6section { 7 height: calc(100vh - 50px); 8 padding: 50px 0 0 0; 9 background: gray; 10} 11section.Red.is_view{ 12 background: red; 13} 14section.Blue.is_view{ 15 background: blue; 16} 17header { 18 display: flex; 19 align-items: center; 20 justify-content: space-evenly; 21 position: fixed; 22 top: 0; right: 0; left: 0; 23 height: 50px; 24 background: black; 25 color: white; 26}

js

1// .Redが見えたらクラス付与 2const optionsRed = { 3 // オプション指定なし 4}; 5const observerRed = new IntersectionObserver((entries) => { 6 entries.forEach(entry => { 7 if( entry.isIntersecting ){ 8 console.log('.Redが表示されました'); 9 $('.Red').addClass('is_view'); 10 } 11 }); 12}, optionsRed); 13observerRed.observe( $(".Red")[0] ); 14 15// .BLueが見えたらクラス付与 16const observerBlue = new IntersectionObserver((entries) => { 17 entries.forEach(entry => { 18 if( entry.isIntersecting ){ 19 console.log('.Blueが表示されました'); 20 $('.Blue').addClass('is_view'); 21 } 22 }); 23}); 24observerBlue.observe( $(".Blue")[0] ); 25

試したこと

ぴったりだといけないのかと思い次のように「10%見えたとき(threshold: 0.1)」としてみたのですが、これでも読み込み直後に実行されてしまいました。

追記:Chromeで threshold: 0.1 で試したら平気でした。またFirefoxでもjsfiddleのsectionの高さが418.4pxの場合に平気でした。

js

1const optionsRed = { 2 // オプション指定なし 3}; 4 5// ↓ 指定 6 7const optionsRed = { 8 threshold: 0.1 // 10%見えたとき 9};

で解決したのは、HTMlから<header>をなくし、.section{height:100vh:}にするという次の方法です。

html

1<!-- <header><div>menu1</div><div>menu2</div></header> -->

css

1section { 2 /* height: calc(100vh - 50px); */ 3 /* padding: 50px 0 0 0; */ 4 background: gray; 5 height: 100vh; /* headerをなくしたので100vhへ変更 */ 6} 7

が…っ!!だめ、これではだめです。だって<header>は必要です。
どうすれば先の問題を、<header>周辺のデザインを変えることなくクリアできるでしょうか。

AkitoshiManabe👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cobm

2020/05/01 00:08

``` const optionsRed = { root:null, rootMargin: '0px', threshold: 0.1 }; ``` Chromeで試してみましたが、thresholdの指定がちょっとでもあればいけてそうな気がします。 ブラウザ等の条件によっても違うのかもしれないですが、 例えば、thresholdの値を0.5とか大きめの値にしても変わらないでしょうか?
sofa

2020/05/01 00:24

いつもアドバイスありがとうございます。 たしかにChromeで threshold: 0.1 で試したら平気でした。 (そもそもthreshold指定しなくてはいけない点に納得できませんが…) またFirefoxでもjsfiddleのsectionの高さが418.4pxの場合に平気でした。 (これは完全に意味不明です…) 新しい技術はなかなか難しいところがありますね…
guest

回答1

0

「Intersection Observer」を使い、要素が見えてからイベントを発火させたい

未知の Observer でしたので確認してみました。

threshold は イベント発火回数を交差率で指定するためのオプションのようです(複数回は配列指定)。
ご質問の jsfiddle.net のHTML/CSSを拝借し、pure javascript で試してみました。

CSS

1section { 2 height: calc(100vh - 50px); 3 padding: 50px 0 0 0; 4 background: pink; 5} 6section.Gray.is_view { 7 background: gray; 8}

javascript

1const opts = { 2 root: null, // 任意のHTML*Elementを指定して表示領域を設定できる 3 rootMargin: '50px 0px 0px 0px', // viewport(root)のマージン指定例 4 // 監視対象の交差率でイベント発火回数を指定 5 // 未表示 <-- --> 表示しきった(解釈が微妙ですが) 6 threshold: [0, 0.25, 0.75, 1] 7}; 8let iObserver = new IntersectionObserver((entries) => { 9 // entries is {Array.<IntersectionObserverEntry>} 10 entries.forEach( (entry, idx) => { 11 /* 12 console.log({ 13 entryNumber: idx, 14 isIntersecting: entry.isIntersecting, 15 intersectionRatio: entry.intersectionRatio, 16 rootBounds: JSON.stringify(entry.rootBounds), 17 boundingClientRect: JSON.stringify(entry.boundingClientRect), 18 intersectionRect: JSON.stringify(entry.intersectionRect ) 19 }); 20 */ 21 let elm = entry.target; 22 // 指定した threshold と比較できる プロパティが intersectionRatio の様子 23 if( entry.intersectionRatio < 0.25 || 0.75 < entry.intersectionRatio ) { 24 elm.classList.remove("is_view"); 25 console.log(elm.className + 'を脱色'); 26 return; 27 } 28 else { 29 //if( entry.isIntersecting ){ // コメントを受けて修正 30 console.log(elm.className + 'を着色'); 31 elm.classList.add('is_view'); 32 } 33 }); 34 35}, opts); 36// 監視対象要素を登録 37iObserver.observe( document.querySelector(".Gray") ); 38iObserver.observe( document.querySelector(".Red") ); 39iObserver.observe( document.querySelector(".Blue") );

追記)コードブロックを訂正してあります

IntersectionObserver は 「監視イベントであって、スクロールイベントではない」という点に着目すべきかもしれません(「何ピクセルスクロールしたから…」という考え方ではないように思います)。

MDNの説明にもあるように root(表示基準要素) と target(監視対象要素) との2つの矩形の重なり具合(交差率)を監視するもので、スクロールが発生したときには、重なり具合の再計算が行われているのでスクロールにも使える実装のようです。

しきい値に設定した1回以上の任意のタイミングで発火させることができるので、回答に例示したコードでは4つのタイミングで「重なり具合を意味する entry.intersectionRatio」で切り分ける処理をイベントリスナに書いたつもりです。他にもイベント発火を1回にしつつ、矩形情報 BoundingRect を比較演算した結果を切り分ける条件もできそうです。

※CODEPEN でも動かしてみました
(ゆっくりスクロール操作すると、重なり具合が 25%~75% のときに着色される動作になっています)

root(デフォルトはviewport)の矩形に対して、監視指定した要素の交差率を考えるだけで良いため、
コンストラクタに渡すオプションには、表示の変化をイメージして、マージンやしきい値を定義する必要があるようです。

前回のご質問で cobm さんの回答にあるリンク先の説明も読み返してみると解りやすいかと思います。

投稿2020/05/01 02:56

編集2020/05/01 08:05
AkitoshiManabe

総合スコア5432

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sofa

2020/05/01 07:03

以下はそちらをコピペし、.Redだけにして他をコメントアウトしたものになりますが https://jsfiddle.net/veLcm9h4/ やはり読み込み直後に「Redを着色」が表示されてしまいます。 そして質問にあるようにsectionのheightが418.4pxの場合は正常となり、質問と同様の状態でした。
AkitoshiManabe

2020/05/01 07:55

> やはり読み込み直後に「Redを着色」が表示されてしまいます。 entry.isIntersecting で切り分けているからだと思います(監視対象であれば 常に true になる様子)。 MDN を読んで後の、私なりの理解を回答欄に追記しました。
oikashinoa

2020/05/01 23:28

昔の事なので詳細忘れましたが… jsfiddleかcodepenでIntersection Observerで似た症状になって、ローカルでサーバー立てて動かしたら想定通りの動きになった覚えが有ります。 https://teratail.com/questions/177307 一度ローカルで試してみてはいかがでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問