Google Chromeの拡張機能にはいくつか種類がありますが、その中からbrowser actionという種類の拡張機能を作成してみます。
拡張機能の作成
以前に作成した以下のブックマークレットのJavaScriptを流用して試してみます。
マークアップ後の画像やmetaをチェックするブックマークレットを作ってみた | cly7796.net
拡張機能の作成方法ですが、まずはmanifest.jsonというファイルを作成して、拡張機能の内容を定義します。
今回は以下のようになっています。
manifest.json
{
// 必須
"manifest_version": 2,
"name": "マークアップチェッカー",
"version": "1.0",
// 拡張機能ページ(chrome://extensions/)などでの設定
"description": "拡張機能作成のサンプルです。", // 拡張機能の説明
"icons": { // 拡張機能のアイコンの指定
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
},
// browser actionの設定
"browser_action": {
"default_icon": { // browser actionで使用するアイコンの指定
"19": "icon19.png",
"38": "icon38.png"
},
"default_title": "マークアップチェッカー" // browser actionのアイコンにオンマウスした時のツールチップ
},
// backgroundで実行しておくファイル
"background": {
"scripts": ["background.js"]
},
// 権限の設定
"permissions": [
"tabs",
"http://*/*",
"https://*/*"
]
}
アイコンは今回サイズの違う5種類を用意しています。
次に、backgroundで動作させるファイルをJavaScriptで記述します。
background.js
// 拡張機能がクリックされたとき
chrome.browserAction.onClicked.addListener(function() {
// script.jsを読み込む
chrome.tabs.executeScript(null, {
"file": "script.js"
});
});
最後に、拡張機能がクリックされたときに読み込まれるファイルを作成します。
script.js
(function() {
/* ------------------------------------------------------------
ベース部分の設定
------------------------------------------------------------ */
/* ----------------------------------------
既にツールを開いていないか確認
---------------------------------------- */
if(document.getElementById('markupCheck') != null) {
document.body.removeChild(document.getElementById('markupCheck'));
}
/* ----------------------------------------
ベース部分の挿入
---------------------------------------- */
var bg = document.createElement('div');
var bar = document.createElement('div');
var wht = document.createElement('span');
var blk = document.createElement('span');
var cls = document.createElement('span');
var bgStyle = 'position:absolute;top:0;left:0;z-index:9999;width:100%;line-height:1.2;padding-top:50px;color:#000000;font-size:14px;font-family:MS PGothic;text-align:left;background:#ffffff;';
barStyle = 'position:fixed;top:0;left:0;width:100%;height:50px;background:#34495e;';
whtStyle = 'position:absolute;bottom:5px;left:10px;width:20px;height:20px;background:#ffffff;cursor:pointer;';
blkStyle = 'position:absolute;bottom:5px;left:40px;width:20px;height:20px;background:#000000;cursor:pointer;';
clsStyle = 'position:absolute;top:15px;right:10px;width:20px;height:20px;color:#000000;font-size:18px;text-align:center;background:#999999;cursor:pointer;';
bg.id = 'markupCheck';
bg.style.cssText = bgStyle;
bar.style.cssText = barStyle;
blk.style.cssText = blkStyle;
wht.style.cssText = whtStyle;
cls.style.cssText = clsStyle;
cls.innerHTML = '×';
bar.innerHTML = '<span style="position:absolute;top:5px;left:10px;color:#ffffff;font-size:12px;">背景色変更</span>';
document.body.appendChild(bg).appendChild(bar).appendChild(cls);
bar.appendChild(blk);
bar.appendChild(wht);
/* ------------------------------------------------------------
イベント
------------------------------------------------------------ */
/* ----------------------------------------
ページトップへ戻す
---------------------------------------- */
window.scrollTo(0,0);
/* ----------------------------------------
閉じるボタン
---------------------------------------- */
cls.onclick = function() {
document.body.removeChild(bg);
};
/* ----------------------------------------
背景色変更(黒)
---------------------------------------- */
blk.onclick = function() {
bg.style.backgroundColor = '#000000';
bg.style.color = '#ffffff';
};
/* ----------------------------------------
背景色変更(白)
---------------------------------------- */
wht.onclick = function() {
bg.style.backgroundColor = '#ffffff';
bg.style.color = '#000000';
};
/* ------------------------------------------------------------
イメージリスト
------------------------------------------------------------ */
var imgAlt = new Array(), imgSrc = new Array(), imgAW = new Array(), imgNW = new Array(), imgAH = new Array(), imgNH = new Array(),
images = document.getElementsByTagName('img');
/* ----------------------------------------
画像のデータ取得
---------------------------------------- */
for (var i = 0; i < images.length; i++) {
imgAlt[i] = images[i].getAttribute('alt');
imgSrc[i] = images[i].src;
imgAW[i] = images[i].getAttribute('width');
imgNW[i] = images[i].naturalWidth;
imgAH[i] = images[i].getAttribute('height');
imgNH[i] = images[i].naturalHeight;
}
/* ----------------------------------------
画像の挿入
---------------------------------------- */
/* -------------------------
画像挿入の設定
------------------------- */
var imageIns = '';
var ttlStyle = 'margin:0 0 10px;font-size:28px;font-weight:bold;',
imgBoxStyle = 'overflow:hidden;margin-bottom:15px;border-bottom:#999999 1px dotted;padding-bottom:15px;',
srcStyle = 'margin-bottom:5px;padding-right:40%;',
imgWrStyle = 'margin-bottom:10px;',
imgStyle = 'max-width:60%;max-height:250px;',
altStyle = 'max-width:60%;margin-bottom:10px;',
dataStyle = 'float:right;width:40%;',
typStyle = 'font-weight:bold;',
itemStyle = 'display:inline-block;width:4em;margin-right:5px;padding:2px 0;font-size:16px;font-weight:bold;text-align:center;background:#999999;',
sizeStyle = 'display:inline-block;padding:3px 2px;',
dataSpcStyle = 'display:inline-block;margin-right:10px;margin-bottom:10px;',
errStyle = 'color:#ffffff;background:#ff0000;',
attStyle = 'color:#000000;background:#ffff00;';
/* image list title */
imageIns += '<p style="' + ttlStyle + '">■画像リスト</p>';
for (var i = 0; i < images.length; i++) {
/* imgBox start */
imageIns += '<div style="' + imgBoxStyle + '">';
/* ファイルパス */
imageIns += '<div style="' + srcStyle + '">';
imageIns += imgSrc[i];
imageIns += '</div>';
/* 画像 */
imageIns += '<div style="' + imgWrStyle + '">';
imageIns += '<img src="' + imgSrc[i] + '" style="' + imgStyle + '" />';
imageIns += '</div>';
/* alt */
imageIns += '<div class="alt" style="' + altStyle + '">';
/* alt属性が無いとき */
if(imgAlt[i] == null) {
imageIns += '<span style="' + errStyle + '">alt がありません。</span>';
/* alt属性が空のとき */
} else if(imgAlt[i] == '') {
imageIns += '<span style="' + attStyle + '">alt が空です。</span>';
/* 上記以外 */
} else {
imageIns += '<span>' + imgAlt[i] + '</span>';
}
imageIns += '</div>';
/* width */
imageIns += '<div class="sizew" style="' + dataSpcStyle + '">';
imageIns += '<div style="' + itemStyle + '">width</div>';
/* width属性が無いとき */
if(imgAW[i] == null) {
imageIns += '<span style="' + sizeStyle + attStyle + '">width がありません。</span>';
/* width属性が空のとき */
} else if(imgAW[i] == '') {
imageIns += '<span style="' + sizeStyle + attStyle + '">width が空です。</span>';
/* width属性が%指定のとき */
} else if(imgAW[i].indexOf('%') != -1) {
imageIns += '<span style="' + sizeStyle + '">' + imgAW[i] + '</span>';
/* width属性が実際の値と異なるとき */
} else if(imgAW[i] != imgNW[i]) {
/* 実際のサイズの半分くらいのとき */
if(Math.abs(imgAW[i] - imgNW[i] / 2) <= 0.5) {
imageIns += '<span style="' + sizeStyle + '">Retina対応っぽいです。</span>';
imageIns += '<span style="' + sizeStyle + typStyle + '">実際:</span>' + '<span>' + imgNW[i] + 'px</span> ';
imageIns += '<span style="' + sizeStyle + typStyle + '">半分:</span>' + '<span>' + imgNW[i] / 2 + 'px</span> ';
imageIns += '<span style="' + sizeStyle + typStyle + '">指定:</span>' + '<span>' + imgAW[i] + 'px</span>';
/* 実際のサイズの半分ではないとき */
} else {
imageIns += '<span style="' + sizeStyle + attStyle + '">実サイズと一致していません。</span>';
imageIns += '<span style="' + sizeStyle + typStyle + '">実際:</span>' + '<span>' + imgNW[i] + 'px</span> ';
imageIns += '<span style="' + sizeStyle + typStyle + '">指定:</span>' + '<span>' + imgAW[i] + 'px</span>';
}
/* 上記以外 */
} else {
imageIns += '<span style="' + sizeStyle + '">' + imgAW[i] + 'px</span>';
}
imageIns += '</div>';
/* height */
imageIns += '<div class="sizeh" style="' + dataSpcStyle + '">';
imageIns += '<div style="' + itemStyle + '">height</div>';
/* height属性が無いとき */
if(imgAH[i] == null) {
imageIns += '<span style="' + sizeStyle + attStyle + '">height がありません。</span>';
/* height属性が空のとき */
} else if(imgAH[i] == '') {
imageIns += '<span style="' + sizeStyle + attStyle + '">height が空です。</span>';
/* height属性が%指定のとき */
} else if(imgAH[i].indexOf('%') != -1) {
imageIns += '<span style="' + sizeStyle + '">' + imgAH[i] + '</span>';
/* height属性が実際の値と異なるとき */
} else if(imgAH[i] != imgNH[i]) {
/* 実際のサイズの半分くらいのとき */
if(Math.abs(imgAH[i] - imgNH[i] / 2) <= 0.5) {
imageIns += '<span style="' + sizeStyle + '">Retina対応っぽいです。</span>';
imageIns += '<span style="' + sizeStyle + typStyle + '">実際:</span>' + '<span>' + imgNH[i] + 'px</span> ';
imageIns += '<span style="' + sizeStyle + typStyle + '">半分:</span>' + '<span>' + imgNH[i] / 2 + 'px</span> ';
imageIns += '<span style="' + sizeStyle + typStyle + '">指定:</span>' + '<span>' + imgAH[i] + 'px</span>';
/* 実際のサイズの半分ではないとき */
} else {
imageIns += '<span style="' + sizeStyle + attStyle + '">実サイズと一致していません。</span>';
imageIns += '<span style="' + sizeStyle + typStyle + '">実際:</span>' + '<span>' + imgNH[i] + 'px</span> ';
imageIns += '<span style="' + sizeStyle + typStyle + '">指定:</span>' + '<span>' + imgAH[i] + 'px</span>';
}
/* 上記以外 */
} else {
imageIns += '<span style="' + sizeStyle + '">' + imgAH[i] + 'px</span>';
}
imageIns += '</div>';
/* imgBox-end */
imageIns += '</div>';
};
/* ------------------------------------------------------------
meta・title系のリスト
------------------------------------------------------------ */
/* ----------------------------------------
変数とかの設定
---------------------------------------- */
var metaData = document.getElementsByTagName('meta'),
metaTitle = document.getElementsByTagName('title'),
linkData = document.getElementsByTagName('link'),
keywords, description, canonical, alternate,
ogArray = [], twArray = [],
ogFlag = false, twFlag = false;
/* -------------------------
metaデータ取得
------------------------- */
for(var i = 0; i < metaData.length; i++) {
switch(metaData[i].getAttribute('name')) {
/* meta keywordsのとき */
case 'keywords':
keywords = metaData[i].getAttribute('content');
break;
/* meta descriptionのとき */
case 'description':
description = metaData[i].getAttribute('content');
break;
default:
break;
}
if(metaData[i].getAttribute('property') != null) {
/* ogp情報のとき */
if(metaData[i].getAttribute('property').match(/og:/)) {
var ogFlag = true;
ogArray.push([metaData[i].getAttribute('property'), metaData[i].getAttribute('content')]);
}
}
if(metaData[i].getAttribute('name') != null) {
/* twitter情報のとき */
if(metaData[i].getAttribute('name').match(/twitter:/)) {
var twFlag = true;
twArray.push([metaData[i].getAttribute('name'), metaData[i].getAttribute('content')]);
}
}
};
/* -------------------------
linkデータ取得
------------------------- */
for(var i = 0; i < linkData.length; i++) {
switch(linkData[i].getAttribute('rel')) {
/* canonicalのとき */
case 'canonical':
canonical = linkData[i].getAttribute('href');
break;
/* alternateのとき */
case 'alternate':
alternate = linkData[i].getAttribute('href');
break;
default:
break;
}
};
/* ----------------------------------------
metaの挿入
---------------------------------------- */
/* -------------------------
meta挿入の設定
------------------------- */
var metaIns = '';
var dlStyle = 'margin-bottom:10px;',
dtStyle = 'display:inline-block;width:10em;margin-right:10px;padding:2px;font-size:16px;font-weight:bold;background:#999999;',
ddStyle = 'padding:2px;',
linkStyle = 'color:#0000ff;';
/* meta list title */
metaIns += '<p style="' + ttlStyle + '">■meta情報</p>';
metaIns += '<div style="margin-bottom:30px;">';
/* title */
metaIns += '<div style="' + dlStyle + '"><span style="' + dtStyle + '">title</span>';
/* titleが無いとき */
if(!metaTitle.length) {
metaIns += '<span style="' + errStyle + '">titleタグがありません。</span>';
/* titleが空のとき */
} else if(!metaTitle[0].childNodes.length) {
metaIns += '<span style="' + errStyle + '">titleタグが空です。</span>';
/* 上記以外 */
} else {
metaIns += '<span style="' + ddStyle + '">' + metaTitle[0].childNodes[0].nodeValue + '</span>';
}
metaIns += '</div>';
/* keywords */
metaIns += '<div style="' + dlStyle + '"><span style="' + dtStyle + '">keywords</span>';
/* keywordsが無いとき */
if(keywords == undefined) {
metaIns += '<span style="' + attStyle + '">keywordsタグがありません。</span>';
/* keywordsが空のとき */
} else if(keywords == '') {
metaIns += '<span style="' + attStyle + '">keywordsタグが空です。</span>';
/* 上記以外 */
} else {
metaIns += '<span style="' + ddStyle + '">' + keywords + '</span>';
}
metaIns += '</div>';
/* description */
metaIns += '<div style="' + dlStyle + '"><span style="' + dtStyle + '">description</span>';
/* descriptionが無いとき */
if(description == undefined) {
metaIns += '<span style="' + attStyle + '">descriptionタグがありません。</span>';
/* descriptionが空のとき */
} else if(description == '') {
metaIns += '<span style="' + attStyle + '">descriptionタグが空です。</span>';
/* 上記以外 */
} else {
metaIns += '<span style="' + ddStyle + '">' + description + '</span>';
}
metaIns += '</div>';
/* ogp情報が何かあるとき */
if(ogFlag) {
for (var i = 0; i < ogArray.length; i++) {
metaIns += '<div style="' + dlStyle + '"><span style="' + dtStyle + '">' + ogArray[i][0] + '</span>';
/* 値が空のとき */
if(ogArray[i][1] == '') {
metaIns += '<span style="' + attStyle + '">' + ogArray[i][0] + 'が空です。</span>';
/* 値が空でないとき */
} else {
/* og:urlのとき */
if(ogArray[i][0] == 'og:url') {
metaIns += '<span style="' + ddStyle + '"><a href="' + ogArray[i][1] + '" target="_blank" style="' + linkStyle + '">' + ogArray[i][1] + '</a></span>';
/* og:imageのとき */
} else if(ogArray[i][0] == 'og:image') {
metaIns += '<span style="' + ddStyle + '">';
metaIns += '<a href="' + ogArray[i][1] + '" target="_blank" style="' + linkStyle + '">' + ogArray[i][1] + '</a><br />';
metaIns += '<img src="' + ogArray[i][1] + '" style="max-width:100%;max-height:250px" />';
metaIns += '</span>';
/* 上記以外 */
} else {
metaIns += '<span style="' + ddStyle + '">' + ogArray[i][1] + '</span>';
}
}
metaIns += '</div>';
};
}
/* twitter情報が何かあるとき */
if(twFlag) {
for (var i = 0; i < twArray.length; i++) {
metaIns += '<div style="' + dlStyle + '"><span style="' + dtStyle + '">' + twArray[i][0] + '</span>';
/* 値が空のとき */
if(twArray[i][1] == '') {
metaIns += '<span style="' + attStyle + '">' + twArray[i][0] + 'が空です。</span>';
/* 値が空でないとき */
} else {
/* twitter:siteのとき */
if(twArray[i][0] == 'twitter:site') {
metaIns += '<span style="' + ddStyle + '">';
metaIns += '<a href="https://twitter.com/' + twArray[i][1].replace('@', '') + '" target="_blank" style="' + linkStyle + '">';
metaIns += twArray[i][1];
metaIns += '</a>';
metaIns += '</span>';
/* twitter:imageのとき */
} else if(twArray[i][0] == 'twitter:image') {
metaIns += '<span style="' + ddStyle + '">';
metaIns += '<a href="' + twArray[i][1] + '" target="_blank" style="' + linkStyle + '">';
metaIns += twArray[i][1];
metaIns += '</a><br />';
metaIns += '<img src="' + twArray[i][1] + '" style="max-width:100%;max-height:250px" />';
metaIns += '</span>';
/* 上記以外 */
} else {
metaIns += '<span style="' + ddStyle + '">' + twArray[i][1] + '</span>';
}
}
metaIns += '</div>';
};
}
/* canonicalが何かあるとき */
if(canonical != undefined) {
metaIns += '<div style="' + dlStyle + '"><span style="' + dtStyle + '">canonical</span>';
/* canonicalが空のとき */
if(canonical == '') {
metaIns += '<span style="' + attStyle + '">canonicalが空です。</span>';
/* canonicalが空でないとき */
} else {
metaIns += '<span style="' + ddStyle + '"><a href="' + canonical + '" target="_blank" style="' + linkStyle + '">' + canonical + '</a></span>';
}
metaIns += '</div>';
}
/* alternateが何かあるとき */
if(alternate != undefined) {
metaIns += '<div style="' + dlStyle + '"><span style="' + dtStyle + '">alternate</span>';
/* alternateが空のとき */
if(alternate == '') {
metaIns += '<span style="' + attStyle + '">alternateが空です。</span>';
/* alternateが空でないとき */
} else {
metaIns += '<span style="' + ddStyle + '"><a href="' + alternate + '" target="_blank" style="' + linkStyle + '">' + alternate + '</a></span>';
}
metaIns += '</div>';
}
metaIns += '</div>';
/* ------------------------------------------------------------
ベース部分へ挿入
------------------------------------------------------------ */
/* ----------------------------------------
meta Insert
---------------------------------------- */
var metaList = document.createElement('div');
metaList.id = 'metaList';
metaList.innerHTML = metaIns;
bg.appendChild(metaList);
/* ----------------------------------------
img Insert
---------------------------------------- */
var imgList = document.createElement('div');
imgList.id = 'imglist';
imgList.innerHTML = imageIns;
bg.appendChild(imgList);
})();
これで拡張機能の作成は完了です。
最終的に、以下のようなファイル構成になりました。
- manifest.json
- background.js
- script.js
- icon16.png
- icon19.png
- icon38.png
- icon48.png
- icon128.png
拡張機能の読み込み
拡張機能をchromeで読み込んでみます。
その他のツール > 拡張機能を選択します。

