最終更新日:190903 原本2017-12-05

フォーム要素にモダンにアクセスするイディオム・定石

目標

  • for ループを使わないこと。
  • html側の変更に強いこと。
  • できるだけモダンに。
  • 可読性を高くしたい。

テキストボックスのテキストを取得・設定する

image.png

    <form>
      <input type="text" id="field">
      <p id="message"></p>
    </form>
document.addEventListener('DOMContentLoaded', () =>{
  const textbox = document.getElementById('field');
  const message = document.getElementById('message');
  textbox.value ='なにか入力してください';    // ※1

  textbox.addEventListener('keyup', () => {
    message.textContent = textbox.value;    // ※2
  }, false);
}, false);

テキストボックスのテキストは取得した input 要素の value プロパティに代入すると表示できます。(※1)
入力されたテキストも value プロパティから取得できます。(※2)
email, search, tel, number, range, time, date, color, datetime-local, month, week, textarea も同じです。

参考

単一セレクトボックスの値を取得、設定する

image.png

selectbox.html
    <form>
      <select id="sel">
        <option value="value1">Value 1</option>
        <option value="value2">Value 2</option>
        <option value="value3">Value 3</option>
      </select>
      <input type="button" id="btn" value="送信">
    </form>
selectbox.js
window.addEventListener('DOMContentLoaded', () => {
  const select = document.getElementById('sel');
  select.value = 'value2';

  select.addEventListener('change', (e) => {    // ※1
    console.log(e.target.value);
  }, false);
}, false);

セレクトボックスの選択が変わった時、コンソールにその値を出力します。
event.target からイベントを発生させたオブジェクトへの参照を取得します。(※1)
.getElementById() でセレクト要素取得し value プロパティを参照しても同じです。

参考

複数選択できるリストボックスの選択された値の一覧を取得する。

無題.png

listbox.html
    <form>
      <label for="menu">ご注文は?:</label>
      <select id="menu" multiple>
        <option value="ramen">ラーメン</option>
        <option value="curry">カレー</option>
        <option value="tonkatsu">とんかつ</option>
        <option value="karaage">から揚げ定食</option>
      </select>
      <input type="button" id="btn" value="注文">
    </form>

getElementXxxx と Array.prototype.filter()

listbox1.js
window.addEventListener('DOMContentLoaded', () => {
  const getSelectedValues = id => {

    // id で select 要素を取得しその配下の HTMLOptionCollection を得る
    const options = document.getElementById(id).options;

    // セレクトされたオプションの値の配列を返す
    return Array.from(options)      // 配列に変換して ※1
      .filter(el => el.selected)    // 選択された要素のみの配列を作り
      .map(el => el.value);         // 配列のvalueをマッピングする
  }

  const button = document.getElementById('btn');
  button.addEventListener('click', () => {
    const values = getSelectedValues('menu');

    console.log(values);    // ex. ["curry","tonkatsu"]
  }, false);
}, false);

セレクタ式と違い文字列ではないので IDE の入力補完を受けられます。(特に TypeScript など静的型付けのAltJSで書くと顕著)
『配列のようなオブジェクト』から配列に変換する方法は以下のようなものもあります。(※1)

// arrayLikeObj ← 配列のようなオブジェクト( NodeList や arguments 等)

array = [...arrayLikeObj]    //スプレッド演算子で展開したものを配列に格納する
array = [].map.call(arrayLikeObj, el => el)    // 要素を取り出し配列としてマッピング(あまり見ない)
array = [].slice.call(arrayLikeObj)     // レガシー??

Arrayの反復メソッドを使うと別の条件も簡単付けられます。

// 選択されている、かつ、クラス名に foo を含む要素を抽出する
[...elems].filter(el => el.selected && el.classList.contains('foo'));

また、call メソッドthisHTMLCollctionNodeList を渡すと最初に配列へ変換せずに Array の反復メソッドを使用できます。

    return [].filter.call(elems, el =>  el.checked)
             .map(el => el.value);

参考

querySelectorAll()

listbox2.js
window.addEventListener('DOMContentLoaded', () => {
  const getSelectedvalues = id => {

    // select 要素配下の選択された option 要素を NodeList として得る
    const checkedOptions = document.querySelectorAll(`#${id} option:checked`); // ※1
    return [...checkedOptions].map(opt => opt.value);
  }

  document.getElementById('btn').addEventListener('click', () => {
    const values = getSelectedvalues('menu');

    console.log(values);    // ex. ["curry","karaage"]
  }, false);
}, false);

テンプレートリテラルを使って ID を埋め込みます。
セレクター式の場合 option 要素の選択状態を表すのは selected ではなく checked であることに注意します。(※1)

参考

複数選択できるリストボックスの選択状態を設定する

set_listbox.js
window.addEventListener('DOMContentLoaded', () => {
  const setListbox = (id, ...values) => {    // ※1
    const select = document.getElementById(id);    // セレクト要素を取得
    Array.from(select.options)    // セレクト要素配下のオプション要素を配列に変換する
      .forEach(opt => opt.selected = values.includes(opt.value)); 
  };
  setListbox('menu', 'ramen', 'karaage');
}, false);

