外部ページでもスムーススクロールするプラグイン 2015.03.26
プラグインはじめに
2015.5.10
サンプルページに外部サイトへのリンクを追加しました。
2015.5.6
スクリプトを一部修正しました。
前回の記事でつくったスムーススクロールのスクリプトをマイナーチェンジし、外部ページでもスムーススクロールするようにしてみました。
外部ページでもスクロール…、とはつまり、アンカーリンク付きの別ページを開いた時、遷移先のページでもスムーススクロールする、ということです。
今回は、プラグイン化もしてみました。
まだ、改良の余地はたくさんあると思いますが、もし良かったら使ってください。
smoothScrollEx.js(右クリック→リンク先を保存)
INDEX
- 使い方
- 仕組み
- jQuery Easing Pluginを使う
使い方
jQueryを読み込み、その後にsmoothScrollEx.jsを読み込みます。
デフォルトならこれだけで準備OKです。
1 2 3 4 5 6 7 |
<head> <meta charset="utf-8"> <title>smoothScrollEx Demo | ページ1</title> <link href="style.css" rel="stylesheet" type="text/css"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <script type="text/javascript" src="smoothScrollEx.js"></script> </head> |
あとは、href属性がハッシュ(#)から始まるアンカーリンクをクリックすれば、その対となる(id=アンカー名)のついた要素へスムーススクロールで移動します。
1 2 3 4 5 6 7 8 9 |
<a href="#first">アンカー1へ </a> <!--アンカーリンク--> <div id="first">アンカー1</div> <!--アンカー先 --> <a href="page2.html#first">ページ2のアンカー1へ </a> <!--アンカーリンク--> <!-----------------------別ページ----------------------------> <div id="first">アンカー1</div> <!--アンカー先(ページ2) --> |
サンプル(上と同じ)
オプション
smoothScrollEx.jsを直接編集することで、スクロールスピードやディレイ速度を変更することができます。
編集できるのは以下の値です。
1 2 3 4 5 6 7 8 |
var options = $.extend({ scrollSpeed: 1000, //スクロールする速さ(ミリ秒) easing: 'swing', //イージング(アニメーションの挙動)※要イージングプラグイン adjustment: -100, //移動完了時のウィンドウ位置の調整値 delayTime: 300 //待機時間(ミリ秒)※遷移する時のみ },options); |
上から順に見ていきますと、
scrollSpeed | スクロールする速さ。ミリ秒で指定。 |
---|---|
easing | ‘swing’,’linear’のどちらか。Easing Plugin使用可。 |
adjustment | 移動完了時、アンカー先のTopがそのままwindowTopになるのでデフォルトで-100にしている。 ぴったりにしたい場合は0にする。 |
delayTime | 別ページに移動した瞬間から、アニメーションスタートまでの時間。 すぐにアニメーションさせたい場合は0にする。 |
となります。
仕組み
スムーススクロール自体の仕組みはとても簡単です。
別記事でも紹介しましたが、href属性が#アンカー名から始まる属性をクリック→id=アンカー名の位置を取得し、windowがそこまでアニメーション、という流れです。
しかし、別ページに遷移する時も同じ動作をさせようとすると、すこし複雑になってきます。
ポイントは、href=”別ページ.html#アンカー名”というリンクをクリックした時の動作をいかにしてなくすか、ということだと思います。
smoothScrollExでは、要素をクリックした時に、href=”別ページ.html#アンカー名”を、href=”別ページ.html?id=アンカー名”と置き換えることで、それを可能にしています。
私が参考にしたスクリプトでは、あらかじめhref属性の”#”を”?=id”に変えておくという方法をとっていました。
(参考:jQuery 外部ページでもスムーズスクロールを実装する方法)
その方法だと、とても簡単に実現できるのですが、事前にHTMLをいじる必要がある点が少し面倒だと感じたので、クリックした時に自動で変換するようにしてみました。
ただ、href属性の”#”をすべて”?id=”に変えてしまうと、ページ内リンクや、別ホストのページにジャンプした時もアンカーリンクが機能しない、という問題点があります。
その問題点を解消するために、href属性の中身をチェックする関数を用意しました。
またif文で分岐させることによって、”#アンカー名”や”別ホスト.com/page.html#アンカー名”といったhref属性の”#”は、クリックしても変換されません。
これらの関数や条件分岐は、すべて検証したわけではないので、もしかすると漏れがあるかもしれません。
ここが今後の改良点だと思います。
より細かな点は以下に示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
;(function($) { $(window).load(function(){ var options = $.extend({ scrollSpeed: 1000, easing: 'swing', adjustment: -100, delayTime: 300 },options); $('[href*=#]').click(function(event){ //href属性に#が含まれている要素をクリックした時 var element = $(this); var elementHref = element.attr('href'); if(elementHref.search(/^#.+/) === 0){ //href属性が#から始まる場合(通常のスムーススクロール) var targetId = elementHref; var targetY = $(targetId).offset().top + options.adjustment; event.preventDefault(); $('html,body').animate({scrollTop: targetY},options.scrollSpeed,options.easing); }else { //それ以外の場合 var hrefSplit = elementHref.split(/#/); //href属性を"#"の前後で分割し、配列にする var hrefFirst = hrefSplit[0]; //#より前の文字列をhrefFirst var hrefLast = '#' + hrefSplit[1];//#より後の文字列を、"#"をつけてhrefLast var pathname = location.pathname; //URLのpathnameを取得 var pathnameSplit = pathname.split(/\//); //pathnameを"/"で分割して配列にする var pathNameLast = pathnameSplit[pathnameSplit.length - 1]; //pathnameの最後の文字列を取得 var hostname = location.hostname; //URLのhostnameを取得 function aHrefCheck(){ //"page.html#anchor","http://hostnameA/page.html#anchor","http://hostnameB/page.html#anchor"を振り分ける関数 var check1 = hrefFirst.match(/https?:\/\/.[^\/]+\//); //#より前の文字列に"http(s)://文字列"が含まれているか判別 if(check1 === null){ //含まれていない場合は"true"を返す return true; }else{ //含まれている場合 var check2 = check1[0].indexOf(hostname); //その文字列にhostnameが含まれているか判別 if (check2 != -1){ //含まれている場合、trueを返す return true; }else{ //含まれていない場合、falseを返す(何もしない) return false; } } } function aHrefChange(){ //"#"を"?id="に変換する関数 if (hrefFirst.indexOf(pathNameLast) != -1){ //#より前の文字列にpathnameの最後の文字列が含まれているか判別 ex."pageA.html#anchor" or "pageB.html#anchor" return; //含まれていたら何もしない }else{ //含まれていない場合、"#"を"?id="に変換する hrefLast = hrefLast.replace('#','?id='); elementHref = hrefFirst + hrefLast; element.attr('href',elementHref); } } if(aHrefCheck() === true){ //aHrefCheck関数でtrueが返ってきた時 aHrefChange(); //aHrefChangeが実行される }else{ //それ以外は何もしない(falseの時) return; } } }); //ページが読み込まれた時 var url = $(location).attr('href'); //URLのhrefを取得 var stringMatch = url.indexOf('?id='); //urlに"?id="の文字列が含まれているか判別 if(stringMatch === -1){ //含まれていない場合(-1を返す)、何もしない return; }else { //含まれている場合、アニメーションさせる var urlSplit = url.split(/\?id=/); var anchorLink = '#' + urlSplit[urlSplit.length -1]; var anchorLinkTop = $(anchorLink).offset().top + options.adjustment; $('html,body').delay(options.delayTime).animate({scrollTop: anchorLinkTop},options.scrollSpeed,options.easing); } return this; }); })(jQuery); |
jQuery Easing Pluginを使う
jQuery Easing Pluginをダウンロードし、smoothScrollEx.jsより前に読み込むことで、簡単にeasingを増やすことができます。
1 2 3 4 5 6 7 8 |
<head> <meta charset="utf-8"> <title>smoothScrollEx Demo | ページ1</title> <link href="style.css" rel="stylesheet" type="text/css"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <script type="text/javascript" src="jquery.easing.1.3.js"></script> <script type="text/javascript" src="smoothScrollEx2.js"></script> </head> |
オプションを’swing’→’任意のeasing’へ変更。
1 2 3 4 5 6 7 8 |
var options = $.extend({ scrollSpeed: 1400, easing: 'easeInSine', adjustment: -100, delayTime: 0 },options); |
追記
追記その1
このプラグインは、ローカル上では上手く動作しないことがあります。
特に、外部サイトへのアンカーリンクの場合、ローカル上では’#アンカー名’を’?id=アンカー名’に変更してしまいます。
そのため、リンク先ページのURLを見ると、スムーススクロールしないのに「○○.com/○○.html?idアンカー名」となります。
これは条件分岐部分に起因します。
条件分岐はかなり複雑です。
これは、私の努力不足によるもので、改善点は多くあると思います。
全パターンを思いつければいいのですが、頭が悪いので、おそらく漏れがあると思います。
漏れにはその都度対応していきたいと思います。
追記その2
コメントを受けて、遷移後のページのURLが「○○.com/○○.html?id=アンカー名」にならないようにするには、どうすればいいか、と色々考えました。
結論を言えば、スクリプトを追加して、オプションの’adjustment’を’0’にすれば可能です。
上のサンプルでは、別ページへのアンカーリンクをクリックすると、遷移してスムーススクロールした後、URLの’?id=アンカー名’が’#アンカー名’に変化します。
adjustmentを0に、というのは、URLが変化した時点で、ブラウザ側の動作が始まってしまい、adujustmentの値分移動してしまうからです。
なお、上記の理由から、’#アンカー名’自体を消してしまうということは困難です。
消してしまうと、消えた時点でブラウザの動作がスタートして、windowのscrollTopが0(つまりページTop)になってしまいます。
ブラウザの動作を無効化するスクリプトがあれば可能かもしれませんが、現時点で私には無理ですね。
今後の課題とします。
Thanks for reading to the end!!
別ページでも”?id=アンカー名”を非表示にするにはどうしたらいいでしょうか?
コメントありがとうございます。
「別ページ」とは、外部サイトということでしょうか?
外部サイトのアンカーリンクの場合→
最新のver1.1では、外部サイトのアンカーリンクの場合、「#id」は変更されずそのままです。
移動先ページのURLは、「○○.com/○○.html/#アンカー名」となります。
(サンプルページに新たに外部サイトへのリンクを追加しました)
サンプル
もちろん、移動先にはjsファイルがないので、スムーズスクロールはしません。
同サイト上でのページ移動で、リンク先ページurlの「?id=アンカー名」を非表示にしたいという場合→
現状の私の技術レベルでは困難です。
(ページ移動後にurlを書き換えると、ブラウザの動作が始まってしまうので、挙動がおかしくなってしまいます。これに関しては記事内で追記します。)
早速のご回答ありがとうございます。
私が希望したのは、後者です。
>同サイト上でのページ移動で、リンク先ページurlの「?id=アンカー名」を非表示にしたいという場合
ページ内では、アンカー名が表示されていなかったので、ページ移動でも同様にならないかなと思いお聞きしました。
ご丁寧にご説明下さりありがとうございました。
現状でも素晴らしいjsなのに変な希望をしてしまいました。すみません。
ありがたく使用させていただきます。
同じページ内の移動は、毎回ページTOP からの移動ではなく、クリックした位置からの移動は可能でしょうか?
ローカルでは正常に動作しますが、サーバーにアップした場合、同サイトの別ページに移動した時に、1回目はスクロールしません。2回目からは正常にスクロールします。最新のIE、Google Chrome、Edgeで試してみました。
下記ウェブサイトのHOMEの左上の「更新」をクリックし、次に「Tips」をクリックしてみて下さい。
本日確認したところ、2回目以降もスクロールしません。
メインメニューから移動先のページを一旦表示させた後は正常にスクロールします。
移動先のページのプラグインが読み込まれないからでしょうか。
よろしくお願い致します。
うまくいきました!ありがとうございます。
このページにたどり着く前に、変なサイトに邪魔されてなんぎしました。
けど、助かりました。
こちらのページを拝見して利用させていただきました。
有難う御座います。
概ねちゃんと動作しますが、1点だけ質問があります。
1回目はページ外でもスムーズスクロールしてidの位置に移動するのですが、いったん元のページに戻りもう一度の同じidリンクをクリックするとURLの最後に?id=undefinedがついてページ移動しません。
これを直すにはどうしたらいいのでしょうか。
宜しくお願い致します。
[…] 2018年9月現在で希望通りの動きができたのがこちらです! 外部ページでもスムーススクロールするプラグイン […]