非jQuery環境ではどう書んだっけ?チートシート

※追記
コメントでこの記事よりも断然質の良い記事を教えてもらいました。
@kabochaさんありがとうございます。

> You Don't Need jQuery
> https://github.com/oneuijs/You-Dont-Need-jQuery

追記の追記。
日本語訳のお手伝いをさせていただきました。

> github
> https://github.com/oneuijs/You-Dont-Need-jQuery/blob/master/README-ja.md
>
> [Qiita]もうjQueryは必要ない
> http://qiita.com/tatesuke/items/b9548dd484b01b139b74

jQueryが使える環境でコーディングした後、jQueryが使えない環境でコーディングしようとすると、「どう書くんだっけ？」となることが多いです。ということで分かる範囲でチートシートを作りました。


当然ですが、サンプルコードは「似たような動作をする」「代替になりうる」という意味なので厳密に同じではありません。

また、JavaScriptやECMAScriptの仕様を意識していないため、古い書き方や新しい書き方など混在していると思います。


## Selectors
### エレメントを選択

伝統的なやり方ならば

~~~js
// idで取得
var elem = document.getElementById("id");

// クラス名で取得
elem = document.getElementsByClassName("className");

// タグ名で取得
elem = document.getElementsByTagName("div");
~~~

最近のブラウザならquerySelectorAll/querySelectorのほうが便利。jqueryのセレクタを流用できる。

~~~js
// セレクタに合致するエレメントを取得
var elem = document.querySelectorAll("#id");

// セレクタに合致する最初のエレメントを取得
elem = document.querySelector(".query");
~~~

ただし、jQueryのオブジェクトじゃないのでeach関数は使えない。自作するか、素直にfor文で回す。

## Attributes
### attr/removeAttr
~~~js
// 属性を設定
element.setAttribute("name", "value");

// 属性を取得
var attr = element.getAttribute("name");

// 属性を削除
element.removeAttribute();

// 属性が存在するか
var hasAttr = element.hasAttribute("name");
~~~

### addClass/removeClass/toggleClass
~~~js
// クラスを追加
element.classList.add("className");

// クラスを切り替え
element.classList.toggle("className");

// クラスを削除
element.classList.remove("className");

// クラスが存在するか
var contains = element.classList.contains("className");
~~~

一応``className``属性で文字列を取得することも可能

~~~js
var classNames = element.className;
~~~


### html
~~~js
// htmlを取得
var html = element.innerHTML;

// htmlを設定
element.innerHTML = html;
~~~

### text
~~~js
// テキストを取得
var text = element.innerText;

// テキストを設定
element.innerText = text;
~~~

firefoxでは``innerText``が使えないので``textContent``を使う。

~~~js
var text = element.textContent;
~~~

当然、ブラウザによって処理を振り分けるのは面倒なので次のようにするとよい。

引用元：『TM Life』　Firefox で innerText を使えるようにする方法
http://tmlife.net/programming/javascript/firefox-can-use-innertext.html

~~~js
(function(){
    var temp = document.createElement("div");
    if (temp.innerText == undefined) {
        Object.defineProperty(HTMLElement.prototype, "innerText", {
            get: function()  { return this.textContent },
            set: function(v) { this.textContent = v; }
        });
    }
})();
~~~


### val

~~~js
// valueを取得
var val = element.value;

// valueを設定
element.value = val;
~~~

## Traversing
### hasClass
~~~js
var hasClass = element.classList.contains(className);
~~~

### children

~~~js
var chldren = element.children;
~~~

こちらもjQueryオブジェクトではないのでeach関数は使えない。

### closest

~~~js
// 親エレメントを取得
var closet = element.parentNode;
~~~

セレクタがある場合はゴリゴリ書くしかない。
たとえば、タグ名を指定したければ``while``文で遡る。

~~~js
var parent = element.parentNode;
while (parent.tagName != "BODY") {
	parent = parent.parentNode;
}
~~~

### find
エレメント自体にも``querySelectorAll``/``querySelector``があるので、それで代用可能。

~~~js
// findの代用
var elements = element.querySelectorAll(query);
~~~

### next

~~~js
// 次の要素を得る
var next = element.nextElementSibling;
~~~

### nextAll

~~~js
var nexts = [];
var current = element.nextElementSibling;
while (current != null) {
	nexts.push(current);
	current = current.nextElementSibling;
}
~~~

### parent

~~~js
// 親エレメントを取得
var parent = element.parentNode;
~~~

### parents
``element.parentNode``と``while``文、``if``文を組み合わせてゴリゴリ書く。


### prev

~~~
// 前の要素を得る
var prev = element.previousElementSibling;
~~~

### prevAll

~~~js
var prevs = [];
var current= element.parentNode.firstElementChild;
while (current != element) {
	prevs.push(current);
	current = current.nextElementSibling;
}
~~~

### siblings
~~~js
var siblings = [];
var children = element.parentNode.children;
for (var i = 0; i < children.length; i++) {
	if (children[i] != element) {
		siblings.push(children[i]);
	}
}
~~~




## Manipulation
### エレメント作成
エレメント作成の基本は``document.createElement``。

~~~js
var newElement = document.createElement("div");
~~~

``createElement``ではjQueryのよう文字列からエレメントを簡単に作成することはできない。
その場合、以下のように、ダミーのエレメントを使うことで実現可能。

~~~js
// ダミーのdiv作成
var dummy = document.createElement("div");
// ダミーのinnerHTMLを書き換える
dummy.innerHTML = "<div>hoge</div>";
// 作成したエレメントを取り出す。
var newElement = dummy.childNodes[0];
delete dummy;
~~~

