【備忘録】Javascript : 基本的なDOM操作

### DOMとは
DOMとは`Document Object Model`の略でマークアップ言語で書かれている文書にアクセスする為の仕組み。
DOMは文書内のタグや属性、要素がツリー(階層)構造で出来ていると捉えている。またDOMはそれらの構成要素をオブジェクトと見なしノードと呼んでいる。(`要素ノード`、`属性ノード`、`テキストノード`といった具合に。)

例) 
・html,head,title,body,p... : 要素ノード
・carset,id,name,lang... : 属性ノード

### 頻出の要素の取得方法
## 1. getElementById
指定されたid値を持つ要素をElementオブジェクトとして返します。

```sample.html

<span id="result"></span>
```

```javascript.js
let current = new Date(); // 実行日時取得
let result = document.getElementById('result');
// id値が'result'の要素を取得し、変数resultに代入
result.textContent = current.toLocaleString();
// toLocaleStringメソッドにより文字列化し、'result'要素にテキストとして埋め込み
```

## 2. getElementsByTagName
指定されたタグ名を持つ要素を`HTMLCollection`として返します。
今の段階では`HTMLCollection`は配列と考えてもらって構わないと思います。注意すべきはタグ名の場合はid値と異なって複数の要素が取得出来る為、メソッド名もElementが複数形になっています。

```sample.html

<ul>
    <li><a href="https://www.google.com/" name="google" >google</a></li>
    <li><a href="https://www.firefox.com/" name="firefox" >firefox</a></li>
    <li><a href="https://www.yahoo.co.jp/" name="yahoo" >Yahoo!</a></li>
    <li><a href="https://www.twitter.com/" name="twitter" >twitter</a></li>
    <li><a href="https://www.amazon.com/" name="amazon" >amazon</a></li>
</ul>
```

```javascript.js
let list = document.getElementsByTagName('a'); // aタグを全て取得　=> htmlコレクションを取得
      let firefox = list.namedItem('firefox'); // name属性が'firefox'のものを取得

      for (let i = 0; i < list.length; i++) {
        console.log(list.item(i).text); // collection.item => 要素
        // google,firefox,Yahoo!,twitter,amazon
      }

console.log('name=f : ' + firefox); // name=f : https://www.firefox.com/
console.log(list); 
// HTMLCollection(5) [a, a, a, a, a, google: a, firefox: a, yahoo: a, twitter: a, amazon: a]

```

返り値のHTMLCollectionをfor文で回し、要素を一つずつ、ログ出力している箇所に注目してください。`list.item(i).text`を見てもらうとlistに対して`item`メソッドとその順番を指定して取得しています。これはそういうもんだと割り切って覚えてしまうのが良いでしょう。その後の`text`メソッドは取得した要素のテキストを取得しています。ちなみに別の取得の仕方もありますので試してみてください。

## 3. getElementsByName
name属性を指定して要素を取得します。結果、NodeListオブジェクトを返してくれます。個人の感覚ですがあまりHTMLCollectionと大差ありません。参考書にも載っていましたが`namedItem`メソッドが使えないくらいみたいです。ラジオボタンやチェックボックスなど、同じname属性をもつ要素群を取得するのに使用するみたいです。

```sample.html

<form>
    <div>
      好きな食べ物は？ :
      <label><input type="checkbox" name="food" value="ラーメン">ラーメン</label>
      <label><input type="checkbox" name="food" value="餃子">餃子</label>
      <label><input type="checkbox" name="food" value="焼肉">焼肉</label>
      <input type="button" id="btn" value="送信">
    </div>
  </form>
```

```javascript.js

document.getElementById('btn').addEventListener('click',function(){
  let result = []; // 空の配列を準備
  let foods = document.getElementsByName("food");
  // name="food"の物を全て取得

  for (let i = 0; i < foods.length; i++){
    // 取得したものを全てチェックする
    let food = foods.item(i);
    if(food.checked){
      // 各要素のfoodのcheckedがtrueの場合、空の配列にvalueをpushする
      result.push(food.value);
    }
  }

  window.alert(result.toString());
},false)

```

上記の例をみてもらうとname属性で指定して同じ要素群を取得し、`checked`がtrueの要素のみvalueを取得しています。チェックボックスの判定の例ですが基本、ラジオボタンも同様の取得の方法です。

## 4. getsElementsByClassName
クラス属性を指定して取得します。name属性やタグ属性と同じようにelementが複数形になっている所に注意してください。

```sample.html
<ul>
  <li><a href="#" class="language">Javascript</a></li>
  <li><a href="#" class="language">Java</a></li>
  <li><a href="#" class="language">Ruby</a></li>
</ul>
```

```javascript.js

let list = document.getElementsByClassName('language'); // class属性が'language'の要素取得
for (let i = 0; i<list.length; i++){
  console.log(list.item(i).text);
  /**
   * Javascript,Java,Ruby
   */
}
```

## 5. querySelector, querySelectorAll
cssと同じようにセレクター式で要素を指定し取得することが可能です。これにより今まで紹介してきた取得方法と比べてより複雑な条件で要素を指定することが出来るようになりました。
ただ注意点としては今までの方法( getElement~By~ )の方法と比べるとパフォーマンス速度が低速になってしまうので使い所を弁えましょう。

下記のコードはリストの要素全てとJavaScript関連の言語、フレームワークなどのみに絞って取得したものです。