スプレッド演算子を使って可変長引数を定義します。(※1)
関数の利用者は... で一目で引数が可変長とわかります。
スプレッド演算子を使わない場合でも arguments から引数にアクセスできます。
しかし、arguments オブジェクトは配列ではないので配列に変換しなければ配列の反復メソッドは使えません。
スプレッド演算子で宣言された可変長引数は Array であるのでそのまま .includes() などを使用できます。

チェックされたチェックボックスの値を取得する

image.png

checkbox.html
    <form>
      <div id="books">
        <label>
          <input type="checkbox" value="Don Quijote de la Mancha" name="book">
          ドン・キホーテ
        </label>
        <label>
          <input type="checkbox" value="The Little Prince" name="book">
          星の王子様
        </label>
        <label>
          <input type="checkbox" value="The Catcher in the Rye" name="book">
          ライ麦畑で捕まえて
        </label>
      </div>
      <input type="button" id="btn" value="送信">
    </form>

Array.prototype.filter()

checkbox1.js
window.addEventListener('DOMContentLoaded', () => {
  const getCheckedValues = name => {

    // HTMLOptionElement の NodeList を得る
    const elems = document.getElementsByName(name);

    // チェックされたinput要素の値の配列を返す
    return [...elems].filter(el => el.checked)
      .map(el => el.value);
  }

  document.getElementById('btn').addEventListener('click', () => {
    const values = getCheckedValues('book');
    console.log(values);    // ex. ["Don Quijote de la Mancha","The Catcher in the Rye"]
  }, false);
}, false);

選択状態を表すプロパティが selected でなく checked である以外、特にリストボックスとの違いはありません。

.querySelctorAll()

checkbox2.js
window.addEventListener('DOMContentLoaded', () => {
  const getCheckedValues = id => {
    const checked = document.querySelectorAll(`#${id} input[type="checkbox"]:checked`);
    return [...checked].map(el => el.value);
  }

  document.getElementById('btn').addEventListener('click', () => {
    const values = getCheckedValues('books');

    console.log(values);    // ex. ["Don Quijote de la Mancha","The Catcher in the Rye"]
  }, false);
}, false);

チェックボックスの選択状態を設定する

set_checkbox.js
const setCheckBox = (name, ...values) => {
  const elems = document.getElementsByName(name);
  elems.forEach(el => el.checked = values.includes(el.value));    // ※1
}

window.addEventListener('DOMContentLoaded', () => {
  setCheckBox('book', 'Don Quijote de la Mancha', 'The Catcher in the Rye');
}, false);

それぞれの input 要素の value が設定するvaluesに含まれている場合、チェックします。(※1)
getElementsByName() の戻り値は NodeList で配列ではありませんが .forEach() があります。
HTMLCollction には .forEach() はありません。)

ラジオボタンの値を取得する

image.png

radio.html
    <form>
      <!-- ラジオボタンは name 属性で一組となります。 -->
      <label>
        <input type="radio" value="vue" name="framework">
        Vue.js
      </label>
      <label>
        <input type="radio" value="angular" name="framework">
        Angular4
      </label>
      <label>
        <input type="radio" value="react" name="framework">
        React.js
      </label>
      <input type="button" id="btn" value="送信">
    </form>

getElementXxxx と Array.prototype.find()

radio.js
window.addEventListener('DOMContentLoaded', () => {
  const getCheckedRadioValue = name => {
    const elems = document.getElementsByName(name);
    const checkedButton = [...elems].find(el => el.checked);    // ※1
    return checkedButton ? checkedButton.value : '';
  };
  document.getElementById('btn').addEventListener('click', () => {
    const value = getCheckedRadioValue('framework');

    console.log(value);    // ex. angular
  }, false);
}, false);

ラジオボタンは一つしか選択されないため最初に条件に合致(checkedプロパティがtrue)した要素を .find() で取得しその value 返します。(※1)
ラジオボタンが選択されていない場合、三項演算子で空文字を返します。

ラジオボタンの選択状態を設定する

set_radio.js
const setRadioButton = (name, value) => {
  const radios = document.getElementsByName(name);
  [...radios].some(el => el.checked = (el.value === value));    // ※1
};

window.addEventListener('DOMContentLoaded', () => {
  setRadioButton('framework', 'vue');
}, false);

ラジオボタンは name 属性でグループ化される単一選択です。
ループを回してラジオボタンをチェックする際、.some() によりチェック直後にループを脱出します。(※1)
この .some() の使い方は本来のものではないという考えもあります。
.some() を使わない場合、無駄なループを回すことになりますが .forEach() を使う、もしくは for of でチェックをしたら break でループを脱出します。

参考

ここまで読んでいただきありがとうございました。
Array の反復メソッドの詳細はすべて Array - MDN web docs の反復メソッドの項に載っています。