### append

~~~js
parent.appendChild(element);
~~~

### prepend

~~~js
parent.insertBefore(element, parent.childNodes[0]);
~~~

### after

~~~js
var parent = element.parentNode;
var prev = element.nextSibling;
parent.insertBefore(insertElement, prev);
~~~

### before

~~~js
var parent = element.parentNode;
parent.insertBefore(insertElement, element);
~~~

### insertAfter

~~~js
$(A).insertAfter(B)
~~~
↓

~~~js
var parent = B.parentNode;
var prev = B.nextSibling;
parent.insertBefore(A, prev);
~~~


### insertBefore


~~~js
$(A).insertBefore(B)
~~~
↓

~~~js
var parent = B.parentNode;
parent.insertBefore(A, B);
~~~

### wrap

~~~js
$(A).wrap(B);
~~~
↓

~~~js
var next = A.nextSibling;
var parent = A.parentNode;
parent.removeChild(A);
parent.insertBefore(B, next);
B.appendChild(A);
~~~

### empty
```
element.innerHTML = "";
```

### remove

```js
element.parentNode.removeChild(element);
```

### clone

```js
element.cloneNode(true);
```

## CSS
### css
``element.style``配下のプロパティをいじる。

```js
element.style.border = "1px solid gray";
```

どんなプロパティがあるかはその都度調べる。たとえば↓とか。
http://javascriptist.net/docs/samples_element_style.html

### offset/position
簡単に代替になるものがない（多分）。
一応、下記を使うと、起点となる要素からの相対距離が得られる。

~~~js
var offsetTop = element.offsetTop;
var offsetLeft = element.offsetLeft;
~~~

起点となるエレメントに要注意。**必ずしも画面の左上が起点ではない**。親要素が``position:relative``なら親要素が起点になる。

画面左上を起点としたいならDOM構造に注意して、各要素のoffsetTopやoffsetLeftを足し合わせるという作業が必要になることがある。


### scrollTop, scrollLeft

~~~js
// スクロール量を得る
var scrollTop = element.scrollTop;
var scrollLeft = element.scrollLeft;

// スクロール量を設定
element.scrollTop = scrollTop;
element.scrollLeft = scrollLeft;
~~~

### width/height

~~~js
// 横幅、縦幅を得る(padding,border含む)
var width = element.offsetWidth;
var height = element.offsetHeight;

// 横幅、縦幅を設定する
// 普通はpadding, borderを含まない box-sizing:border-boxならpadding, bordeerを含む。
element.style.width = "100px";
element.style.heigth = "100px";
~~~
``offsetWidth``や``offsetHeight``はReadOnlyです。


## Events

### on, off(イベント)
``addEventListener``/``removeEventListener``一択(attachEvent?何それ?)。

~~~js
element.addEventListener("eventName", callback);
element.removeEventListener("eventName", callback);
~~~

``jQuery.on(element, "eventName", callback)``のように、将来的に追加されるエレメントにも対応させる方法は調べてません。


### ready
単純なところでは``window.onload``。

~~~
window.onload = function() {
	// コード
}

~~~
これだとひとつしかイベントを登録できないし、''$(document).ready''とも厳密にはタイミングが違う。

最近のブラウザでは以下のほうが無難かも。

~~~
document.addEventListener('DOMContentLoaded', function() {
  console.log('DOMContentLoaded');
});
~~~

### trigger 

1. イベントオブジェクトの生成
1. イベントオブジェクトを初期化
1. イベントをディスパッチ

の流れ。

~~~js
// クリックイベントを発生させる

// イベント作成
var event = document.createEvent("MouseEvent");

// イベントオブジェクトを初期化
event.initMouseEvent("click",true,true,window,0,0,0,0,0,false,false,false,false,0,null);

// イベントをディスパッチ
targetElement.dispatchEvent(event);
~~~

MouseEvent以外にもKeyboardEventとか色々あってブラウザごとにバリエーションは違うらしい。Mozillaだと↓にいっぱい載ってる。

https://developer.mozilla.org/en-US/docs/Web/API/Event

クリックイベントとかを自前で発火させることはあまりないとは思うけど・・・


カスタムイベントを発火させるには

~~~js
var event= document.createEvent("Event");
event.initEvent("eventName",false,false);
targetElement.dispatchEvent(event);
~~~

各引数の意味とかはこの辺が詳しい↓

http://hakuhin.jp/js/event.html

## Effects
### show/hide/toggle
``display:none``するcssクラスを準備しておいて、``styleList``を通してクラスをつけたり外したりするのが一番楽。

```css
.hide{
	display:none !important;
}
```

```js
// 非表示にする
element.classList.remove("hide");

// 表示する
element.classList.add("hide");

// toggle
element.classList.toggle("hide");

```

もちろん、

~~~js
// 直接styleをいじって非表示にする
element.style.display = "none";
~~~

として非表示にすることも可能。しかし、どうやって表示するか決めかねる。

~~~js
// 表示するとき
// displayはblockか？inlineか？はたまたinline-blockか？それ以外？
element.style.display = "??";
~~~

jQueryはこのあたりをhide前にキャッシュしたり、見えないiframeにエレメントを追加したりしてうまいこと実装しているようだけど、ゼロから実装しなおすのは面倒くさい。可能なら上の例のようにcssのクラスを準備しておくのが一番手っ取り早い。



### ajax
詳しくない＆Web上にたくさん情報があるので割愛。
気が向いたら書くかも。