最終更新日から1年以上過ぎています。
内容が古くなっている可能性もあるのでご注意ください。
ウェブページにおいて「別の場所に移動する」といえばaタグですが、html言語にデフォルトで用意されているオプションは「同画面遷移か別画面遷移(target=”_blank”)か」くらいしかなく、演出も特になくパッと画面が変わるだけです。
ほんとはtarget属性にはもっと種類がありますが、<frame>ありきの値ばかりなのでノーカン
リンクの種類によって処理を変えたい
リッチな体験ができるウェブサイトを作ろうとすると、htmlデフォルトの挙動だけではちょっと物足りなくなってきます。
最低限このくらいの処理分けはしたいところ。
- 同じページ内のリンクだったらぬるっとアニメーション演出で移動
- サイト内別ページへのリンクだったらローディング演出を挟んで移動
- 外部サイトやtarget=”_blank”だったら演出ナシで即移動
CSSではaタグを押してから遷移の間に演出を挟むことはできないので、javascriptの出番になってくるわけですが、ひとつひとつのリンクオブジェクトにクラスを付けて判定するのはちょっと微妙。
クラスの付け忘れとか発生する未来が見えますし、パーツファイル化している場合などは「このリンクはあるページにおいては別ページへのリンクとなるが、またあるページにおいてはページ内リンクにもなり得る」みたいなケースも出てきます。
ということで、「aタグを押した時に、ジャンプ先を判定して処理を変える」ためのjQueryを実装します。
考え方
まずはデフォルトの挙動をキャンセル
まずはaタグを押した時に「hrefプロパティで指定された値にジャンプする」というhtmlデフォルトの挙動をいったん待ってもらわなければいけません。
それにはコレです。
$(document).on('click', 'a', function(e) {
e.preventDefault(); //htmlデフォルトの挙動をキャンセルする
});
いくらリッチなアニメーションをつけようとも、この処理をいれなければアニメーションが終わるのを待たずに遷移して終わりです
判定に必要なデータを取得
この後にゆっくり判定処理を入れていきます。
はじめ「ジャンプ先によって処理を変える」と書きましたが、正確には「現在表示しているURLとhrefで指定されているURLの関係性によって処理を変え」ます。
なので、「現在地」と「目的地」を取得していきましょう。
$(document).on('click', 'a', function(e) {
e.preventDefault();
const from = location.origin + location.pathname + location.search; //①現在地のフルパス(ただし#アンカーは除く)
const to = $(this).prop('href'); //②目的地の絶対パス
const check = to.replace(from,''); //③toとfromの差分
const blank = $(this).attr('target'); //④targetプロパティの値
});
各種データを4つの定数に格納しました。上から解説していきます。
①現在地のフルパス(ただし#アンカーは除く)
現在表示しているURLを取得します。単純にフルパスを取得したければlocation.href で十分なのですが、今回「アンカー(#)」は除きたいという事情があるのでちょっとまだるっこしい形になりました。
locationメソッドにはこのほかにもいろんなプロパティが用意されていて、組み合わせてごにょごにょすれば大体の値は取れるのではないかな~と思います。
また別の機会に勉強がてらまとめます
②目的地(href先)の絶対パス
次に目的地のURLを取得します。hrefプロパティに入力されている値をそのままとると絶対パスだったり相対パスだったり「#」だけだったりいろいろ表記ブレしているので、①でとったfromと突き合わせるために$(this).prop(‘href’);で絶対パスをとってきます。
ちなみに単純にhrefの値をそのまま取りたければ$(this).attr(‘href’);でOK
③toとfromの差分
①現在地と②目的地が問題なく取得できたら、それを突き合わせて差分を出していきます。
具体的には②目的地の文字列内から①現在地の文字列を見つけて取り除いた「③check」という変数をつくる、ということをしてます。
例をいくつか出すと、こんな感じになります。
- ①http://example.com/
- ②http://example.com/sample/
- ② – ① = ③sample/
- ①http://example.com/directory/
- ②http://example.com/
- ② – ① = ③http://example.com/
- ①http://example.com/
- ②http://example.com/#section2
- ② – ① = ③#section2
- ①http://example.com/(#section1だけどアンカーは取得されていない)
- ②http://example.com/#section2
- ② – ① = ③#section2
- ①http://example.com/
- ②http://example.com/?parameter=param1
- ② – ① = ③?parameter=param1
このcheckの文字列を判定に使っていく感じになります。
④targetプロパティの値
基本的には「ページ内遷移はアニメーション、サイト内遷移は同タブ、外部サイト遷移は別タブ」という分岐を作っていきますが、中には「サイト内遷移だけど別タブで出したい」というケースもあると思います。そういう時にhtml側でaタグにtarget=”_blank”を設定するだけで望む動きになるよう、値を取得してこれも判定に使います。
判定
材料が揃ったらいよいよ料理します。ifで処理を分岐させていきましょう。
$(document).on('click', 'a', function(e) {
e.preventDefault();
const from = location.origin + location.pathname + location.search;
const to = $(this).prop('href');
const check = to.replace(from,'');
const blank = $(this).attr('target');
if(blank == '_blank' || to.indexOf(location.origin) == -1) {
// ①target="_blank"、もしくは別サイトに行く時の処理
} else if( check.indexOf('#') == 0 || check == '') {
var target = 0;
if (check != '') {
target = $(check).offset().top;
}
// ②ページ内リンクだった時の処理
} else {
// ③サイト内別ページへジャンプする処理
}
});
①target=”_blank”、もしくは別サイト
さきほどの④でとったtargetプロパティの値が’_blank’だった場合、もしくは飛び先の絶対パスにlocation.origin(現在地のドメインが)含まれていない=外部サイトへのリンクである場合…が、分岐①です。
別タブ遷移処理を入れる想定です。
②ページ内リンク
変数checkの値が「#から始まっている(=アンカーだけ)」もしくは「空白」だった場合、アンカー以外のURLが全く一緒だったことになります。つまりこの場合は分岐②「ページ内リンク」となります。
この場合はぬるっとアニメーションして目的地へ移動する処理を入れて行きたいところですが、この目的地もjsで取得してあげる必要があるので、また分岐を入れています。アンカーがある場合はアンカーの位置を取得。アンカーがない場合はページトップ(0)を目的地(変数「target」)に入力しています。
③サイト内別ページ
①②に条件が当てはまらなかったケースはすべて③の「サイト内別ページ」とみなしています。
ここには「同画面遷移」という一番オーソドックスでつぶしのきく処理を入れる想定なので、「サイト内別ページ」以外の想定外なリンク先が仮に発生したとしても破綻はしないでしょう。
コピペ用ソース
ここまでを経てまとめたソースがこちらです。
上の「考え方」で説明した処理は条件分岐まででしたが、一応実際の処理まで入れています。
$(document).on('click', 'a', function(e) {
e.preventDefault();
const blank = $(this).attr('target');
const fromDomain = location.origin;
const from = fromDomain + location.pathname + location.search;
const to = $(this).prop('href');
const check = to.replace(from,'');
if(blank == '_blank' || to.indexOf(fromDomain) == -1) {
hrefBlank(to);
} else if( check.indexOf('#') == 0 || check == '') {
var target = 0;
if (check != '') {
target = $(check).offset().top;
}
hrefInPage(target);
} else {
hrefPageToPage(to);
}
});
function hrefBlank(target) {
window.open(target, '_blank');
}
function hrefPageToPage(target) {
// ローディング演出
$("演出に使ったDOM要素").on('animationend webkitAnimationEnd',function(){
window.location = target;
});
}
function hrefInPage(target) {
$('body,html').stop().animate({scrollTop:target}, 800);
}
このソースに足りないところ
この処理で大体のリンクには対応できますが、いくつか既知の不具合があります。
仮に?propaty=1¶meter=2&status=3など、&で区切って複数のパラメータを扱うサイトだった場合、パラメータの順序が違うと「同じページ内のリンク」だったとしても「同サイト別画面へのリンク」と判定されてしまうのです。
この場合は、取得したパスを判定する前に&でsplitしてアルファベット順に並べて…など、もうひと手間必要になってくるでしょう。
ただ、「同じ内容を持つURLが複数ある」というのはそれはそれでSEOなどの問題があるので、ここで処理をごにょごにょするよりは、もっと上流でパラメータの順序を制御してバリエーションが生まれないように(URLの正規化)した方がベターだと思います。
複数のパラメータを扱うような大規模サイトを作っている方は上級者だと思うので、ここで解説しなくともきっとご自分で解決されることでしょう…( ˘ω˘ )
また、URLのパラメータには、ページの表示内容に影響するものと、しないもの(計測用などのために入れているパターン)があります。
現状のコードだと、ページの表示内容に影響しないパラメーターも差分の突き合わせに使われるので、この場合も実質はページ内リンクなのにページ間リンクの挙動が発生する原因となります。
これを避けるには、表示に影響しないパラメーターを配列などにまとめておいて、取得したデータから削除するなど、やはりひと手間足す必要があります。
というわけで、aタグの飛び先に応じて処理を分ける条件分岐jQueryでした。
コピペ導入するだけで大体のサイトで動くので(サイト内別画面への遷移だけローディング演出部分を組み込む必要がありますが)、よかったらご利用ください。
recommend
develop / 2021.05.09
svgの縦横比を可変にするdevelop / 2021.02.15
WordPressデフォルト投稿タイプのアーカイブを任意のURLにするdevelop / 2021.02.04
ホバーで背景色がスライドしながら変わるCSSgraphic / 2021.02.14
意外と奥が深い「ダミーテキスト」の話graphic / 2021.05.15
NexusFontが重い場合は「Font Cache」を無効にするといいdevelop / 2021.06.07
タッチデバイスでだけ効くcssを書く方法