HTML5 History APIで非同期通信時にURLを変更する方法

HTML5 History APIで非同期通信時にURLを変更する方法

店長

店長

こんにちは、フロントエンドエンジニアの店長です。

先日記事が出てましたが改めて自己紹介します。
大学卒業後はカフェで仕事をしていたのですが、退職して1年半ほどWebデザイナーをしていました。そして、LIGにはフロントエンドエンジニアとしてジョインすることに。

お察しのとおり、店長というアダ名はカフェで働いていたためです。
今後ともよろしくお願いします。

さて、今回はHTML5のHistory APIについてお話したいと思います。

History APIについて

History APIには以前からブラウザの履歴(スタック)を行き来する機能があったのですが、HTML5でさらに以下のような機能が追加されました。

  • 画面を遷移せず、履歴に新たなURLを追加する。
  • 現在のページの履歴を変更する。
  • ブラウザの戻る・進むボタンをクリックしたときにイベントを検知する。

このような機能がどんな場面で使われているかというと、非同期通信でページ内を更新したときに、新たにURLを発行する場合です。
通常は非同期でページ内を更新した場合、URLは変わらないので、戻るボタンをクリックしたら最初の状態に戻ってしまいます。
そこで通信した際に新たなURLを発行してあげることで、通信後の状態をURLで残しておくことができるようになります。

今回は実際にデモを作りながら説明していきたいと思います。

DEMOについて

今回作るDEMOはjQueryでAjax通信を行って写真を取得するものです。
取得した際はHistory APIを使用して、履歴の追加を行います。
また、ブラウザの戻る・進むボタンがクリックされた際は、クリックした先のURLのデータを取得します。

DEMOはこちらからご覧になれます。

Ajaxで非同期通信する簡単なスライドを作る

まずはじめにAjaxで読み込みを行うデモを作りたいと思います。

ファイルの構成

├── css
│   └── style.css
├── data
│   ├── page1.html
│   ├── page2.html
│   ├── page3.html
│   └── page4.html
├── image
│   ├── 1.jpg
│   ├── 2.jpg
│   ├── 3.jpg
│   └── 4.jpg
├── js
│   └── app.js
├── page1.html
├── page2.html
├── page3.html
└── page4.html

各ページのHTMLファイルを用意し、dataフォルダの中には通信したときに取得する中身を用意しておきます。

 

<div class="container">
    <div class="photo" id="photo">
        <img src="image/1.jpg">
    </div>
    <div class="pager" id="pager">
        <li class="pager-num"><a class="is-current" href="page1.html">1</a></li>
        <li class="pager-num"><a href="page2.html">2</a></li>
        <li class="pager-num"><a href="page3.html">3</a></li>
        <li class="pager-num"><a href="page4.html">4</a></li>
    </div>
</div>

ページ内の構成は写真とページャーというシンプルな構造です。

JavaScript

$(function(){
    var BASE_PATH = '/history-api/data/';
    var request = null;
    var $pager = $('#pager').find('a');

    //ページャーをクリックしたとき
    $pager.on('click', clickHandler);

    //クリックした際に実行される関数
    function clickHandler(e){
        e.preventDefault();
        var self = this;
        var page = $(self).attr('href');
        //ページャーの変更
        changePager.apply(self);
        //データをロードする
        loadingData(BASE_PATH + page);
    }

    //ページャーを変更する
    function changePager(){
      $pager.removeClass('is-current');
        $(this).addClass('is-current');
    }

    //データを取得する
    function loadingData(url){
        //requestを確認してabort
        if(request){
            request.abort();
        }
        //データのロード
        request = $.ajax({
           type: 'GET',
           dataType: 'html',
           url: url
        }).done(function(data){
            $('#photo').html(data);
        });
    }
});

まずは、ページャーをクリックしたらそのページの情報を読み込むようにします。
page1.htmlの場合はdata/page1.htmlの情報を取得します。
通信はjQueryの$.ajaxで行います。

クリックしたときはrequestという変数の中身をチェックし、$.ajaxが含まれている場合は.abord()メソッドを使用して、現在通信してる分をキャンセルします。
こうすることで余計なリクエストを減らすことができます。

pushState()で履歴を追加する

では、この中に、クリックされたとき新たに履歴を追加する機能を作っていきましょう。

履歴を追加するにはhistory.pushState()を使用します。

history.pushState(state, title, url);

state

任意のオブジェクトを渡すことができます。渡したオブジェクトはpopStateイベントハンドラで参照が可能です。

title

タイトルを指定できますが、現在は特に使われていません。

url

追加するURLを指定します。

今回はstate、titleは特に使用しないのでnullを指定しておきます。
ページャーがクリックされたらページを移動したいので、ここにコードを追加します。

//クリックした際に実行される関数
function clickHandler(e){
    e.preventDefault();
    var self = this;
    var page = $(self).attr('href');
    //履歴の追加
    history.pushState(null, null, page);
    //ページャーの変更
    changePager.apply(self);
    //データをロードする
    loadingData(BASE_PATH + page);
}

ただ、このままだと現在のページと同じURLをクリックした際もURLを追加していしまうので、それを回避します。

 

//クリックした際に実行される関数
function clickHandler(e){
    e.preventDefault();
    var self = this;
    var page = $(self).attr('href');
    var currentPage = getCurrentPage();
    //クリックしたリンク先のURLと現在のURLが一致する場合は処理を終える
    if(page === currentPage){
        return;
    }
    //履歴の追加
    history.pushState(null, null, page);
    //ページャーの変更
    changePager.apply(self);
    //データをロードする
    loadingData(BASE_PATH + page);
}

//現在のページ名を取得する
function getCurrentPage(){
  return location.pathname.split('/').pop();
}

最初にcurrentPageで現在のページ名を取得し、ページャーがクリックされたらcurrentPageとクリックされたリンクのhrefを比べます。
もし同じ場合はreturnして処理を終えます。
違う場合はcurrentPageに新たにパスを追加して、pushStateで履歴(スタック)にも追加を行います。

LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。

Webサイト制作の実績・料金を見る

この記事のシェア数

フロントエンドエンジニアの店長です。 LIGに入社と同時に店長(あだ名が)になりました。偉くはありません。 以前、某カフェで働いていました。 音楽とコーヒーが大好きです。よろしくお願いいたします。

このメンバーの記事をもっと読む