```sample.html

<ul id="languageList">
    <li><a href="#" class="Javascript">Javascript</a></li>
    <li><a href="#" class="Java">Java</a></li>
    <li><a href="#" class="Ruby">Ruby</a></li>
    <li><a href="#" class="python">python</a></li>
    <li><a href="#" class="Javascript">Vue.js</a></li>
    <li><a href="#" class="Javascript">Typescript</a></li>
    <li><a href="#" class="golang">golang</a></li>
    <li><a href="#" class="Javascript">Angular.js</a></li>
    <li><a href="#" class="Javascript">React.js</a></li>
    <li><a href="#" class="Javascript">JQuery</a></li>
  </ul>
```

```javascript.js

let languageList = document.querySelectorAll('li'); // liタグの要素全取得
let javascriptList = document.querySelectorAll('#languageList .Javascript'); // idがlanguageList 且つ class名がjavascript の要素を全取得
// languageList,javascriptListも NodeListオブジェクト

for (let i = 0; i< languageList.length; i++){ 
  console.log(languageList.item(i).textContent); 
  /** Javascript,Java,Ruby,python,Vue.js,Typescript,golang,Angular.js,React.js,Jquery*/
}

for (let i = 0; i< javascriptList.length; i++){ 
  console.log(javascriptList.item(i).textContent); 
  /** Javascript,Vue.js,Typescript,Angular.js,React.js,Jquery*/
}
```

## 6. ノードウォーキング
要素を取得する際に度々、文書全体から要素を探すのはパフォーマンスの低下に繋がってしまうので、ある起点の要素を取得した際に、その相対関係を指定する事によって期待の要素を取得することができます。

位置関係を示すノード一覧
・起点ノード : `currentNode`
・親ノード : `parentNode`
・兄ノード : `previousSibiling`
・弟ノード : `nextSibiling`
・全子ノード : `childNodes`
・最初の子ノード : `firstChild`
・最後の子ノード : `lastChild`

```sample.html
<form>
    <label for="food">一番好きな食べ物は？</label>
    <select name="food" id="food">
      <option value="ラーメン">ラーメン</option>
      <option value="餃子">餃子</option>
      <option value="焼肉">焼肉</option>
    </select>
    <input type="submit" value="送信">
  </form>
```

```javascript.js
let food = document.getElementById('food'); // selectを取得
let opts = food.childNodes; // foodの子要素を取得
console.log(opts); // NodeList(7) [text, option, text, option, text, option, text]

for(let i = 0; i<opts.length; i++){
  let opt = opts.item(i);
  if(opt.nodeType === 1){ 
    // 子ノードが要素ノードの時のみ、値を出力
    console.log(opt.value); // => ラーメン、餃子、焼肉
  }else{
    // TEXT_NODEの時
    console.log(opt); // => #text
  }
}
```

id属性`food`を持つ`select`タグを取得し、`childNodes`を使用して子要素を全て取得します。注意点がこの子要素の中にはvalue値以外の要素が入っているため、`nodeType === 1`の場合のみ、取得するものです。nodeTypeが1のものとは要素ノードを指します。

```javascript.js

/**
 * firstElementChild : 子要素で子要素の中の兄要素
 * nextElementSibling : 弟要素
 * 要素オブジェクトしか取得しないのでnodeTypeを気にする必要がなくて済む
 */
let childOpts = food.firstElementChild; //最初の子要素を取得
while(childOpts){
  // 要素が存在する間、ずっとループする
  console.log(childOpts.value);// => ラーメン、餃子、焼肉
  childOpts = childOpts.nextElementSibling; // 弟要素をchildOpts自身に代入、最後の要素に弟要素はないのでループが終わる
}

let lastChildOpts = food.lastElementChild; //最後の子要素を取得
while(lastChildOpts){
  // 要素が存在する間、ずっとループする
  console.log(lastChildOpts.value);// => 焼肉、餃子、ラーメン => 逆順
  lastChildOpts = lastChildOpts.previousElementSibling; // 兄要素をlastChildOpts自身に代入、最後の要素には兄要素がないのでループが終わる
}
```

## 7. getAttribute, setAttribute
属性にアクセスできます。

【使用方法】
・getAttribute：`要素.getAttribure(属性)`
・setAttribute：`要素.setAttribute(属性,値)`

```sample.html
  <p id="beforechange">赤</p>
  <input type="submit" value="送信" id="btn">

  <style>
    #beforechange{
      color: red;
    }
    #afterchange{
      color:blue;
    }
  </style>
```

```javascript.js
let btn = document.getElementById('btn');
let word = document.getElementById('beforechange');

btn.addEventListener('click',function(){      
  word.setAttribute('id','afterchange');// id属性に'afterchange'をセット
  word.innerHTML='青';
},false);
```

## 8. attributes
全ての属性情報が取得できます。
HTMLCollectionに似て個別のノードに名前、もしくはindexでアクセスすることができます。

```sample.html
<img src="スクリーンショット 2020-06-05 14.52.32.png" 
  alt="デスクトップ" height="67" width="215" id="picture">
```

```javadscript.js
  let pic = document.getElementById('picture');
  let attrs = pic.attributes; // 属性とその値を全て取得

  console.log(attrs["id"]); // 名前でアクセス => id="picture"
  for(let i = 0; i<attrs.length; i++){
    let attr = attrs.item(i); // index でアクセス
    console.log(attr.name + ' : '+ attr.value)
    /**
     * src : スクリーンショット 2020-06-05 14.52.32.png
     * alt : デスクトップ
     * height : 67
     * width : 215
     * id : picture
     */
  }
})
```