userscriptで既存htmlに干渉

htmlに干渉したい

userscriptで既存ページをごにょごにょしたい今日この頃。
元画像を一括で出したい、URLの条件分岐したい、バックグラウンドタブを自動で開きたい、別ボタンを挿入して操作をショートカットしたい、などなど用途は様々。色々とコンパクトにまとめていきます。
※使用ブラウザはchromeを想定しています。目的が既存ページの操作という趣味的なモノなのでクロスブラウザを考慮していません。……と、言うのもアレなんで。一段落したらIE8以上で対応策を追っていきたいと思います。

※現在進行形で少しづつ追記、訂正しています。

先にまとめ

勉強がてら意固地になってjavascriptをまとめましたが、相対的にjqueryの便利さを痛感しました。jqueryはバージョンで対応ブラウザを把握できます。言わずもがな、記述は楽ですし、簡単に操作を広げることができます。

指定

id getElementById(要素)
class getElementsByClassName(要素)
tag(動) getElementsByTagName(要素)
selector querySelector(要素)
tag querySelectorAll(要素)

複数取得できるものは[n]でn番目を指定。(nは0番目~)
jqueryならep(n)を利用。

親要素(一段) parentNode
子要素 childNodes
子要素(tag) children
最初の子要素 firstChild
最後の子要素 lastChild
最初の子要素(tag) firstElementChild
最後の子要素(tag) lastElementChild

要素

生成 createElement(要素)
削除 親要素.removeChild(要素)
入れ替え 親要素.replaceChild(書く,消す)

jqueryは属性指定の要素を一発で生成できます。

bodyの先頭に挿入
bodyText = function(text, id) {
$("</div>", {
id: id,
text: text
}).prependTo('body');
};
bodyText("test", "id");

テキスト

取得 innerHTML
取得(外タグ含) outerHTML
取得(タグ抜) innerText
上書き(タグ判定) innerHTML=
上書き textContent=
上書き createTextNode(text)

挿入

先頭 親要素.insertBefore(要素, 親要素)
末尾 appendChild(要素)
nextSiblingで末尾追加
var create = createElement("div");
X.parentNode.insertBefore(create, X.nextSibling);

挿入系はjqueryに豊富に揃えられています。
jqueryはタグの挿入のほか、移動も容易です。
複製タグclone()と組み合わせることもしばしば。

以下のサイトがよくまとめられています。

参考リンク
目的の場所にさくっと要素を追加する
http://www.detelu.com/blog/2011/11/jquery-manipulation/

属性

取得 getAttribute(attr)
追加 setAttribute(attr, value)
削除 removeAttribute(attr)
id追加 id=
class追加 className+=
css追加 style.プロパティ=値

javascriptではプロパティをキャメルで指定。
(css:margin-top, js:marginTop)
jqueryが使えるならcss(“プロパティ”, “値”)で、styleの記述方法が可能。
jqueryはオブジェクトの要領で一括指定もできます。

css一括(jquery)
style = {
"margin-top": "20px",
"padding": "5px",
"width": "100px",
"border": "1px solid #ccc",
"text-align": "center",
}
#("box").css(style);

推移

推移 window.location.href=
新しいタブ window.open(path, ‘_blank’)
更新 window.location.reload()
aタグ推移
var href = document.getElementById("link").getAttribute("href");
window.location.href = href;

自動クリック

参考

参考リンク
JavaScriptでjQueryを使わずにイベントをtriggerする  
http://qiita.com/ryounagaoka/items/a48d3a4c4faf78a99ae5

コード

自動クリック
function triggerClick(el){
var evt = document.createEvent("MouseEvents");
evt.initEvent("click", true, true);
el.dispatchEvent(evt);
}

var btn = document.getElementById("btn");
triggerClick(btn);

chromeで動作確認。
jqueryが使えるならtrigger(“click”)で一発。

createEvent()のイベントタイプはこちら
initEvent()の引数はこちら

推移(バックグラウンド)

参考

参考リンク
Open a new tab in the background?  
http://stackoverflow.com/questions/10812628/open-a-new-tab-in-the-background

コード

バックグラウンドタブで開く
function tabBackground(el){
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, true, false, false, false, 0, null);
el.dispatchEvent(evt);
}

var btn = document.getElementById("btn");
tabBackground(btn);

chromeで動作確認。
ポップアップの許可が必要なので注意。

attrでurl取得してwindow.open(path, ‘_blank’)した方が楽かも。

タブを閉じる

参考

