どうもー、ご無沙汰です。
WEBおじさんです。
今回のネタはこちら。
「jQueryでoffset().topで要素の位置を取得しようとするとなんか絶対違う値が返ってくる」
こちらですがね、結構色々とGoogle先生に品を変え形を変えて質問してみたんですが、全然見当違いの内容が返って来て、解決までに時間がかかりました。
なんなら、何度か同じことで悩み通して、挙句に解決しなかったのでCSSやJSのやり方を変えた、ということもあります。
今回、クライアントからの依頼で制作していたところ、またもやこの問題に直面し、このままにしておくのはなんかとても癪に障るので、今回はWEB制作のプロとして絶対に原因究明、スッキリ解決をしてやろうという覚悟で臨もうと思いました。
そして、丸一日使って様々な角度からHTML、CSS、jqueryを徹底的に調べ上げ、遂に原因を突き止めたので、備忘録も含め、同様の内容で困っている方々への情報共有として書こうと決めました。
それでは、本編スタートw
offsetとは要素の位置を取得するためのメソッド
まず、このoffset().topとは何か、なぜこれが必要だったのか、ということをちょこっとお話しします。
offsetとは、document内のHTML要素の表示位置の座標を取得することができるjQueryのメソッドです。
どんな時に使用するかというと、スムーススクロールやレイジーロードのようなものなど、同ページ内での移動やスクロールしたときに何かのイベントをさせようとする際にトリガーとして「○○の位置まできたら」とか目的地を「○○の位置へ」といったような形を作ると思うのですが、その「指定する位置」を取得することが主な利用目的ですね。
今回、私が使用する目的としては、メールフォームの作成時、必須項目が入っていなかった場合に「送信」ボタンを押したときに必須項目が入っていなかった場合はその必須項目の位置を取得し、そこまでスムーススクロールさせる、という動きを作りたくてこのoffsetを使用しました。
しかし!
いくらやってもなぜか正常な値が返ってこないのです。。。
しかも何回テストしてもその都度ちょっと違う値が返ってくるし。。。
なんならマイナスの値が返ってくるし。。。
「どういうことなんだ、これは。」という最早怒りを通り越して悪ガキを説教する時の先生の感情みたいなものが私の全身を支配しておりました。
何度も何度も同じようなことが起きて、原因がわからないのでその都度こちらが妥協して別の方法にするのももういい加減にしろ、ということで時間も対してあるわけでもないのに徹底的にここと向き合うことに決めました。
原因はやはりCSS
先に言ってしまいます。
原因はやはりCSSでした。
まぁ、最初に睨んだ通りですがね。
あとは、どこのCSSが影響してこの原因を起こしているか、ということです。
これがまぁめんどくさい。
のですが。
現在はほとんどのブラウザ側に開発者ツール的なものが導入され、トラブル発生時の原因究明が非常に楽になりました。
以前はイチイチCSSを書き換え、アップ、リロードして確認、みたいなことがまぁ面倒で、検証の回数が多いのでどこをどう直したのかも忘れてしまうんですね。
それがまた別の問題を誘発したりして、ファイル比較ツールとかも使ったりするんですが、これももうなんかめんどくさくて、こういう検証を避けてきましたが、開発者ツールができてからこれらの検証の時間が圧倒的に減りました。
話が逸れましたが原因はCSSです。
なので、開発者ツールでそれっぽいところのCSSを色々と書き換えて検証してみるのですが、なかなかその原因となる箇所が見つからない。
なので、jqueryの書き方がいけないのか、と思い、動的ではなく一旦静的に、しかもid属性を付けてそのidを指定してやってみるも全く解決の兆しが見えず。
静的に指定しても同じならやはりCSS。
JSエラーも特に出てないし。
ということで、まっさらなHTMLを別で作って同じJSで動かしてみたところ、超普通に動作。JSの書き方は完璧なんだろう。
じゃあ、何が?
offsetで返ってくる値があるってことはどこかが基準になっているはず。
どこが基準になっているのか。
通常であれば、ページの左上を(0,0)として考えるはずなのに返ってくる値はなぜかマイナス。ここに原因究明のヒントがある。
ん?ページの左上が(0,0)返り値がマイナス?
超違和感。なんか、もしかして基準になるところが動いている?
これはなんかおかしい。
あ!もしかして、body内の要素がbodyの外側に行っちゃってる?的な?
なんか、bodyの高さがいつも同じ値だし!(開発者ツールで確認)
bodyのCSSにheight:100%、overflow-y:hiddenがある!全然気にしてなかったけど!このへん怪しすぎる!特にoverflow!
ちょっと外してみるー!?
すると、静的に指定したidの要素のところまでスーッといどうしたではありませんか!
なるほど!bodyにheightが100%、overflow-y:hiddenが指定された状態だとウィンドウの大きさがbodyの大きさになり、overflowがhiddenなのでbodyが伸縮せずに固定されたような状態になるのねー!
だから、スクロールして見えなくなっている上の方の要素はマイナスの位置になるのかー!
要は、bodyがheight:100%でoverflow:hidden(overflow-y:hidden)だった場合、bodyの枠は常にウィンドウ枠と同じサイズとなり、スクロールしてもbody自体は動かないため、offsetの基準は常に(0,0)。だが、bodyを枠として、内側の要素は上下に移動されるため、bodyという枠から上にはみ出した部分はマイナスの値が返り値となる。
しかし、jQueryで移動させる際の指定要素はbodyだったりするので、bodyをマイナスに動かそうとしてもページの一番上に行ってしまう、ということなんですね。
これが今回の全てのからくり。
※スムーススクロールの記述はこう書いていた
$(‘html, body’).animate({scrollTop:’移動先の位置’}, 400, ‘swing’);
この指定要素がbodyで、bodyにはoverflow-y:hiddenが指定されていたのでbody自体は伸縮せず、スクロールしてもbody自体は枠のような感じになり、その場に留まり続けウィンドウの外に動くこともないため、bodyを基準に上下に動かすのは「ページのトップに戻る」以外では希望の動きにならない原因だったのですね。
こういうことだったんですねー。
結局、bodyのheight:100%、overflow-y:hiddenを解除しても表示自体は変わらなかったので、この2つは解除しました。
今回、このコーディングは別の人に依頼していたため、このような状態になっていたと思われます。
恐らくこのCSSの書き方はレスポンシブに対応した感じのCSSなんだと思いますが、中途半端な形でこちらに振られたのでコーディングが未完成のままになっていたことがそもそもの原因でした。
今はCSSもJSもそんなに知識がなくてもコピペやファイルのダウンロードなどで簡単にサイトに動きをもたらすことができてすごく便利な時代ですが、一方で別の動きを追加した際に別のトラブルが発生し、詳細な知識がないために結局片方、又は両方の動きを諦めるようなことになってしまうことも恐らくあると思います。
個人的に、それはプロフェッショナルとしてあるまじきことだと思うので、目先の時間や労力にとらわれて出来合いのものを使用するのではなく、しっかりとした知識の基、自身でプログラムしたり出来合いのものを使用するようにしてもらいたいと思います。
ちなみに、今回の原因となったコーディング先はどうやら「チャ」の方がやっている会社さんらしいです。
「チャ」の方は優秀な方も多くいらっしゃる反面、金稼ぎの為に仕事が粗かったりいい加減な仕事をする方々が多いのも事実です。
そのせいでこちらが苦労し、迷惑を被ることも多々あるのです。
まぁ、実際は「チャ」の方々だけではありませんがね。
非常にもったいないと思うので、ちゃんとした知識を身に着けていただきたいと思う今日この頃です。
自分も含め。
ということで、今回はこの辺で。
また次回。