今回はコードのラップについてのお話です。
少し前の「document.getElementByIdを毎回書くのを止めようかなって話」のほぼ続きです。
オリジナルラッパーを作るyo!
後でモジュールにしようとは思いますがいったんグローバルで。
はい、こちら「ビスクラッパー.js」です。
//bsc_wrapper.js
function wrapElement(element) { const hasProp = (prop) => prop in element; const wrapped = { get si() { return hasProp('selectedIndex') ? element.selectedIndex : undefined; }, set si(value) { if (hasProp('selectedIndex')) element.selectedIndex = value; }, get sl() { return hasProp('selected') ? element.selected : undefined; }, set sl(value) { if (hasProp('selected')) { element.selected = value; // <option> の selected 変更時に親 <select> の selectedIndex を更新 if (element.parentElement && element.parentElement.tagName.toLowerCase() === 'select') { const index = Array.from(element.parentElement.options).indexOf(element); if (value && index !== -1) { element.parentElement.selectedIndex = index; // 選択 } else if (!value && index === element.parentElement.selectedIndex) { element.parentElement.selectedIndex = -1; // 選択解除 } } } }, get ds() { return hasProp('disabled') ? element.disabled : undefined; }, set ds(value) { if (hasProp('disabled')) element.disabled = value; }, get ck() { return hasProp('checked') ? element.checked : undefined; }, set ck(value) { if (hasProp('checked')) element.checked = value; }, get vl() { return hasProp('value') ? element.value : undefined; }, set vl(value) { if (hasProp('value')) element.value = value; }, get ih() { return hasProp('innerHTML') ? element.innerHTML : undefined; }, set ih(value) { if (hasProp('innerHTML')) element.innerHTML = value; }, get cn() { return hasProp('className') ? element.className : undefined; }, set cn(value) { if (hasProp('className')) element.className = value; }, get tc() { return hasProp('textContent') ? element.textContent : undefined; }, set tc(value) { if (hasProp('textContent')) element.textContent = value; }, get hd() { return hasProp('hidden') ? element.hidden : undefined; }, set hd(value) { if (hasProp('hidden')) element.hidden = value; }, get ro() { return hasProp('readOnly') ? element.readOnly : undefined; }, set ro(value) { if (hasProp('readOnly')) element.readOnly = value; }, get rq() { return hasProp('required') ? element.required : undefined; }, set rq(value) { if (hasProp('required')) element.required = value; }, get ty() { return hasProp('type') ? element.type : undefined; }, set ty(value) { if (hasProp('type')) element.type = value; }, get nm() { return hasProp('name') ? element.name : undefined; }, set nm(value) { if (hasProp('name')) element.name = value; }, get id() { return hasProp('id') ? element.id : undefined; }, set id(value) { if (hasProp('id')) element.id = value; }, get st() { return { get bc() { return element.style.backgroundColor; }, set bc(value) { element.style.backgroundColor = value; }, get dp() { return element.style.display; }, set dp(value) { element.style.display = value; }, get co() { return element.style.color; }, set co(value) { element.style.color = value; }, get fs() { return element.style.fontSize; }, set fs(value) { element.style.fontSize = value; }, get wd() { return element.style.width; }, set wd(value) { element.style.width = value; }, get ht() { return element.style.height; }, set ht(value) { element.style.height = value; }, get mg() { return element.style.margin; }, set mg(value) { element.style.margin = value; }, get pd() { return element.style.padding; }, set pd(value) { element.style.padding = value; } }; }, element, getElement() { return element; }, on(type, listener) { element.addEventListener(type, listener); }, off(type, listener) { element.removeEventListener(type, listener); } }; return new Proxy(wrapped, { get(target, prop) { if (/^\d+$/.test(prop) && hasProp('options')) { const index = parseInt(prop); const option = element.options[index]; if (!option) { console.warn(`wrapElement: options[${index}] は存在しません`); return undefined; } return wrapElement(option); } if (prop in target) return target[prop]; const value = element[prop]; return typeof value === 'function' ? value.bind(element) : (hasProp(prop) ? value : undefined); }, set(target, prop, value) { if (prop in target) { target[prop] = value; return true; } if (hasProp(prop)) { element[prop] = value; } return true; } }); } function $i(id) { const element = document.getElementById(id); if (!element) return null; return wrapElement(element); } function $n(name) { const elements = document.getElementsByName(name); return Array.from(elements).map(element => wrapElement(element)); } function $$(query, { preferId = false } = {}) { const idElement = document.getElementById(query); const nameElements = Array.from(document.getElementsByName(query)); if (idElement && nameElements.length > 0) { console.warn(`idとnameに同じ名称あり: "${query}"`); if (preferId) return wrapElement(idElement); return null; } if (idElement) return wrapElement(idElement); if (nameElements.length > 0) return nameElements.map(element => wrapElement(element)); return null; } const $c = console.log.bind(console);
/* // グローバル互換性(オプション) window.$i = $i; window.$n = $n; window.$$ = $$; window.$c = $c; window.apc = apc; window.finishset0 = finishset0; //モジュールの場合(エクスポート) export { $i, $n, $$, $c }; */
こんな感じです。
前回は「const $ = (id) => document.getElementById(id);」で圧縮!!って話をしました。
これだけでもだいぶ平和になるんですが今回はそれを更に便利にした感じです。
例えば
function apc(){ var ea=getmaxessence() var ma=document.getElementById("material"); var msi=ma.selectedIndex var eq=document.getElementById("equipment");var esi=eq.selectedIndex var s1=document.getElementById("sp1"); var s1i=s1.selectedIndex var s2=document.getElementById("sp2"); var s2i=s2.selectedIndex var s3=document.getElementById("sp3"); var s3i=s3.selectedIndex var fi=document.getElementById("finish"); var fii=fi.selectedIndex var pmap=document.getElementsByName("pmap") var equp=document.getElementsByName("equp") var sp1p=document.getElementsByName("sp1p") var sp2p=document.getElementsByName("sp2p") var sp3p=document.getElementsByName("sp3p") var finp=document.getElementsByName("finp") var perp=document.getElementsByName("performance") var per2=document.getElementsByName("performance2") 上記を … function apc(){ var ea=getmaxessence() var ma=$i("material"); var msi=ma.si var eq=$i("equipment");var esi=eq.si var s1=$i("sp1"); var s1i=s1.si var s2=$i("sp2"); var s2i=s2.si var s3=$i("sp3"); var s3i=s3.si var fi=$i("finish"); var fii=fi.si var pmap=$n("pmap") var equp=$n("equp") var sp1p=$n("sp1p") var sp2p=$n("sp2p") var sp3p=$n("sp3p") var finp=$n("finp") var perp=$n("performance") var per2=$n("performance2")
$i ← document.getElementById
$n ← document.getElementsByName
どちらも二文字でアクセスできるようにラップしました。
varなのはDWが古くてletとか構文エラー入るので…直近のコードはだいたいletかconstです。
今回の目的「無くてもいいけどあったら楽」
jquery使ったことは無いんですがそちらより短くアクセス出来ると思います。勿論様々なラップがされてるjqueryには機能面では劣ります。
今回の目的としては通常のjavascriptの記述方式の流れを汲む事と短縮しても何のプロパティにアクセスしてるかプロパティ名を知ってれば分かる事。
このラッパーを使うのを止めた場合でもjavascriptの記述に戻れる(と思う)様に考えてます。
jqueryとかはメソッド方式らしく「obj.method()」みたいな感じで動かすらしいです。
ただその記述方法は独特なものになるので今回はオリジナルのプロパティを設定する形にしたかった事もあり、ゲッターとセッターを使ったラップにしてあります。拡張も容易です。
ちなみに、jqueryでメソッド方式の優位点はチェーンを作れるところ
「obj.method().プロパティ1().プロパティ2().プロパティ3()」みたいにする事で一度の記述で複数のプロパティを書き換えたり出来るそうです。
そういうオリジナルの形に進んじゃうと便利だけど普通の記述の構成忘れちゃいそうなので今回のラッパーはjqueryは違う方向性って事です。
ゲッターとセッター
今回実は初めて使いました。オブジェクトを作って
・引数無しならそのプロパティの値を取得=ゲッター
・引数を代入したらその値をセット=セッター
ざっくり言えばそんな感じです。
他にも一応方法はあって、生エレメントDOMを設定を直接書き換える方法もあるにはあります「Object.defineProperties(HTMLElement.prototype…」みたいな感じです。
直接的な挙動に短縮プロパティを設定する方法なのでこちらなら色々面倒なことを考えなくても良くなりますが危険度の方が高いと思います。まぁ使わなかった方法の話はいいでしょう。閑話休題。
前回の記事の方法であれば「const $i = (id) => document.getElementById(id);」
関数名を合わせるとこの様になり「$i("idname")」でアクセスできます。
今回の方法でも同様に「$i("idname")」でアクセスできますが戻ってくるものが違います。
let a=$i("idname")
こうした場合、前回と今回ではaに入るものが違います。
前回の方法は生エレメントを直接取得していますが今回はオブジェクトを取得しています。
つまり、前回の方法なら全てのメソッドやプロパティにアクセス出来ますが、オブジェクト側は設定しないと使えないのです。
最初にゲッターセッターを作ったあとに通常プロパティ名でアクセスしたら何も取得出来ませんでした
let a=$i("idname")
console.log(a.selectedIndex)
こう記述した時にaの中身は
前回:a=生エレメント
今回:a=オブジェクト
という事です。セッターゲッターに通常のプロパティと同じ名前のプロパティ名を設定すればどちらでも使えるように出来ますが全て列挙するのは現実的ではありません。
ではどうするかというとゲッターセッターに短縮プロパティを登録して、登録外のものは生エレメントに転送する方法です。
となると、生エレメントは常に持っておかなければならないという結論に。
さらに言えばオブジェクトから生エレメントを取り出せるようにしました。
「let b=$i(id).element」といった感じですね。
命名規則の話
基本的に二文字に圧縮しようと決めていました
・単語が二つあるものはその頭をとる(innerHTMLならih)
・母音頭2文字(di sa bledみたいに区切りってds)
・母音が無ければ先頭2文字(styleとかならst)
基本的にこのルールによってプロパティ名を決めました。
結局ラッパーって何?
簡単に言えばオリジナルオブジェクトです$i(id)は生エレメントをオリジナルオブジェクトで包み込んでそれにプロパティを生やしてるだけです。指定のプロパティでアクセスがきたらそれを内部のエレメントに投げて結果貰ったりDOMを書き換えたりするって事。
0 件のコメント:
コメントを投稿