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

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

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

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

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

jQuery

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

CSS

CSSはXMLやHTMLで表現した色・レイアウト・フォントなどの要素を指示する仕様の1つです。

Q&A

解決済

2回答

4611閲覧

jQueryを使ってボタン操作でsection移動をうまくしたい

SaekoIwaki

総合スコア33

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

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

jQuery

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

CSS

CSSはXMLやHTMLで表現した色・レイアウト・フォントなどの要素を指示する仕様の1つです。

0グッド

0クリップ

投稿2016/09/10 01:33

現状・目標

シンプルに下ボタンを押せば下に、上ボタンを押せば上に
指定したクラスに一個ずつスクロールするjsを組みたいです。
参考URLを参考に作りました。
これの、逆をやりたいです。

問題点

上ボタンを押下したら、-2するようしましたが、
現在の問題点が、画面TOPから中途半端な部分に
セクションがある場合、上ボタンを押しても2つ前要素のsectionに移動してしまいます。
一番下に持って行った時の動きがわかりやすいかと思います。
###DEMO

かと言って-1に値を変えれば、次のsectionに移動してくれません。
sectionが中途半端な位置にある場合や、一番最後のsectionに到達した時に1つ戻る方法
はどのように記述すればよろしいでしょうか。
お力を貸していただけると嬉しいです。
ソースコード

HTML

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <div class="gridContainer clearfix"> <div id="next_btn">↓</div> <div id="back_btn">↑</div> <div class="section1 cor1 control"></div> <div class="section2 cor2 control"></div> <div class="section3 cor3 control"></div> <div class="section4 cor1 control"></div> <div class="section5 cor2 control"></div> <footer class="control">footer</footer> </div>

js

$(function() { var idx = -1; var controlPos = 0; var scrollTop = 0; var targetTop = 0; $(function(){ $(window).on("load scroll", function(e){ $(".control").each(function(index, ele){ controlPos = $(ele).offset().top; scrollTop = $(window).scrollTop(); idx = -1; if(controlPos > scrollTop){ idx = index; return false; } }); }); $("#next_btn").on("click", function(e){ if($(".control").eq(idx).is("*") && idx >= 0){ idx = idx; console.log("idx:"+idx); targetTop = $(".control").eq(idx).offset().top; $("html, body").animate({scrollTop:targetTop}, "normal"); } return false; }); $("#back_btn").on("click", function(e){ if($(".control").eq(idx).is("*") && idx >= 0){ idx2 = idx-2; console.log("idx2:"+idx2); if(idx2 === -1){ return false; } targetTop = $(".control").eq(idx2).offset().top; $("html, body").animate({scrollTop:targetTop}, "normal"); } return false; }); }); });

css

#next_btn{ line-height:70px; height: 70px; width: 70px; border-radius:100%; display: inline-block; background: #4285F4; color:white; margin: 1.3%; box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); top: 0; left:0; position: fixed; font-size:50px; text-align:center; } #back_btn{ line-height:70px; height: 70px; width: 70px; border-radius:100%; display: inline-block; background: #F44141; color:white; margin: 1.3%; box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); top: 0; right:0; position: fixed; font-size:50px; text-align:center; } footer{ width: 100%; height: 150px; float: left; background-color: bisque; } .section1{ width: 100%; height: 150px; float: left; } .section2{ width: 100%; height: 354px; float: left; } .section3{ width: 100%; height: 441px; float: left; } .section4{ width: 100%; height: 311px; float: left; } .section5{ width: 100%; height: 810px; float: left; } .cor1{background-color: tan;} .cor2{background-color: blue;} .cor3{background-color:darkcyan;}

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

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

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

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

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

guest

回答2

0

解決済みたいですが、少し間違っている部分や処理が連続していて重そうなので書いてみました。参考までに見てみてください。

HTML