参考URL stackoverflow
http://stackoverflow.com/questions/19761241/window-close-and-self-close-do-not-close-the-window-in-chrome

コード

タブを閉じる
open(location, '_self').close();

chromeのみで動作確認。

条件分岐

ページ内容(URL)による分岐です。
wpで条件分岐と名付けられているので、自分もそのように言っています。

条件分岐
var url = location.href;

//match
if(url.match(/xxx/)){
}

//indexOf
if(~url.indexOf('xxx')){
}

正規表現を読み込まない分、indexOfの方が速いという話もあるが、どっちも変わらないという話もある。そもそもindexOfが遅いとも言われている。時間ができたら調べたいけど、問題なく動かせているし調べ物としての優先順位が中々上がってこない。
indexOfは不一致で-1を返す。ビット演算子(~)で-1を0にしてifに対応。

ie8以前にindexOfが対応していない。
対応は以下のコードで実装。

indexOf対応策リンク
//IEでのindexof使用する際の注意 
//http://kaede.jp/2012/12/04000410.html

if(!Array.prototype.indexOf){
Array.prototype.indexOf = function(target,index){
//indexが数値かどうかの判断
if(isNaN(index)){
index = 0;
}
for(var i = index; i < target.length; i++){
if(this[i] === target){
return i;
}
}
return -1;
}
}

読み込むタイミング

js読み込みのタイミング
//DOMの読み込み完了後(html読み込み後)
//IE9以上
document.addEventListener('DOMContentLoaded', function(){}, false);

//DOMの読み込み完了後(html読み込み後)
//IE8以下
document.attachEvent('onreadystatechange', function(){});

//すべての関連ファイルが読み込まれた後(一番最後)
window.onload = function(){}
jquery読み込みのタイミング
//DOMの読み込み完了後(html読み込み後)
$(document).ready(function(){});
$().ready(function(){});
$(function(){});

//すべての関連ファイルが読み込まれた後(一番最後)
$(window).on("load",function(){});

jqueryは楽ですね、はい。

存在チェック

indexOfのIE8対応は上に記載

存在チェック
var box = document.getElementById("box");

if(box != null){}

//jquery
if($("#box")[0]){}

idを持つ要素の存在チェックはisnt null。
jqueryなら[0]のチェックで記述を統一できます。

子要素の存在

子要素の有無
var box = document.getElementById("box");

//boxが特定のタグを子に持つ(以下はdiv)
if(box.getElementsByTagName("div")[0]){}

//boxが特定のクラス名を有した要素を子に持つ(以下はname)
if(box.getElementsByClassName("name")[0]){}

//jquery
if($("#box").children("name")[0]){}

孫要素の存在

子要素の有無
var box = document.getElementById("box");
var child = box.children;

//第一子が特定のタグを子に持つ(以下はdiv)
if(child[0].getElementsByTagName("div")[0]){}

//第二子が特定のクラス名を有した要素を孫に持つ(以下はname)
if(child[1].getElementsByClassName("name")[0]){}

//jquery
if(box.find("name").length > 0){}

孫の確認は子供が何番目を指定してやって下さい。

セレクタの存在

セレクタの有無
if(document.querySelectorAll("#box input[type=button]")[0]){}

jqueryのように指定できます。

クラス名の所持

クラス名の有無
var box = document.getElementById("box");

if((box.className).replace(/[\n\t]/g, " ").indexOf("クラス名") > -1){}

jqueryならhasClass()で一発。

属性の存在

クラス名の有無
var box = document.getElementById("box");

if(box.hasAttribute("属性")){}

オブジェクトのプロパティの存在

プロパティの有無
var obj = {};

if("プロパティ名" in obj){}

一致

indexOfのIE8対応は上に記載

特定の文字列の一致

文字列の一致
//例:リスト
var els = document.getElementById("box").getElementsByTagName("li");

for (var i = 0; i < els.length; i++) {
if (els[i].innerHTML.indexOf("文字列") !== -1) {
console.log("存在");
break;
}
}

特定の属性値の一致

属性値の一致
var box = document.getElementById("box");

if(box.getAttribute("data-x").indexOf("30") !== -1){}

イベント

addEventListener
var box = document.getElementById("box");
box.addEventListener("click", function(){}, false);
box.addEventListener("resize", function(){}, false);
box.addEventListener("load", function(){}, false);

addEventListenerはIE8以下では使えない。
jqueryだと.on()、これはIEにも対応。

『addEventListener polyfill』で対応策が色々と検出されますが、私には難しいので後日に回します。