javascriptを使ったSEO対策まとめ


一昔前まではjavascriptを使ったSEOに弱いというのがあったりしましたが、今ではGooglebotが大分賢くなりjavascriptを実行できるようになってきてます。

とはいえ何も考えなくてもいいかというとそうでもないので、javascriptを使った場合にSEO対策として意識しないといけないことをまとめてみました。

いろいろ書きましたが、 **Hisory APIを使ってURLをきちんと書き換えよう** っていうのが主です。(pjaxと呼ばれている手法です）


## クリックやスクロールでDOMを生成するコンテンツはインデックスされない

ページロード時点ではhtml内に生成されていないが、あるイベントが起きた時にDOM要素を生成するパターン。

Qiitaで言うとTOPページ下部にある「もっと見る」とかがそうですね。
![qiita.com-.jpg](https://qiita-image-store.s3.amazonaws.com/0/6808/cac0b30f-fefa-06e5-13b8-48df0702d962.jpeg "qiita.com-.jpg")


Googlebotはjavascriptを実行することはできるのですが、clickやスクロールのイベントは実行することができないため、イベントによって新しい要素が生成されるなかのコンテンツは読み取ることができません。

なので「 [Fetch as Google （Googleからどう見えるのかを確認）](http://qiita.com/ykyk1218/items/78ed3bce0371a4ee219c#fetch-as-google-google%E3%81%8B%E3%82%89%E3%81%A9%E3%81%86%E8%A6%8B%E3%81%88%E3%82%8B%E3%81%AE%E3%81%8B%E3%82%92%E7%A2%BA%E8%AA%8D)」で説明してますが画像後読み処理とかさせる場合はGooglebotからは画像が見えなくなる可能性があります。

`display:none`で隠すなど、初期表示の段階でHTMLに含まれていればインデックスの対象になるようです。
ただ、コンテンツの重要度は下がってしまうので気をつけましょう。

参考サイト
https://www.suzukikenichi.com/blog/content-in-javascript-wont-be-indexed-by-google-if-it-appears-only-when-clicked/


## ajaxで読み込んだページをインデックスさせたい

### 昔

ajaxを使って読み込んだコンテンツをインデックスさせる方法は昔と今とではやり方が変わりました。過去の方法は下記URLに書いてある通りです。

https://www.suzukikenichi.com/blog/how-to-make-crawlable-and-indexable-ajax-content/

1. #を#!に置き換える
2. Googleは#!を?_escaped_fragment_= に置き換えてリクエスト
3. サーバ側で?_escaped_fragment_で置き換えられたURLのHTMLを返す
4. サーバから返されたHTMLをGoogleがインデックス、URLは#!に戻す

でもサポート終了
http://googlewebmastercentral-ja.blogspot.jp/2015/10/deprecating-our-ajax-crawling-scheme.html

> 今後はプログレッシブ エンハンスメントの原則に沿って開発を進められることを推奨します。たとえば、HTML5 の History API を使用することで、Google のシステムだけでなく幅広いブラウザが履歴にアクセスできるようにすることが可能です。

**プログレッシブ エンハンスメント**とはCSS3をサポートしているようなブラウザではリッチな見せ方を、そうでないものはそれなりの見せ方を、、、というものです。IEもIE10になって細かい差はあるもののCSS3をサポートできてきているので、ちゃんとイケてる見せ方にした方がGoogle的にも良質のコンテンツと判断してもらえそうです。


### 今はどうすればよいのか

1. HistoryAPI
2. Fetch as Google

とりあえずこの2つを意識しておけばいいと思います。

基本的にクローラはjavascriptは実行してくれるのですが、問題もいくつかあります。
例えばajaxを使ってページング処理をするような場合、普通に作るとURLが変わらず、中身だけが変わっていくようになってしまいます。

URLが変わらないと別コンテンツとして認識されないためGoogleにインデックスされなくなってしまいます。

**そこでajaxで読み込むごとにHistoryAPIを使ってURLを書き換えて上げる必要があります。**

また、Googleクローラが賢くなったとはいえ、人の目で見るのとbotがみるのとでは多少の違いが発生する可能性があります。そこでGoogleがどうみているのかを確認する方法があります。

#### History APIの使い方

URLを書き換えるには2つの関数があります。

**pushState**

```
history.pushState(state, title, url)
```

- state：javascriptオブジェクト、この後で紹介するpopState側でこの値を受取ることができます。
- title：ページタイトルを指定するのかと思いきや、ここに値を書いても特に何もおこらず、、、将来的には使う可能性があるっぽい。
- url：変更するページのURL

**replaceState**

```
history.replaceState(state, title, url)
```

pushStateと同じ。

この2つの違いはブラウザの履歴に残すかどうかです。
pushStateで追加したURLはブラウザバックで戻れますが、replaseStateで追加したURLにはブラウザバックで戻れません。（履歴に残らない）

---

pushStateを使ってURLを書き換えた場合ブラウザバックで戻れるのですが、ちゃんとブラウザバックのイベントを取得して処理してあげないとURLが書き換わるだけでなにも起きません。

イベントを処理をするには **popstate** で対応します。

```
$(window).on('popstate', function(e) { <ブラウザの戻る、進むで処理したい内容> }
```


というわけでQiitaAPIを使って簡単なサンプル作りました。

![demo_qiita.gif](https://qiita-image-store.s3.amazonaws.com/0/6808/2b07c4da-6fbe-d92c-7cb4-d16e15d56d04.gif "demo_qiita.gif")


ちょっとわかりずらいですが、次へをクリックするたびにURLが書き換わっているのがわかると思います。

ソースは下記


```html
<html>
  <head>
    <script src="https://code.jquery.com/jquery-2.2.2.min.js" integrity="sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=" crossorigin="anonymous"></script>
    <script src="./purl.js"></script>
    <script>
      var page = $.url().param('page')
      if(!page) {
        page = 1
      }
      $(document).ready(function() {
        $.ajax({
          type: "GET",
          url: "https://qiita.com/api/v2/items?page="+ page +"&per_page=20",
          dateType: "json",
          success: function(msg) {
            html = ""
            for(var i=0; i<msg.length; i++) {
              html = html + msg[i].title + "<br>"
            }
            $("#qiita").html(html)
          }
        });

        $("#next").on('click', function(e) {
          e.preventDefault
          page = parseInt(page) + 1;
          loadData(page)
         });

         //ブラウザ操作
         $(window).on('popstate', function(e) {
           if (!e.originalEvent.state) return
           //$.url()で今のURLが取れるはずだけど、ブラウザバッグだと
　　　　　 //うまくとれない
           //location.pathnameだとURLが取れるのでこれを引数で渡す
           var url = $.url(location.pathname)
           var currentPage = url.param('page');
           if (currentPage == page) {
             return;
           }else{
             if(!currentPage) {
               page = 1
             }else{
               page = currentPage
             }
             loadData(page)
           }
         });

       });

       function loadData(page) {
         console.log(page)
         //url書き換え
         history.pushState(page, "Qiita" + page + "ページ目", "?page=" + page);
         $.ajax({
           type: "GET",
           url: "https://qiita.com/api/v2/items?page="+ page +"&per_page=20",
           dateType: "json",
           success: function(msg) {
             html = ""
             for(var i=0; i<msg.length; i++) {
               html = html + msg[i].title + "<br>"
             }
             $("#qiita").html(html)
            }
         });
       }
    </script>
  </head>
  <body>
    <div id="qiita"></div>
    <div><a id="next" href="javascript:;">次へ</a></div>
  </body>
</html>

```

#### purl.js

ここで使ってるpurl.jsはjavascriptを使ってURLからパラメータをとったりすることが簡単にできる便利ライブラリです。

https://github.com/allmarkedup/purl

#### HistoryAPIを使っている実例

例）無限スクロール
ferretの場合
https://ferret-plus.com/


TOPページで最新の記事一覧がページ下部に移動すると自動的に読み込むようになっています。
また各個別の記事ページも下部まで移動すると次のコンテンツを自動で読み込む用になっています。

ちゃんと読み込んだタイミングでURLが書き換わっていて、こうすることでURLとページ内のコンテンツが一致するためajaxでよみこむページもきちんとインデックスされるようになります。

![スクリーンショット 2016-03-22 15.48.55.png](https://qiita-image-store.s3.amazonaws.com/0/6808/49b5f048-c2bc-6e05-ce68-069d70b42578.png "ajaxで読み込んだページがインデックスされている")
↑ajaxで読み込んだページがインデックスされています。


例）別コンテンツの読み込み
twitterの場合

https://twitter.com/

![demo.gif](https://qiita-image-store.s3.amazonaws.com/0/6808/68be8359-3744-a817-360b-e822be9f41dc.gif "demo.gif")

tweetの中身をモーダルで表示させているのですが、URLはきっちり変わっています。
またモーダルを閉じるとURLが元に戻ります。

こうすることでちゃんとtweet1つ1つのページもインデックスされるようになっているんですね。


#### 注意

無限スクロール系とか別のコンテンツを読み込む場合はURLを変更するだけではだめで、他にもいろいろ変更しないとダメな箇所があります。

- title
- description
- ogp画像
- rel="next/prev"

等々、、、

要はajaxで読み込んだページをちゃんと1つのページとして表示させた時に正しく表示させるよう意識をする、ということになります。

`<head></head>`で囲まれているところでページごとに設定が必要な場合は対応する必要があります。

他にもいろいろ意識しないといけないことはあったりするのですが、詳細はこちらを参考にしてみてください。

[検索エンジンとの相性を考慮した無限スクロールのベストプラクティス](http://googlewebmastercentral-ja.blogspot.jp/2014/02/infinite-scroll-search-friendly.html)


#### Fetch as Google （Googleからどう見えるのかを確認）

Search Console（旧Webマスターツール）を使ってGoogleからの見た目を確認することができます。

見た目を確認するにはまず最初にサイトの登録をしなければならないのですがそこは省略します。

下記の画像は[phocase](https://phocase.jp/)というスマホケース販売サイトの検索結果ページでFetch as Googleを使って確認した様子です。
画像の後読み処理が入っているのですがGooglebotではスクロールイベントを拾うことができないので画像が読み込まれていません。

![s_スクリーンショット 2016-03-24 19.52.55.png](https://qiita-image-store.s3.amazonaws.com/0/6808/d0ce87eb-7b0b-0be8-8d6a-f7e34d601e3f.png "s_スクリーンショット 2016-03-24 19.52.55.png")

これがどのくらいSEOに影響をあたえるのか・・・は難しいところですがいい影響は与えることはなさそうですね。少なくとも画像検索の方にインデックスされることはなさそうです。

参考
http://wind-mill.co.jp/iwashiblog/2015/09/lazy-load-seo/
http://qiita.com/ryo_hisano/items/e8554777793551c105bd

とはいえ、

僕の勝手な見解としては、画像後よみにして表示スピード上げるほうが結果的にSEOにはいい影響を与える気がしてます。一応botからのアクセスの場合は画像を表示させる見たいなこともできるのですが、クローキングに近い対応な気がするので危ういイメージ。

クローキングというのはbotでアクセスした場合と人がアクセスした場合で表示を見せ方を変えることです。クローキングはGoogleから嫌われてしまう行為なのでやらない方が無難かなー、

それにそのうちGooglebotの方でスクロールイベントを認識できるようになるかもしれないので、そこに期待してます。