1<html> 2<head> 3<style type="text/css"> 4 #next_btn{ 5 line-height:70px; 6 height: 70px; 7 width: 70px; 8 border-radius:100%; 9 display: inline-block; 10 background: #4285F4; 11 color:white; 12 margin: 1.3%; 13 box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 14 0 3px 1px -2px rgba(0, 0, 0, 0.2), 15 0 1px 5px 0 rgba(0, 0, 0, 0.12); 16 top: 0; 17 left:0; 18 position: fixed; 19 font-size:50px; 20 text-align:center; 21 22 } 23 24 #back_btn{ 25 line-height:70px; 26 height: 70px; 27 width: 70px; 28 border-radius:100%; 29 display: inline-block; 30 background: #F44141; 31 color:white; 32 margin: 1.3%; 33 box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 34 0 3px 1px -2px rgba(0, 0, 0, 0.2), 35 0 1px 5px 0 rgba(0, 0, 0, 0.12); 36 top: 0; 37 right:0; 38 position: fixed; 39 font-size:50px; 40 text-align:center; 41 42 } 43 44 footer{ 45 width: 100%; 46 height: 150px; 47 float: left; 48 background-color: bisque; 49 } 50 51 .section1{ 52 width: 100%; 53 height: 150px; 54 float: left; 55 } 56 .section2{ 57 width: 100%; 58 height: 354px; 59 float: left; 60 } 61 62 .section3{ 63 width: 100%; 64 height: 441px; 65 float: left; 66 } 67 68 .section4{ 69 width: 100%; 70 height: 311px; 71 float: left; 72 } 73 74 .section5{ 75 width: 100%; 76 height: 810px; 77 float: left; 78 } 79 80 .cor1{background-color: tan;} 81 .cor2{background-color: blue;} 82 .cor3{background-color:darkcyan;} 83</style> 84</head> 85<body> 86<div class="gridContainer clearfix"> 87 88 <div id="next_btn">↓</div> 89 <div id="back_btn">↑</div> 90 91 <div class="section1 cor1 control"></div> 92 <div class="section2 cor2 control"></div> 93 <div class="section3 cor3 control"></div> 94 <div class="section4 cor1 control"></div> 95 <div class="section5 cor2 control"></div> 96<footer class="control">footer</footer> 97</div> 98<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> 99<script type="text/javascript"> 100 101var myGf; 102//プログラムの実行 103$(function(){ myGf.scrollManager() }); 104 105//script.js 106(function(g) 107{ 108 //プログラム本体 109 g.scrollManager = function() 110 { 111 var el = {}; 112 113 var init = function() 114 { 115 el.window = $(window); 116 //$('html,body') 2重での指定は2重でイベントが発生する可能性があるので判定 117 el.body = $(isHtmlScrollable() ? 'html' : 'body'); 118 //可能な限りセレクタをキャッシュしておくと速度的にも、後々の修正的にも楽です。 119 el.scroolEl = $(".control"); 120 el.nextBtn = $("#next_btn"); 121 el.backBtn = $("#back_btn"); 122 el.scrollSpeed = "normal"; 123 124 el.isAnimate = false; 125 126 el.state = {}; 127 el.state.idx = -1; 128 el.state.maxIdx = el.scroolEl.length - 1; 129 130 el.target = {}; 131 el.target.top = 0; 132 el.target.bottom = 0; 133 134 setUp(); 135 }; 136 137 var setUp = function() 138 { 139 //ローディング時にサイトトップとは限らないのでここでインデックスを代入。 140 //細かいですがfor文内で欲しい結果が分かった直後break;で以降のループをストップ。 141 var windowScrollPos = el.window.scrollTop(); 142 if(windowScrollPos > 0) 143 { 144 for (var i = 0; i < el.scroolEl.length; i++) { 145 if( el.scroolEl.eq(i).offset().top > windowScrollPos ) { 146 addIdxAndSetPos(i); 147 break; 148 } 149 } 150 }else{ 151 el.target.top = el.scroolEl.eq(0).offset().top; 152 el.target.bottom = el.scroolEl.eq(0).height() + el.target.top; 153 } 154 155 start(); 156 }; 157 158 var start = function() 159 { 160 //イベントはまとめて指定しておくとあとで分かりやすいです。 161 el.window.on("scroll", scrollEvent); 162 el.nextBtn.on("click", next); 163 el.backBtn.on("click", back); 164 }; 165 166 // スクロール用イベント 167 var scrollEvent = function() 168 { 169 if(el.isAnimate) return; 170 var windowScrollPos = el.window.scrollTop(); 171 172 if(windowScrollPos < el.target.top || windowScrollPos > el.target.bottom){ 173 if(windowScrollPos > el.target.top) addIdxAndSetPos(1); 174 else addIdxAndSetPos(-1); 175 } 176 }; 177 // 下矢印クリックイベント 178 var next = function() 179 { 180 //el.isAnimate でアニメーション中かを判定して連打対策と 181 //animateでのスクロール中はスクロールイベントが走らないようにしています。 182 if(el.isAnimate) return; 183 addIdxAndSetPos(1); 184 scrollAnimate(el.target.top); 185 }; 186 // 上矢印クリックイベント 187 var back = function() 188 { 189 if(el.isAnimate) return; 190 if(el.window.scrollTop() <= el.target.top) addIdxAndSetPos(-1); 191 scrollAnimate(el.target.top); 192 }; 193 // 要素のカウントはこの関数内で、本当は外から触れない(触らせない)ような実装がいいかもですが、今回は適当に。 194 //合わせてターゲットとなる要素の始まり(el.target.top)と終わり(el.target.bottom)を代入しています。 195 var addIdxAndSetPos = function(add) 196 { 197 var tmp = el.state.idx; 198 el.state.idx += add; 199 if(el.state.idx > el.state.maxIdx) el.state.idx = el.state.maxIdx; 200 else if(el.state.idx < 0) el.state.idx = 0; 201 202 if(tmp == el.state.idx) return; 203 el.target.top = el.scroolEl.eq(el.state.idx).offset().top; 204 el.target.bottom = el.scroolEl.eq(el.state.idx).height() + el.target.top; 205 //FOR DEBUG 206 console.log(el.state.idx); 207 }; 208 // bodyをスクロールさせる。別で使う可能性もあるかもと思ったので引数で指定 209 var scrollAnimate = function(target) 210 { 211 el.isAnimate = true; 212 el.body.animate({scrollTop:target}, el.scrollSpeed,function(){ 213 //ブラウザによってイベントの発火間隔が違うのでその対策 214 setTimeout(function(){el.isAnimate = false;},100); 215 }); 216 }; 217 // html がスクロールできるか判定 218 var isHtmlScrollable = function() 219 { 220 var h = $('html'), t = h.scrollTop(), e = $('<div/>').height(10000).prependTo('body'); 221 h.scrollTop(10000); 222 var r = !!h.scrollTop(); 223 h.scrollTop(t); 224 e.remove(); 225 return r; 226 }; 227 228 init(); 229 }; 230 231})(myGf || (myGf = {})); 232 233</script> 234</body> 235</html>

