超個人的メモです。
jQueryなしにJavaScriptを使いたい主義で。
url先のレスポンス(HTMLでもJSONでも返してくるもの)を取得する
function getSrc(url) { var req = new XMLHttpRequest(); req.open("GET", url, false); // 第3引数がfalseなので同期通信 req.send(null); // 通信開始 return req.responseText; }
返値:String
HTMLをDOMにする
html要素の中に入れる
var html = <HTML文字列> var domParser = new DOMParser(); var parsedHtml = domParser.parseFromString(html, "text/html").documentElement
div要素の中に入れる
var html = <HTML文字列> var dom = document.createElement("div"); dom.innerHTML = html;
DOMインターフェースのヒエラルキー(とは言わないのかもしれないけど)
コメント、テキスト、属性(Attribute)、要素(Element)、Document などほとんどのインターフェースは Node から派生する。
DOM要素を取得
要素を指定して取得
(リンク先はMozilla Developer Network)
- id で選択
- document.getElementById
返値:HTMLElement
(これだけ "Element" と単数になるのは id が document 内で unique だから。
他の類名メソッドと異なり、使えるのは document オブジェクトのみで要素に対しては使えない)
- document.getElementById
- CSS class 名で選択
- name で選択
- tag 名で選択
- document.getElementsByTagName
返値:NodeList - element.getElementsByTagNameNS(名前空間指定。 HTML では使わない?)
- document.getElementsByTagName
- CSS セレクタで選択
- document.querySelector
返値:HTMLElement(見つかった最初の要素のみ) - document.querySelectorAll
返値:NodeList
- document.querySelector
- XPath で選択
XPathResultを扱いやすく
XPathResult は扱いにくい(単一と複数で振る舞いが違うしforEachも使えない)ので、ラップした関数を使うと便利そう。
document.xpath = function(expression) { ret = document.evaluate(expression, document, null, XPathResult.ANY_TYPE, null); switch(ret.resultType) { case 1: return ret.numberValue; // NUMBER_TYPE case 2: return ret.stringValue; // STRING_TYPE case 3: return ret.booleanValue; // BOOLEAN_TYPE case 4: // UNORDERED_NODE_ITERATOR_TYPE case 5: a=[]; // ORDERED_NODE_ITERATOR_TYPE while(e=ret.iterateNext()) { a.push(e); }; return a; // 複数要素を要素とする配列を返す default: return ret; } }
注意すべきこと:テキストノード及びコメントノードも指定に含まれる場合がある
この図の場合、document.getElementsByTagName("body").firstChild
の返値は h1 タグではなく "\n␣␣" になる。document.getElementsByTagName("body").firstElementChild
にすれば h1 タグが返る。
テキストノードなどを含む指定
返値には要素( Element オブジェクト)以外のテキストノード・コメントノードも含まれる。
- 親ノード
- なし(親がテキストノードということはあり得ない)
- 兄ノード(隣接ノード)
- 弟ノード(隣接ノード)
- 子ノード
- Node.childNodes(子ノード全てなので複数)
返値:NodeList - Node.firstChild
- Node.lastChild
- Node.childNodes(子ノード全てなので複数)
テキストノードなどを含まない指定
返値には要素( Element オブジェクト)のみが含まれる。
- 親要素
- 兄要素(隣接要素)
- 弟要素(隣接要素)
- 子要素
DOMの属性を取得
対象要素targetのid属性を取得
target.id; target.getAttribute("id"); target.attributes["id"]; target.attributes.item("id"); // 本来はインデックス番号のみ target.attributes.getNamedItem("id");
DOMに要素を追加(innerHTMLの例は省く)
要素作成
document.createElement(<タグ名>);
テキスト要素作成
document.createTextNode(<テキスト>);
textNode = document.createTextNode(null); textNode.nodeValue = <テキスト>; // 空要素に追加。 nodeValue プロパティを変更すると反映される
elem = document.createElement(<タグ名>); elem.textContent = <テキスト>; // 要素elem の中身(子要素)をテキストに変更する(元々の子要素は消える)
ドキュメントの断片を作成
document.createDocumentFragment();
対象要素targetにid属性"unique"を設定
target.id = "unique"; target.setAttribute("id", "unique");
後者の参考:element.setAttribute - Web API | MDN
なおここには「現在の値にアクセスしたり、変更したりするにはプロパティを使用すべきです。」と書いてあるので、前者のようにプロパティでのアクセスが出来る場合、後者は使わないほうが良さそうですね。
要素itemを対象要素targetの末尾に追加
target.appendChild(item);
target.insertBefore(item);
target.insertBefore(item, null);
要素itemを対象要素targetのなかで内部要素innerTargetの前に追加
target.insertBefore(item, innerTarget);
要素の置き換え
(Node.replaceChild について追記予定。 ChildNode.replace() というのもあるらしい。)
要素の削除
子要素の削除
(Node.removeChild について追記予定)
子要素全てを削除
子要素の先頭要素が無くなるまで削除ループを続ける。
var myNode = document.getElementById("foo"); while (myNode.firstChild) { myNode.removeChild(myNode.firstChild); }
参考:Remove all child elements of a DOM node in JavaScript? - Stack Overflow
var myNode = document.getElementById("foo"); while (myNode.hasChildNodes()) { myNode.removeChild(myNode.firstChild); }
参考:基本から学ぶHTML5+JavaScript iPhone/Android対応 スマートフォンアプリの作り方: クジラ飛行机, 土井 毅 p.141
自要素の削除
(ChildNode.remove() について追記予定)
イベントハンドラ
ハンドラ:イベントが生じたときに実行する関数(やメソッド)
参考:イベントハンドラとイベントリスナの用語の使い分けを教えてください|teratail
登録
- DOM0で設定
element をクリックすると関数 Bar を実行
element.onclick = Bar;
同様の設定を行うと上書きされる。
- DOM2で設定
element をクリックすると関数(もしくはhandleEvent()メソッドを実装するオブジェクト)である listener を実行
element.addEventListener("click", listener);
同様の設定を行うと重複設定される。
削除するには
element.removeEventListener("click", listener)
呼び出し
- DOM0
foo.onclick = function() {}; //匿名関数 // 呼び出し foo.onclick();
当然ながら、イベントに登録している関数自身を呼び出しても同じ効果が得られる
foo.onclick = Bar;
// 関数の呼び出し
Bar();
- DOM2
foo.addEventListener("click", Bar); // 関数の呼び出し Bar();
ただし、HTMLElement.click() / HTMLElement.focus() / HTMLElement.blur() はイベントハンドラを呼び出せる。
foo.addEventListener("click", Bar);
foo.click();
- thisについて
ハンドラが登録されたオブジェクト(らしい
(追記予定)
画像の真のサイズの取得
画像を load し終えてから addEventListener で取得。 DOMContentLoaded では取得出来ない。
<body> <script> var listener = function(evt){ var image = document.getElementById("image"); var out = []; out.push(" width: " + image.naturalWidth); out.push("height: " + image.naturalHeight); console.log(out.join("\n")); }; // window.addEventListener("DOMContentLoaded", listener); window.addEventListener("load", listener); </script> <img id="image" src="http://ほにゃらら" /> </body>
インターフェース
クラスがオブジェクトの実装を指定するものであるのに対して,インタフェースはオブジェクトの外見(どのようなメソッドを持つかなど)だけを指定します。
まつもと直伝 プログラミングのオキテ - まつもと直伝 プログラミングのオキテ 第3回(3):ITpro
Nodeインターフェース
Node インターフェースには、すべての「DOM オブジェクト」で利用できる、基本的な機能がまとまっています。
「ノードインターフェース」を使用すると、木構造の親子関係を構築することができます。
JavaScriptプログラミング講座【ドキュメントオブジェクトモデル(DOM)について】
コンストラクタ
関数 A がコンストラクタの場合。
function A(){}; var a = new A(); // new A でも大丈夫なようだが、関数だから () を付けた方がいいのかな A.prototype.test = "test" a.test // "test" // インスタンス生成後でも反映されるのは「暗黙の参照」が働くため
prototypeの注意事項
なお,prototype拡張による具体的な問題点として,for inを用いた際にそのプロパティが列挙されてしまうという問題があります。
配列に対してfor inは使わないほうがよいというのも覚えておくべきことですが,ネイティブオブジェクトのprototype拡張はどこかでfor inが使われているんじゃないかと注意しなければいけなくなるのでお勧めできません。
:(中略)
for inを通常は用いることがないであろうString,Number,Functionなどのネイティブオブジェクトであれば,prototypeを拡張しても問題が出にくいため,積極的に拡張してもよいでしょう。
第14回 プロトタイプと継承:これでできる! クロスブラウザJavaScript入門|gihyo.jp … 技術評論社
継承
B の親が A の場合。
b.__proto__ === B.prototype b.__proto__.__proto__ === A.prototype B.prototype.__proto__ === A.prototype B.prototype.__proto__ === a.__proto__ // <- a.__proto__ === A.prototype B.prototype === a // new A()
を実現すれば良い。(参考:第15回 プロトタイプと継承#2:これでできる! クロスブラウザJavaScript入門|gihyo.jp … 技術評論社)
function A(){}; function B(){}; B.prototype = new A();
配列っぽいけど配列じゃ無い(=lengthプロパティはある)オブジェクトobjで配列のメソッドを使いたい
forEachを使いたい
もちろん map, filter, every/some, reduce/reduceRight でも同様に書けます。
Array.prototype.forEach.call; Array.prototype.forEach.apply;
例
> var arrMeta = docObj.getElementsByTagName('meta'); // 返値の NodeList オブジェクトは Array オブジェクトじゃないので forEach を使えない > Array.prototype.forEach.call( arrMeta, function(obj) { console.log( obj.getAttribute("property") ) } ); > Array.prototype.forEach.apply( arrMeta, [function(obj) { console.log( obj.getAttribute("property") ) }] ); // call とほぼ同じですが apply の場合は第2引数を配列にする必要があるようです
Arrayにしてしまうfunction
slice()
を使うと配列そのものが返る。
function toArray(obj) { return Array.prototype.slice.call(obj); }
たとえばdocument.getElementsByTagNameで取得することができるNodeListは動的なものなりますので、for文を用いてループをさせると多少遅くなってしまいます。なのでtoArray(document.getElementsByTagName('a'))のようにあらかじめしておけば、速度も速くなりますし、またforEachやmapといった配列を操作するのに便利なメソッドをつかうことが可能になり、コード全体の見通しも比較的 良くなるのではないでしょうか。
オブジェクトobjのコンストラクタを知りたい
Object.prototype.toString.call(obj); Object.prototype.toString.apply(obj); // 全く同じ
例
> Object.prototype.toString.apply(arrMeta); < "[object NodeList]"
返値の意味
全てのオブジェクトは toString メソッドを持ち、オブジェクトが文字列値として表されるべきときや、文字列が期待される構文で参照されたときに自動的に呼び出されます。デフォルトで、toString メソッドは Object の子孫にあたるあらゆるオブジェクトに継承されています。このメソッドがカスタムオブジェクト中で上書きされていない場合、toString は [object type] を返します( type はそのオブジェクトの型)。
オブジェクトのプロパティ一覧を列挙するには
Object.keys()
を使うのが一番簡単っぽい。
Object.keys()
概要
与えられたオブジェクト自身に存在する列挙可能なプロパティの配列を、for-in ループで提供されるものと同じ順番で返します (for-in ループとの違いは、for-in ループではプロトタイプチェインのプロパティも列挙することです)。
例
var arr = ["a", "b", "c"]; alert(Object.keys(arr)); // "0,1,2" とアラート表示 // array like object var obj = { 0 : "a", 1 : "b", 2 : "c"}; alert(Object.keys(obj)); // "0,1,2" とアラート表示 // getFoo は列挙可能ではないプロパティ var my_obj = Object.create({}, { getFoo : { value : function () { return this.foo } } }); my_obj.foo = 1; alert(Object.keys(my_obj)); // foo のみがアラート表示列挙可能ではないものを含むすべてのプロパティを取得したい場合は、Object.getOwnPropertyNames() をご覧ください。