Web の勉強を2012年頃にしたのですが、そこから知識のアップデートをしていなかったので完全に取り残されていました。 たまに担当システムの要件で JS を書くことがあり、その過程で今風な書き方を色々と勉強しました。
一番驚いたのは、2012年当時は「素のJavaScriptで書くのは罰ゲーム」みたいなイメージだったのですが、 そこからかなり進化していて、jQuery などのライブラリがなくても複雑な処理が書けるようになっていることでした。
いくつか印象に残ったことについて、この記事にまとめたいと思います。 なお、今風の書き方を実践すればするほど、 IE や Safari では動かなくなります。
要素に値を保持させるには data 属性を使う
data 属性とは
フロントやってる人からしたら「知らなかったの!?」と言われるレベルの常識らしいのですが、最近まで知りませんでした。
data 属性とは data-*="value" の形で任意のデータを要素に持たせることが出来る機能です。
JavaScript でその要素が持っている特定のデータについてなにか処理をしたい、というときに簡単にデータを持たせることが出来るようになりました。
例えば、地方を表す div 要素があったとき、それぞれに天気のデータを持たせるコードは以下のように書けます。
<div data-weather="sunny">北海道</div>
<div data-weather="cloudy">東京</div>
<div data-weather="sunny">大阪</div>
<div data-weather="rainy">福岡</div>
<div data-weather="rainy">沖縄</div>
この中で、北海道の天気を取得するJavaScript は以下のように
element.data.(属性名) の形で書けます。
vat hokkaidoWeather = document.querySelector('div').data.weather
この機能がない時は、class の中に無理矢理データを持たせたりしていたのですが、
<div class="weather-sunny">北海道</div>
<div class="weather-cloudy">東京</div>
<div class="weather-sunny">大阪</div>
<div class="weather-rainy">福岡</div>
<div class="weather-rainy">沖縄</div>
クラスの一覧を取得し、その中で weather で始まるクラスを探し、天気の部分だけをパースして…と面倒でした。 そもそも、class はデータを入れるための場所ではないので、この様な使い方は正しくありません。
data-* の属性はいくつでも持たせることが出来るので、なにか要素に付随する状態や属性を表したい場合はこちらを使うべきです。 (逆に、見た目に関するものをこちらに入れるのは正しくありません。classを使いましょう)
値の取得・設定
JavaScript を使ってこれらの値を取得・設定する時には、dataset 関数を使います。
例えば、以下のような HTML に対して値を取得するには、
<div id="okinawa" data-weather="rainy">沖縄</div>
以下のような JavaScript で可能です
var okinawaElement = getElementById('okinawa');
var okinawaWeather = okinawaElement .dataset('weather');
data-* の後は複数の単語を書くことが出来ます
<div id="okinawa" data-today-weather="rainy" data-yesterday-weather="sunny">沖縄</div>
複数の単語が続く場合、dataset ではキャメルケースで記述します
var okinawaElement = getElementById('okinawa');
var okinawaTodayWeather = okinawaElement .dataset('todayWeather');
var okinawaYesterdayeather = okinawaElement .dataset('yesterdayWeather');
DOM 要素を選択する場合には querySelector() を使う
querySelector を使うと jQuery と同じような書き方で DOM 要素を選択することが出来ます。
querySelectorAll は合致する要素の配列が返ってきます(1つしかなくても配列で返ってきます。
一方、
querySelector は一番最初に合致した要素が返ってきます。
Web の最先端である Google Developers のサンプルコードを見ても、 querySelector/querySelectorAll が沢山出てきます。
一昔前は JavaScript でDOM 要素を選択するには
getElementById か getElementsByClassName でした。
(クラスで絞り込む場合には複数個返ってくるので Element’s’です。英語って難しいですね)
これらの関数はその名の通り、IDやクラス名から要素を選択するという機能を持っているのですが、
ID やクラス名以上に複雑な選択をしようとすると記述が煩雑になっていました。
例えば、以下のような div があったとき
<div class="prefecture" data-weather="sunny">北海道</div>
<div class="prefecture" data-weather="cloudy">東京</div>
<div class="prefecture" data-weather="sunny">大阪</div>
<div class="prefecture" data-weather="rainy">福岡</div>
<div class="prefecture" data-weather="rainy">沖縄</div>
getElementsByClassName を使って天気が sunny の要素だけを取り出そうとすると、以下のようになります。 要素を選択するだけなのにこんなに書かないといけません。
var prefectures = document.getElementsByClassName('prefecture');
var result = [];
for(var i = 0; i < a.length; i++) {
if (prefectures[i].dataset.weather == 'sunny') {
result.push(prefectures[i]);
}
}
console.log(result);
jQuery ならばこれを CSS セレクタで記述することが出来るので、 非常にシンプルな形で書くことが出来ます(コードは省略)。
querySelector(), querySelectorAll() を使うと、jQuery と同じように簡潔に書くことが出来ます。 さっきは8行だったコードがたったの2行になりました
var result = document.quertSelectorAll('prefecture[data-weather="sunny"]');
console.log(result);
for 文 は複数の書き方がある
JavaScript はC言語ライクな for 文を書くことが出来ます。 それに加え、今風の言語では当たり前なオブジェクトの中身を取り出す for-each 的な書き方もできます。
例として、引き続き先ほどのHTMLを使用します。
<div class="prefecture" data-weather="sunny">北海道</div>
<div class="prefecture" data-weather="cloudy">東京</div>
<div class="prefecture" data-weather="sunny">大阪</div>
<div class="prefecture" data-weather="rainy">福岡</div>
<div class="prefecture" data-weather="rainy">沖縄</div>
div の配列を用意します。
var divs = document.querySelectorAll('div');
この配列をいろんな方法で取り出して見ましょう
[方法その1] for (i = 0; i < length; i++)
私が大学で習った for 文はこの書き方でした。
for (var i = 0; i < divs.length; i++) {
console.log(divs[i]);
}
<出力結果>
<div class="prefecture" data-weather="sunny">北海道</div>
<div class="prefecture" data-weather="cloudy">東京</div>
<div class="prefecture" data-weather="sunny">大阪</div>
<div class="prefecture" data-weather="rainy">福岡</div>
<div class="prefecture" data-weather="rainy">沖縄</div>
[方法その2] for-in 文
for-in を使うと配列のキーが返ってきます。 (ただ、それ以外にも余計な物が返ってきてしまいます)
for (let key in divs) {
console.log(key);
}
<出力結果>
0
1
2
3
4
length
item
entries
forEach
keys
values
これは、
divs 自身の要素に加えて、「配列」から継承されるオブジェクトも全て返ってくるためです。
自分自身の持つ要素だけを取り出したい場合には、
hasOwnProperty を使います。
(IntelliJ 系の IDE の場合、このチェックをしていないと警告が出ます)
for (let key in divs) {
if (divs.hasOwnProperty(key)) {
console.log(key);
}
}
<出力結果>
0
1
2
3
4
最後にこのキーで元の配列にアクセスすると、div 要素にアクセス出来ます。
for (let key in divs) {
if (divs.hasOwnProperty(key)) {
console.log(divs[key]);
}
}
<出力結果>
<div class="prefecture" data-weather="sunny">北海道</div>
<div class="prefecture" data-weather="cloudy">東京</div>
<div class="prefecture" data-weather="sunny">大阪</div>
<div class="prefecture" data-weather="rainy">福岡</div>
<div class="prefecture" data-weather="rainy">沖縄</div>
上記のような仕様により、Mozilla のドキュメントには 配列の繰り返し処理には C 言語ライクな for 文の方が向いていると書かれています。
[方法その3] for-of
注意) IE では動きません
for-of はオブジェクトの値を取り出す事が出来ます。
for-in が Python の object.keys() だとしたら、
for-of は object.values() にあたります。
ここ数年で追加された仕様のため、古いブラウザでは動かないという欠点がありますが、 とても簡潔に書くことが出来ます。
for (let div of divs) {
console.log(div);
}
<出力結果>
<div class="prefecture" data-weather="sunny">北海道</div>
<div class="prefecture" data-weather="cloudy">東京</div>
<div class="prefecture" data-weather="sunny">大阪</div>
<div class="prefecture" data-weather="rainy">福岡</div>
<div class="prefecture" data-weather="rainy">沖縄</div>
[方法その4] array.forEach
for-in と for-of と違い、これは array 型のオブジェクトにしか使うことが出来ません。
forEach は値の回数だけコールバック関数を実行します。 …と聞いてもよく分からないと思うので、実際にコードで説明します。
divs.forEach(function (v) {
console.log(v);
})
<出力結果>
<div class="prefecture" data-weather="sunny">北海道</div>
<div class="prefecture" data-weather="cloudy">東京</div>
<div class="prefecture" data-weather="sunny">大阪</div>
<div class="prefecture" data-weather="rainy">福岡</div>
<div class="prefecture" data-weather="rainy">沖縄</div>
このように、コールバック関数の最初の引数に値が入ります。 コールバック関数は引数を3つ持つことが出来ます。
2番目の引数には配列のインデックスが入ります。
divs.forEach(function (v, i) {
console.log(i);
})
<出力結果>
0
1
2
3
4
3番目の引数には実行中の配列 (この例の場合は
divs の値)が入ります。
divs.forEach(function (v, i, a) {
console.log(a);
})
<出力結果>
(5) [div.prefecture, div.prefecture, div.prefecture, div.prefecture, div.prefecture]
(5) [div.prefecture, div.prefecture, div.prefecture, div.prefecture, div.prefecture]
(5) [div.prefecture, div.prefecture, div.prefecture, div.prefecture, div.prefecture]
(5) [div.prefecture, div.prefecture, div.prefecture, div.prefecture, div.prefecture]
(5) [div.prefecture, div.prefecture, div.prefecture, div.prefecture, div.prefecture]
即ち、 a[i] = v ということになります。
次回予告
- let
- template
- fetch
の3本でお送りいたします。ちなみに、全て IE では動きません
江藤 光
まだまだ気持ちは新人です。