右上のデベロッパーモードにチェックを入れます。

パッケージ化されていない拡張機能を読み込むをクリックして、先ほど拡張機能を作成したディレクトリを選択します。

作成した拡張機能が表示されました。
ローカルでファイルの修正作業を行った場合、リロードをクリックすると反映されます。

実際に拡張機能の動作を試してみます。
調べたいページを開いて、右上の拡張機能アイコンをクリックします。

そのページのmeta情報などが表示できるのが確認できました。

拡張機能のパッケージ化
先ほどはデベロッパーモードで拡張機能を読み込みましたが、パッケージ化して追加してみます。
拡張機能のパッケージ化をクリックします。

拡張機能のルートディレクトリにを指定して、拡張機能のパッケージ化をクリックします。

拡張機能と鍵ファイルの2つが作成されました。
crxファイルは拡張機能を追加する時に、鍵ファイルは拡張機能を更新する時に使用します。

拡張機能を追加する場合、chrome://extensions/に crxファイルをドラッグアンドドロップします。
以下のようなアラートが表示されるので、問題なければ拡張機能を追加をクリックします。

これで拡張機能が追加されました。
【参考サイト】
- Manifest File Format – Google Chrome
- chrome.browserAction – Google Chrome
- JavaScript APIs – Google Chrome
- chrome.tabs – Google Chrome
コメントが承認されるまで時間がかかります。