『配列中にない値が範囲として含まれることを調べる方法』ってどういうこと?
『配列中にない値が範囲として含まれることを調べる方法』
言いかえれば、
『ウインドウサイズの範囲を指定した配列データから範囲インデックス値を取得する』
ということです。
これはレスポンシブサイトを作る時にブラウザの横幅や縦幅サイズから、
javascript で要素のサイズを変更したり、位置を配置しなおしたりする時に
ウインドウの基準となるサイズを [if~else] で逐次処理記述するのではなく、
配列を活用して、範囲とその時の値をデータ化して汎用的な処理関数を作る、
という目的のために考えたものです。
※ この記事は、web制作者の自分が、フロントエンドエンジニアさん向けに javascript ネタ記事として
配列活用の例で自分のサイトに掲載した記事から
関数アイデア(アルゴリズム)だけを紹介する記事となっています。
表題を一つの利用目的でもう少し詳しく言ってみれば、
『ウインドウサイズの範囲を示す配列から
現在のウインドウサイズが
どの値のグループ(範囲)に所属するかを調べ、
配列インデックス番号で返す関数の作り方アイデア紹介』
となります。
範囲インデックス値の取得とは?
例えば範囲指定配列として、x = [ 1024 , 800 , 600 ] (降順の数値データ)を用意した場合、
1024以上は インデックス番号が 0
1024未満800以上は インデックス番号が 1
800未満600以上は インデックス番号が 2
600未満は インデックス番号が 3
という値を返す関数を作るためのアイデア、ということです。
例えばウインドウ横幅サイズが w = 900 だった場合、
表題のように、データ配列には 900 という値は存在してません。
普通は必要となった時に逐次 [if~else] で以下のように書くかと思います。
if(w>=1024){return 0;}
else if(w>=800){return 1;}
else if(w>=600){return 2;}
else{return 3;}
※ 本題ではない上に、戻り値は同じなので
else if(1024>w && w>=800) のようにしてないのはわざとです。
なぜそれが必要なのか?
orz(w,x) のような感じで関数にサイズ値と範囲指定配列を渡すと
範囲インデックス値が戻るような関数を作りたい。
なぜなら、範囲インデックス値が判明すれば、
article タグ のメインボックスサイズ を b = [960,750,500,340]
nav タグ のメニューボックスサイズ を c = [250,250,200,150]
のように、ウインドウサイズによって変更する
要素のサイズ値を配列データで簡単に用意して使いやすいからです。
w = 900、つまり、ブラウザウインドサイズが 900 px だった場合、
インデックス番号は 1 なので、
article タグのサイズは b[1]=750
nav タグのサイズは c[1]=250
とすぐに値を指定できます。
いやいやそんなの、[if~else] で逐次書いとけばいいじゃん?(関東弁)
せやせや、わざわざ関数にする意味あるんかいな?(関西弁)
という突っ込みが聞こえてくるかもしれません。
『範囲インデックス値取得関数』の利点
さっきの x = [ 1024 , 800 , 600 ]
article タグ のメインボックスサイズ を b = [960,750,500,340]
nav タグ のメニューボックスサイズ を c = [250,250,200,150]
以外に、
縦幅の範囲 y = [ 768 , 600 , 480 ]
article タグ のメインボックス縦サイズ を d = ['auto','650px','400px','auto']
nav タグ のメニューボックス縦サイズ を e = ['100%','80%','50%','100%']
その他の横幅範囲 x2 = [ 700 , 400 ]
広告スペースの表示非表示指定 f=['block','inline-block','none']
別メニューbox の表示非表示指定 g=[0,0,1]
(※ 値や要素ネタは適当です。)
というものも
article タグ縦サイズ → d[orz(w,y)]
nav タグ縦サイズ → e[orz(w,y)]
広告スペース → f[orz(w,x2)] → styleのdisplayの値
別メニューbox → g[orz(w,x2)] → 0や1のonoffフラグで別の関数を動かす等
とすることができるので、ブラウザウインドウサイズによって変更したい
ものをほとんどすべてを配列データ化して指定できるようになります。
こういう範囲指定とその処理部分を [if~else] で書くと、
横幅メイン処理用の[if]グループ、
縦幅処理用の[if]グループ、
特別な横幅判定処理用の[if]グループ・・・
という感じで
後々修正しづらいようなスパゲッティなソースになってしまうのではないかと思ったりします。
また、1枚のHTML内の各要素に対する範囲指定が、
そのページで一律一個で全く同じ、とは限らないからです。
(上記の想定のように横幅・縦幅というカテゴリ違い以外で、
x2 のように調べたい範囲の値が違う場合もあります)
また、ブラウザウインドウサイズを細かく調節してチェックした時に、
例えば (w>=723) (w>=701) (w>=652) (w>=534) (w>=505) (w>=403) を足したい、
などが発生したら、
if 文による範囲指定が複数あれば、それらをプログラム記述に追加してゆくのが大変な作業となってしまいます。
さらに別のカテゴリ違いのページで
値が違うだけのものを似たような [if~else] 文で何度も何度も書くのが面倒すぎるため、
数値をデータ化し、配列で値指定できるようにしておくと楽ができる。
個別のデータ配列を渡せば後は同じ関数で値違いだけの同じ処理をする
(要素のサイズを指定値に変更する)という風に作れます。
そんな目的のためのインデックス値取得関数なのです。
(後述しますが、発展させた活用方法もあります)
範囲インデックス値取得関数のアルゴリズム
通常は『範囲インデックス値取得関数』内で [if~else] で数値比較してインデックス番号を計算して返す、
という作り方をするかもしれませんが、もっと簡単な方法があります。
範囲インデックス値取得関数のアルゴリズムは以下になります。
『最初に配列を indexOf で調べた時に、その配列に w と同じ値があれば、それをそのまま返し、
なければ、配列に w の値を追加し、降順(又は昇順)に並び替え、
再度 indexOf すれば、必ず w の値があるので、
範囲を示すインデックス値として取得することができる』
範囲インデックス値取得プログラム例(降順版)
function orz(w,r){
var c=r.indexOf(w),d=[];
if(c===-1){
d=r.concat(w);
d.sort(function(a,b){return b-a;});
c=d.indexOf(w);
}
return c;
}
※昇順 sortの定型処理
array.sort(function(a,b){return a-b;});
※降順 sortの定型処理
array.sort(function(a,b){return b-a;});
動作例
x = [ 1024 , 800 , 600 ] w = 900 の値で頭の中で動作チェックしてみます。
[1] indexOf(w)した戻り値を c に入れます。
c = [ 1024 , 800 , 600 ].indexOf(900);
x を渡した引数の配列 r の中に 900 という値はないので、戻り値は -1 です。
c に -1 が代入されます。
if(c===-1)なので以下の [2]~[4] の処理を行います。
[2] 関数内で作業用に用意した配列 d に、配列 r (中身は 配列 x ) に 900 を追加した新しい配列を入れます。
d = [ 1024 , 800 , 600 , 900 ]
となります。
[3] 降順に並び替えます。(降順 sortの定型処理)
d = [ 1024 , 900 , 800 , 600 ]
となります。
[4] もう一度indexOf(w)すると・・・
w = 900 を追加したので、当然インデックス値を返します。
d=[ 1024 , 900 , 800 , 600 ]
の d.indexOf(900) の戻り値は 1 です。
つまり、w=900 はインデックス番号 1
w が他の値だった場合を考えてみます。
w = 1024 の場合は最初の c が 0
w = 800 の場合は最初の c に 1
w = 600 の場合は最初の c に 2
w = 500 の場合
上記の[1]~[4]の動作で d=[ 1024 , 800 , 600 , 500 ]
となるので、2回目の indexOf で 3
以上のように、想定している戻り値の通りの値が戻ってきます。
1024以上は インデックス番号が 0
1024未満800以上は インデックス番号が 1
800未満600以上は インデックス番号が 2
600未満は インデックス番号が 3
レスポンシブサイトって「media screen」使うんじゃないの?
いまさらの項目ですが、この点についての見解は以下になります。
PCなのかスマホなのかタブレットなのか…という基本的な画面サイズを指定する、
という点では「media screen」で指定しておくかと思いますが、
要素の並びや配置、微妙なサイズ調節をcssだけで完璧にしようと思ったら
大量のcssファイルを作成することになり、
中身の修正等の管理が大変になってしまうかと思います。
なので、javascriptを切った状態で最低限表示の保障をするための「media screen」としておき、
それと併用する形で、
idを付加した要素に対して、
ブラウザサイズごとに横幅縦幅や配置の微調節を細かく変更する処理を
javasctiptでやる方が楽ではないかと思います。
そしてその javascript のプログラム中で if ~ else を大量に書くよりも
配列で値を指定する方が(作る時も修正する時も)楽ができると思ったりします。
追加する場合も
a = [ 1024 , 800 , 600 ]
↓
a = [ 1024 , 800 , 723 , 701 , 652 , 600 , 534 , 505 , 403 ]
b = [960,750,500,340]
↓
b = [960,750,684,641,602,500,340,330,300,250]
c = [250,250,200,150]
↓
c = [250,250,250,240,230,200,150,145,140,135]
のように増えた値に対応するサイズ値を増やすだけで済みます。
※ 数値は適当です。
数字だけの配列なんて、見てるだけで頭痛くなるやん
どの値が対応してるのか、全然わからへんやんか~
と関西弁で突っ込んでしまいそうな場合は
エクセルの表にして、
csvのカンマ区切りで出力するだけで
簡単にサイズ用の配列値を用意出来るかと思います。
範囲インデックス値取得関数の活用例
例えば、HTMLのdataset属性を使って、
ウインドウサイズによってサイズ変更したい要素のサイズ値を
HTMLファイル側で指定させるような
汎用サイズ変更処理関数などを作る時も重宝します。
要素タグにidを付け、
<div id="test1" data-s="sce_on"
data-h="1024,800,600" data-w="960,750,500,340">記事文章</div>
のような属性を書いて、
javascript 関数側で
『data-s="sce_on"』のようなスイッチを発見させて、
それがある要素の id から、[ data-* ]のアトリビュート値を取得させます。
『data-h="1024,800,600"』から取得した
文字列 '1024,800,600' は split を使って
文字列のカンマ区切りで配列データ化などの処理で範囲配列を作成します。
もちろん、HTMLの属性から取得した数値は文字列なので、
文字化している数字を parseInt しておきます。
a = [ 1024 , 800 , 600 ]
『data-w="960,750,500,340"』から取得した
文字列 '960,750,500,340' も同様にサイズ変更値配列を作成します。
b = [960,750,500,340]
後は汎用サイズ変更処理関数内で、
document.getElementById(id名).style.width= b[orz(w,a)] +'px';
とするなど、好きなように作る事が出来るかと思います。
ということで『範囲インデックス値取得』関数のようなものがあれば、
範囲や値を HTML 側で dataset 属性にカンマ区切りの文字列化した形でデータとして指定する、
それを配列化して値のインデックスを取得する、
という使い方もできます。
例えば自分のサイトでは、HTML 側でデータを指定する汎用サイズ変更処理関数以外に、
ウインドウサイズによって、画像の表示サイズ( width を変更)も
同様に HTML 側で指定する関数を作ってテストしたりしています。
参考:原森情報技術研究所 / 原森サイトの技ネタ紹介
※ 上記ページのブラウザのウインドウサイズの横幅を変更すると、あるサイズ以下は表示される画像が小さくなります。
そんなふうに、HTML側でデータを指定して、
そのページだけ特定要素のサイズを微調節させるような機能の関数は
if ~ else 判定文でサイズチェックをしている状態では
かなりめんどくさいプログラムになるかと思います。
(なぜなら、その時の範囲指定の数値は変更可能な変数データではないからです)
結局、レスポンシブサイトをいろいろ作っているうちに、
特定の数値でウインドウサイズから足し引き計算の形にするか、
指定値として配列で値を用意することにたどり着くのではないか、
などと思います。
今回紹介したのはその配列データ化で活用するための関数、ということになります。
まとめ
自分のサイトの記事で書いた配列活用の発想の元ネタは、
date.getDay() が曜日によって 0~6 の値を返し、
その値を配列 [ '日' , '月' , '火' , '水' , '木' , '金' , '土' ]
(又は文字列 '日月火水木金土'
)
のインデックス番号として使うようにできている
なぜなら日本語・英語・ロシア語などの違う言語でも
プログラム上では配列インデックスにする、という使い方は同じ。
という考察から配列を活用すれば、データを用意するのが楽になる。
配列値はインデックス番号を返す関数を用意すれば楽に値を指定できる。
そういう発想から『配列中にない値が範囲として含まれることを調べる方法』を
考えた時に思いついたアルゴリズムとして、
『最初に配列を indexOf で調べた時に、その配列に w と同じ値があれば、それをそのまま返し、なければ、配列に w の値を追加し、降順(又は昇順)に並び替え、再度 indexOf すれば、必ず w の値があるので、範囲を示すインデックス値として取得することができる』
というネタを今回紹介してみました。
アルゴリズムはどんなプログラムでも一緒ですので、
indexOf があるものならば同様に作れると思います。
よければぜひ、使ってみてください。(朝の料理人 もこみち風)
※デザイナーさんやフロントエンドエンジニアさん向けに
思いついたjavascriptネタを紹介している記事は
下記サイトに別の文章展開で書いてますのでよかったら、ご参照ください。