投稿2016/09/12 07:46

IShix

総合スコア1724

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

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

SaekoIwaki

2016/09/12 16:11

解決済みにもかかわらず、軽量化も含めて 初めから書いていただき、ありがとうございました。 すべて理解したいので、時間をかけてでも理解して、作っていただいた方を使用したいと思います。大変勉強になります。 本当にありがとうございました。
IShix

2016/09/13 06:17

喜んでいただけてよかったです。せっかくなのでもう少しどこをどうしたらいいかを具体的に説明させてください。中には僕の個人的な意見もありますのでそうなんだー程度に見ていただけたらと思います。長文ですがよろしくお願いいたします。 $(document).ready(function(){}); $(function() { }); jQuery(function($) { }); 上記3つともおなじ意味です。1番目を省略したのが2番目、他ライブラリーとのコンクリフトを避けるためのコードが3番目です。これはHTMLタグ(DOM)の</body>が読み込まれた直後発火します。ちなみにSaekoIwakiさんが使っていたloadイベントは画像まで読み込まれないと発火しないので画像が含まれるページではイベントが動くのに時間がかかります。 $(function() { var idx = -1; var isDirty = true; var controlPos = 0; var scrollTop = 0; var targetTop = 0; $(function() { //(略) }); }); 変数のスコープを気にして2度書いていると思いますが実行タイミングがおかしくなるので大枠は無名関数で囲ってあげた方がいいとおもいます。あと変数内に関数を入れると任意のタイミングで実行できて便利です。無名関数の説明と変数への関数代入の説明のため僕が書いたコードを軽く説明します。 var myGf; 自分用のグローバル変数を宣言。すべての変数、関数はここにぶら下げます。そうすると使用するグローバル変数、関数がこの変数1つだけで済みます。 $(function(){ myGf.scrollManager() }); プログラムの実行 このコードを書かないとプログラムは実行されません。 (function(g) { //プログラム本体 g.scrollManager = function(){ //(略) }; })(myGf || (myGf = {})); 無名関数で囲う 最後の(myGf || (myGf = {}))でmyGfの変数のみの受け入れを許可しています。がオブジェクトならそのまま変数を受け入れる。オブジェクトでなければオブジェクトを作成。そしてfunction(g)でgに代入して無名関数内で使えるようにしています。 よくあるのがjsを外部ファイルで色々なページで読み込むと別ページで関係ないプログラムが実行されてしまう問題です。それを防ぐのが上記コードです。無名関数以下をファイル内に書き込んでも実行をしなければ走りません。 (function(g,$) { //プログラム本体 g.scrollManager = function(){ //(略) }; })(myGf || (myGf = {}),jQuery); JQueryのコンクリフトを避けるならこんな感じです。 最後に設計についてですが、Javascriptはユーザーサイドで実行されるプログラムなのでとにかく軽くを意識した方がいいと思います。eachなどはすごく重い処理なのでこのコストを払うに見合うかをちゃんと考えなければいけません。そして、できる限り疎結合で組むのも大切です。例えば今のようにidxのカウントを色々なところでやるとidxが関わる修正をする場合、すべての処理を一旦確認しないと次に進めません。他人はもちろん自分のコードでも1ヶ月後には忘れてしまいます。。。ですが、それを1箇所で行うことでそこ以外を見る必要がないので楽です。そして軽くすることに関しでですがもう少し具体的に説明します。 ▼ SaekoIwakiさんのコードの流れ ・今のスクロール位置を取得 x 5 ・要素のtopを取得 x 5 ・idxを決定 x 5 [処理回数] スクロール毎に10回の取得 、5回の判定 ▼ 僕が書いたプログラム [前準備] ターゲットとなる要素のトップとボトムを取得 ・スクロール毎に位置を取得 x 1 ・ ターゲットのトップより上にスクロールが進めばidxをマイナス x 1 ・ターゲットのボトムより下にスクロールが進めばidxをプラス x 1 [処理回数] 前準備で1回 スクロール毎に 1回の取得 2回の判定 といった感じで構成次第で軽さが全然違います。ただ僕のプログラムには欠点があってターゲットとなる要素が伸びたり縮んだりした場合、対応できないです。場合によっては毎度要素の高さをチェックするか、変更を検知する方式に変える必要がありますが処理コストがx1増えるだけなので大した痛手ではありません。もし必要でしたらどの部分を移動すれば実現できるか触ってみてください。 以上です。長々とすみません。JS書くの楽しいですよね!お互い頑張りましょう!
IShix

2016/09/13 06:19

追記です。言い忘れてました。あとJQueryを読み込む位置は最後の方がいいですよ。
SaekoIwaki

2016/09/13 13:32

IShikawan様 丁寧に解説していただき、ありがとうございました。 軽量化のことは考えておらず、改めてご指摘されてIShikawan様が作ったものと比較 してみて、確かに重い処理をしていたことに気づきました。 JSに関しては現在勉強中でして、今一行ずつわからないことは調べて解読中だったので 解説していただき助かってます。 今回はレスポンシブ対応なので、要素が伸びたり縮んだりした場合にも対応したJSを作りたいと思います。 ご指摘通り、JQueryを読み込む位置も最後に修正しておきます。 IShikawan様の用にレベルの高いJSがかけるように頑張ります! ありがとうございました。
guest

0

ベストアンサー

もっと綺麗な実装があると思いますが、とりあえず正しく動くようにしてみました
ポイントはスクロールが中途半端なとき(isDirty)を判定して、
それに合わせて動く量を-1か-2か切り替えています

javascript

1$(function() { 2 3var idx = -1; 4var isDirty = true; 5var controlPos = 0; 6var scrollTop = 0; 7var targetTop = 0; 8$(function(){ 9 $(window).on("load scroll", function(e){ 10 $(".control").each(function(index, ele){ 11 controlPos = $(ele).offset().top; 12 scrollTop = $(window).scrollTop(); 13 14 idx = -1; 15 if(controlPos == scrollTop){ 16 idx = index+1; 17 isDirty = false; 18 return false; 19 } 20 21 if(controlPos > scrollTop){ 22 idx = index; 23 isDirty = true; 24 return false; 25 } 26 }); 27 }); 28 29 $("#next_btn").on("click", function(e){ 30 if($(".control").eq(idx).is("*") && idx >= 0){ 31 idx = idx; 32 console.log("idx:"+idx); 33 targetTop = $(".control").eq(idx).offset().top; 34 $("html, body").animate({scrollTop:targetTop}, "normal"); 35 } 36 return false; 37 }); 38 39 $("#back_btn").on("click", function(e){ 40 if($(".control").eq(idx).is("*") && idx >= 0){ 41 if(isDirty) { 42 idx2 = idx-1; 43 } else { 44 idx2 = idx-2 45 } 46 console.log("idx2:"+idx2); 47 if(idx2 === -1){ 48 return false; 49 } 50 51 52 53 54 targetTop = $(".control").eq(idx2).offset().top; 55 $("html, body").animate({scrollTop:targetTop}, "normal"); 56 } 57 return false; 58 59 }); 60 61}); 62}); 63

投稿2016/09/10 03:21

popobot

総合スコア6586

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

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

NS-DOS

2016/09/10 07:32

横からですが… idx = idx; は idx = idx + 1; とかの貼り付けミスでしょうか。 それにしても綺麗ですっきりしたコードですね。勉強になります。 あと、質問者様へ。細かい事ですがボタンのCSSは cursor:pointer; とかを入れたほうがいいと思いますよ。
SaekoIwaki

2016/09/10 12:38

icchi様 返事が遅くなりすみませんでした。 先ほど確認しましたら理想通りに動きました。 中途半端な動きの時の対処が目からうろこです。 ありがとうございました。 NS-DOS様 idx = idx はただconsole.logで idxで何故か調べられなかったので idx = idxと入れてあげて見れるように下だけなので特に意味はなかったです。 やはりボタン類には cursor:pointer;等入れたほうがいいのですかね?気にしていませんでしたが、今後は気をつけます。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問