Bookmarkletを作ろう(ajax編)

javascriptとjQueryでBookmarkletを作ろうの第四回

# はじめに
[前回](https://qiita.com/kanaxx/items/68c50031eeceedca79cb) はブラウザに表示した画面（HTML）から要素を指定して値を抜き出すBookmarkletのサンプルを作ったので、今回はもう一歩進んで外部のAPIを叩いてみるBookmarlketに仕上げます。

#使うもの
- javascript
- jQuery3
- Page speed api

jQueryでajax叩く方法は、こちらのページを参考にさせていただきました。
https://gist.github.com/kawa-/33d72748ce9b120c785f6f13b5b525b3

#作るもの
googleの検索結果ページの各ページの表示スピードをgoogle page speed apiで調べ、HTML内に結果を挿入するものです。

実行結果
![image.png](https://qiita-image-store.s3.amazonaws.com/0/168315/a23b7d67-4c92-36cf-de16-f97b141928c9.png)

分かりにくいですが、73とか31がリアルタイムで計測したページスピードです。

##コード

全体

```javascript
void((function(f){
    var script = document.createElement('script');
    script.src = '//code.jquery.com/jquery-3.2.1.min.js';
    script.onload = function(){
      var $ = jQuery.noConflict(true);
      f($);
    };
    document.body.appendChild(script);
})(
function($, undefined){

    count=0;
    $('div.srg>div.g').each(function(i,e){

        href=$(e).find('a[href]').first().attr('href');
        score = $(e).find('div._score_');
        if( score.length ){
            console.log(score.text());
        }else{
            count++;
            if( count>5 ){
                console.log('break function');
                return false;
            }
            console.log(count + '(' + i + ')' + href);


            $.ajax({
                type: "get",
                url: "https://www.googleapis.com/pagespeedonline/v4/runPagespeed",
                timeout: 20000,
                cache: false,
                data: {'url':href, 'strategy':'desktop'},
                dataType: 'json'
            })
            .done(function (response, textStatus, jqXHR) {
                console.log(response);
                if (response.status === "err") {
                    console.error("err: " + response.msg);
                } else {
                    score = response['ruleGroups']['SPEED']['score'];
                    console.log(href+'=>' + score);
                    $(e).prepend('<div class="_score_">'+score+'</div>');
                }
            })
            .fail(function (jqXHR, textStatus, errorThrown) {
                
                console.log(jqXHR);
                alert("失敗: サーバー内でエラーがあったか、サーバーから応答がありませんでした。");
            })
            .always(function (data_or_jqXHR, textStatus, jqXHR_or_errorThrown) {
            // done,failを問わず、常に実行される処理
            });
        }
    });
    console.log('end function');
}
)
);
```

圧縮版

```javascript
javascript:void function(b){var a=document.createElement("script");a.src="//code.jquery.com/jquery-3.2.1.min.js";a.onload=function(){var a=jQuery.noConflict(!0);b(a)};document.body.appendChild(a)}(function(b,a){count=0;b("div.srg>div.g").each(function(a,c){href=b(c).find("a[href]").first().attr("href");score=b(c).find("div._score_");if(score.length)console.log(score.text());else{count++;if(5<count)return console.log("break function"),!1;console.log(count+"("+a+")"+href);b.ajax({type:"get",url:"https://www.googleapis.com/pagespeedonline/v4/runPagespeed",
timeout:2E4,cache:!1,data:{url:href,strategy:"desktop"},dataType:"json"}).done(function(a,d,e){console.log(a);"err"===a.status?console.error("err: "+a.msg):(score=a.ruleGroups.SPEED.score,console.log(href+"=>"+score),b(c).prepend('<div class="_score_">'+score+"</div>"))}).fail(function(a,b,c){console.log(a);alert("\u5931\u6557: \u30b5\u30fc\u30d0\u30fc\u5185\u3067\u30a8\u30e9\u30fc\u304c\u3042\u3063\u305f\u304b\u3001\u30b5\u30fc\u30d0\u30fc\u304b\u3089\u5fdc\u7b54\u304c\u3042\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002")}).always(function(a,
b,c){})}});console.log("end function")});
```


##解説１
**HTML内から対象を探す部分**

```javascript
$('div.srg>div.g').each(function(i,e){
  href=$(e).find('a[href]').first().attr('href');
```

1. googleの検索結果ページから、```<div class="srg">```の下の```<div class="g">```を探してきます。
2. 上のパターンで複数見つかった要素にたいし、eachメソッドを実行し
3. hrefを持つAタグの最初をやつのhrefの値を捕えます


##解説２
**Bookmarkletの起動制御部分**
Bookmarkletを何度も実行しても大丈夫なように、保険をかけておきます。

```javascript
score = $(e).find('div._score_');

if( score.length ){
   console.log(score.text());
}else{
   count++;
   if( count>5 ){
       console.log('break function');
       return false;
   }
//(省略)
```

1. 検索結果のかたまりから、```<div class="_score_">```を探す。これはgoogleの検索結果には含まれないタグなので基本的にはelseの処理をする。URLに対して2回目の実行の場合は、trueの処理に入る。
2. 検索結果で出てくるURLの全部に対して同時にAPIを投げると怒られるケースがあるので、1回のBookmarkletで実行するのは5個までにしておきます。


##解説３
**google のAPIを実行する部分**

```javascript
$.ajax({
    type: "get",
    url: "https://www.googleapis.com/pagespeedonline/v4/runPagespeed",
    timeout: 20000,
    cache: false,
    data: {'url':href, 'strategy':'desktop'},
    dataType: 'json'
})
```

APIを実行するコードはこれだけです。今回は、検索結果から取り出したURLとstrategy（PCのスピードを取るためのパラメータ）だけを渡しています。

Googleのpage speed apiのパラメータと戻り値は、こちらの資料にて調べられます。
https://developers.google.com/speed/docs/insights/v4/reference/pagespeedapi/runpagespeed

##解説４
**APIの戻り値を使う部分**

今回は、APIの戻り値からspeed scoreだけ取り出して、```<div>```タグを埋め込んでいます。この結果を埋め込むことで、一度調べたURLに対してAPIを実行しないようにします。

```javascript
.done(function (response, textStatus, jqXHR) {
    console.log(response);
    if (response.status === "err") {
        console.error("err: " + response.msg);
    } else {
        score = response['ruleGroups']['SPEED']['score'];
        console.log(href+'=>' + score);
        $(e).prepend('<div class="_score_">'+score+'</div>');
    }
})
```

コードは以上です。

#まとめ
こんな感じでブックマークレットを作ると、画面に表示されたHTMLを使ってAPIを投げて値を取ることができます。ページスピードを１つ１つ調べるようなお仕事が存在するとすれば、ブラウザにボタンを設定して押すだけで良くなります。
ブックマークレットは、スタート地点のHTMLの構造が変わるとうまく動かなくなりますが、社内のwikiページを起点とするような形にすれば、対象のURLを増やすのも簡単ですし、不意に形が変わって動かなくなることも少なくできるとは思います。

覚える技術要素はそんなに多くないですし、テスト的に試してみる環境を整えるハードルも低いので、いろいろ試してみるとよいです。