ブックマークレットによるHTMLリンクチェッカ
はじめまして、入社半年のkatsuraです。初投稿です。よろしくお願いします。
品質管理グループにてHTMLメールのテストを日々の業務で行っていた中で、テストを補助するツールを作成しました。今回はそのツールについて紹介したいと思います。
HTMLメールのテスト
HTMLメールのテスト項目の一つに、リンクチェックというものがあります。
リンクチェックとは、HTMLメール内のリンクテキストや画像とリンク先のURLが、リンク仕様書通りに正しく記載されているかのテストです(下の図参照)。
これを行うことにより、例えば「商品画像をクリックしたら、別の商品のページが開いてしまった」というような事故を防ぐことができます。

このリンクチェックというテストは、誰にでも出来る単純なテストですが、正確にテストしようとすると面倒なテストです。
リンクのURLが上の図のようにわかりやすいものであれば良いのですが、商品URLの場合複雑なものも多いため、ひと目見ただけでは仕様書のURLと一致しているか判断がつかないものが多いです。
ツールを作成する以前、HTMLメール制作部門ではHTMLメールのリンク一つ一つにカーソルを当て、ブラウザのステータスバーに表示されるURLが正しいかどうかを一つづつ目視で確認していました。
しかしリンク一つ一つを正確にチェックする作業は神経を使う作業であり、また人の手で行うものなので、どうしてもミスが出やすいものでした。
そこで、このテストを簡単に行えるようにするための補助ツールを作成することにしました。補助ツールの出力としては、以下の点を満たすものを目標としました。
- HTMLメール内に存在するURLが仕様書のどのキャプションに存在するかをHTMLメール内に直接表示する
- 仕様書に存在するURLがHTMLメール内に存在するかどうかの結果を表で表示する
これを図で説明すると以下のようになります。
実装の選択肢
補助ツールのイメージが出来たところで、まずどの言語あるいは、どの手法を用いてこの補助ツールを実装するか検討しました。
- RubyやPythonなどのローカル実行のスクリプト言語を用いた実装
→配布先で言語のインストールが必要であり、プログラマー以外への配布が難しいので見送りました - Excel VBAによる実装
→HTMLの解析をVBAで行うのは大変そうだったので見送りました - JavaやC#などを用いたGUIアプリケーションによる実装
→利用者側のマシンで動かすアプリケーションを考えていたのですが、更新時に利用者側に都度アップロード作業が発生してしまい面倒なため見送りました - PHPなどサーバー側の言語による実装
→サーバーサイドのプログラミング経験があまりなかったので見送りました - ブックマークレットという技術を使ったJavaScriptによる実装
→HTMLとの親和性が高く、ブラウザで実行するので利用者側で特別な準備が不要で利用できる上、更新に際しての再配布も利用者が意識する必要がないため採用しました
というわけで、様々な言語による実装を考え、最終的に行き着いたのがブックマークレットという技術を使ったJavaScriptによる実装でした。
ブックマークレットという形で提供するのであればブラウザさえあれば実行できるので、プログラミングに詳しくない人に使ってもらうのに問題ありません。また、ブックマークレットであれば、ローカルのHTMLファイル対してもWeb上のHTMLファイルに対しても同じ方法で実行できるというメリットもあります。
以降は、ブックマークレットについての簡単な解説と、今回実装したJavaScriptの紹介を行います。
ブックマークレットについて
ブックマークレットについて今更ですが簡単に説明しようと思います。
ブックマークレットとは、一言で言うと 「JavaScriptを実行できるブックマーク(お気に入り)」 のことです。ブラウザのプラグインと比べるとできることには制限がありますが、ブラウザに依存せずに実行でき導入も簡単というメリットがあります。
では、具体的にブックマークレットはどのように使うものなのか、ブックマークレットのサイトにて公開されている、「Internet Archive 検索」というブックマークレットを例に説明していきたいと思います。
Internet Archiveとは世界中のWebサイトのログを保存・公開しているサイトであり、このブックマークレットを使うと任意のWebページのログをワンクリックで表示することが出来ます。
このブックマークレットを登録するためには、ブックマークレットを公開しているページヘいき、ブックマークレットを登録するだけです。
あとは、ブックマークレットを使いたい画面で、先ほど登録したブックマークレットをクリックするだけです。
このように、ブックマークレットという技術を使うと簡単にJavaScriptを実行することができます。
完成した HTML リンクチェック用のブックマークレット
さて、そんなわけで実装完了したブックマークレットを早速ご紹介します。以下のリンクから登録することができます。
対象とするページの文字コードごとにブックマークレットを用意しています。
使い方
まず、ブックマークレット用のリンクをブックマークに登録します。
登録したブックマークレットを実行するとリンクチェッカが起動します。
ブックマークレットとHTMLファイルの文字コードが一致していないと文字化けしてしまうので、対応する文字コードのブックマークレットを実行するようにしてください。
タブ区切りで仕様書を入力し、実行を押すとリンクチェックが実行されます。
実行結果は以下のようになります。
ソースコード
今回作成したブックマークレットは、ブラウザに登録するJavaScriptとサーバーに設置するJavaScriptの2つで構成されています。
- ブックマークレット用のJavaScriptはサーバー用のJavaScriptとjQueryを呼びだすことのみをしています。
- サーバーに設置するJavaScriptはリンクのチェックなどの主要な処理を行います。
ブックマークレット用のJavaScript
javascript:(function(f,s){s=document.createElement("script");s.src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js";document.body.appendChild(s);s=document.createElement('script');s.src='https://lab.tricorn.co.jp/wp-content/uploads/2015/04/linkchecker_simple.js';s.onload=function(){f(jQuery.noConflict(true));};document.body.appendChild(s);})(function($){init($);});
サーバーに設置するJavaScript
var colorRed = "#d74a38", colorBlue = "#0063a4";
var colorYellow = "#ffec67", colorGray = "#e6e6e6";
//起動画面表示
//仕様書の入力欄などを作成
function init($,text){
if( $("#fixedArea").length != 0 ){
alert("リンクチェッカは起動済みです!");
return;
}
$("body").append("<div id='fixedArea'></div>");
$("#fixedArea").css({"position":"fixed"
, "top":"0px"
, "left":"70%"
, "height":"800px"
, "width": "30%"
, "overflow": "scroll"
, "resize" : "vertical"
, "background": "white"
, "z-index": "99"
});
$("body").prepend("<div id='fixedSpace'></div>");
$("#fixedArea").append("<div id='inputArea'></div>")
$("#inputArea").css({"margin":"auto","width":"800px"});
$("#inputArea").append("<button id='checkAll'>チェック</button>")
$("#inputArea").append("<textarea name='linkText' id='linkText' rows='6' cols='80'>")
$("#linkText").val(text);
//checkAllボタンが押されたらリンクチェックの結果を表示する
$("#checkAll").click(function(){
tableShow($);
});
$(window).on('load resize', function(){
$(".linkComment").each(function(){
a = $(this).closest("a");
var top = a.position().top;
var left = a.position().left;
$(this).css({"left" : "" + left + "px"});
});
});
}
//結果の表示
function tableShow($){
var text = $("#linkText").val();
var line = text.split("\n");
var urlList = [];
var undefined;
//リンク仕様書データの読み込み
for( var i = 0; i < line.length; i++ ){
var str = line[i].split("\t");
if( str.length > 1 ){
urlList.push([ i, str[0], str[str.length - 1] , 0 ] );
}
}
//リンクの巡回
$("a").each(function(index){
var a = $(this);
var url = a.attr("href");
if( url === undefined ){return true;}
var flag = false;
var linkedCount = 0;
var top = a.position().top;
var left = a.position().left;
if(a.children("img").length > 0){
//画像リンクの場合には赤枠で囲む
a.children("img").each(function(index2){
$(this).css({"border-color":colorRed,"border-width":"3px","border-style":"solid"});
top = $(this).position().top;
left = $(this).position().left;
});
}
//リンクの説明用のdivを追加
a.prepend("<div id='link" + index + "' class='linkComment'></div>");
$("#link" + index ).css({"position":"absolute"
,"display":"block"
,"color":"white"
,"background":colorRed
,"border-color":"white"
,"border-width":"2px"
,"border-style":"solid"
,"width":"180px"
,"height":"20px"
,"font-size":"12px"
,"font-weight":"bold"
,"text-align": "left"
,"scroll":"visible"});
if( left != 0 ){
$("#link" + index ).css({"left" : "" + left + "px"});
}
var divText = "";
for( var i = 0; i < urlList.length; i++ ){
if( urlList[i][2] == url ){
flag = true;
divText = divText +"<span id='jump" + i + "_" + urlList[i][3] + "'>" + i + ":" + urlList[i][1] + "</span>";
urlList[i][3]++;
linkedCount++;
$("#link" + index ).addClass("linkComment" + i);
}
}
if(flag == false){
divText = divText +"<span id='jump" + urlList.length + "_" + 0 + "'>"+urlList.length+"</span>";
urlList.push([ urlList.length, "HTMLにのみ存在", url, 1 ]);
linkedCount++;
}
$("#link" + index ).css({"height":"" + linkedCount * 12});
$("#link" + index).html(divText);
});
//入力ボックスの消去
$("#inputArea").remove();
//閉じるボタンを追加
$("#fixedArea").append("<button id='close'>閉じる</button>");
$("#close").click(function(){
$("#fixedArea").remove();
$(".linkComment").remove();
$("img").css({"border-width":"0px"});
});
//結果表示の表を追加
$("#fixedArea").append("<table id='result' style='width:1000px'></table>");
var headerText = '<tr><th style="width:20px">id</th>'
headerText = headerText + '<th style="width:60px">出現回数</th>';
headerText = headerText + '<th style="width:220px">キャプション</th>';
headerText = headerText + '<th style="width:700px">URL</th></tr>';
$("#result").append(headerText);
for( var i = 0; i < urlList.length; i++ ){
$("#result").append("<tr id='tr" + i + "'>");
$("#tr" + i).append("<td>" + i + "</td>");
var tmpText = "";
for( var j = 0; j < urlList[i][3]; j++ ){
tmpText = tmpText + "<a href='#jump" + i + "_" + j + "'>" + (j + 1) + "</a> ";
}
$("#tr" + i).append("<td>" + urlList[i][3] + "回(" + tmpText + ")</td>");
$("#tr" + i).append("<td>" + urlList[i][1] + "</td>");
$("#tr" + i).append("<td>" + urlList[i][2] + "</td>");
}
$("#result").css({"margin":"auto"});
for( var i = 0; i < urlList.length; i++ ){
if(i % 2 == 0){
$("#tr" + i).css({"background":colorGray});
}else{
$("#tr" + i).css({"background":"white"});
}
if( urlList[i][1].indexOf("HTMLにのみ存在") > -1 ){
$("#tr" + i).css({"background":colorRed});
for( var j = 0; j < urlList[i][3]; j++ ){
$("#jump" + i + "_" + j ).parent().addClass("errorComment");
$("#jump" + i + "_" + j ).parent().css({"background":colorYellow
,"color":"black"
,"border-color":"black"
});
$("#jump" + i + "_" + j ).html($("#jump" + i + "_" + j ).text() + ":HTMLにのみ存在!");
}
}
if( urlList[i][3]==0 ){
$("#tr" + i).css({"background":colorYellow});
}
}
$("#result tr").css({"border-width": "1px"
,"border-style": "solid"
,"border-color": "black"
,"color":"black"
,"font-size": "11px"
});
}
最後に
今回、HTMLファイルのリンクチェックツールの作成にJavaScriptによるブックマークレットという技術が使えるということを紹介しました。
JavaScriptによるブックマークレットという技術は、リンクチェックに限らずHTMLファイルに関するツールを作成するのに使うことができると思います。
もし、業務の中でHTMLファイルに関するツールを作成する機会があった際には、JavaScriptによるブックマークレットのことを思い出していただけたらと思います。









ディスカッション
コメント一覧
非常に便利に「使えそうだ」と思い、是非参考にさせていただきたいのですが、1点だけ苦言です。
ブックマークレット自体の事例や登録方法について、ご紹介は充分なのですが、せっかく実装したブックマークレット自体の使い方がいまいち不明瞭です。
特に「仕様書」についての記述が「タブ区切りである」事以外無くタブ区切りで何を入力させたいのか不明です。
B列は自明ですが、A列は動作に必須のカラムでしょうか?(例えばリンクのnameに同名が入っている必要がある、など)
ユーザビリティを考えた際にどのように紹介されるか、ご一考いただければ幸いです。
コメントありがとうございます。
ご指摘はもっともで、A列(キャプション)の説明が少し不十分でした。
元々、仕様書とHTMLファイルがあり手動でチェックを行っていたものを、自動化したという経緯があったので、そもそもの仕様書がなぜ必要なのかという説明が抜けてしまっていました。
こちらは、今後、ブログ記事を更新していく際には気をつけていこうと思います。
さて、A列が必須のカラムかどうかについてですが、必須ではありませんので空文字列でも動作はします。
URLがわかりやすいものであれば、A列は空文字列でも問題ないかと思います。
ただ、ECサイトの商品ページなどプログラムが作成したURLは、URLだけを読んでもそれがどのようなコンテンツに対応しているのかわからない可能性が高いです。
また、HTMLファイルを作成している段階ではリンク先URLが存在しない場合もあり、そのような場合にはリンクを開いて内容確認をするという事もできません。
今回のブックマークレットを利用する案件では、そのようなケースが多かったため、A列のカラムにリンクURLの説明を記載していたという経緯がありました。