2017年2月24日金曜日

getChildsで作成したオブジェクトからクローンを作成する関数

昨日公開したgetChildsが返すオブジェクトは「<div>メイン</div>」を「obj.divメイン」のように参照することができるようにするものでした。

必要に応じて複数回クローンを作成するようなモデルエレメントに対してgetChildsを実行した場合、getChildsの機能によってiTやvalueなどが変化するため、一度getChildsで処理したエレメントやその複製をgetChildsに渡すと、意図した参照用プロパティが得られないという問題がありました。

上記の問題は、本日公開するgetCloneという独自関数で解決できます。

以下ソース
<html>
<head>
<title>getChilds</title>
<style type=text/css>
.max{width:100%;height:100%}
.w-max{width:100%}
</style>
</head>
<body>
<table class=max>
<tr height=1>
<td>
<button>追加</button>
<button>オールクリア</button>
</td>
</tr>
<tr>
<td>
<div class=max style="overflow:auto;border:solid 1px #000;padding:10px;" title=メイン>
<table title=モデル border=1 class=w-max>
<td><textarea class=w-max></textarea></td>
<td width=1>
<button>削除</button>
</td>
</table>
</div>
</td>
</tr>
</table>
</body>
<script>
resizeTo(350,300)
CN='childNodes', iT='innerText', LEN='length', FI='firstChild', PR='previousSibling', PA='parentNode', NE='nextSibling', iB='insertBefore'
for0L=function(arr,fun){for(var i=0,L=arr[LEN],res;i<L;i++){if(res=fun(i,arr[i])){return res}}}
forIn=function(obj,fun){var name,res; for(name in obj){if(res=fun(name, obj[name])){return res}}}
isEqual=function(elemA, elemB){
// elemAとelemBが同一エレメントを参照しているならtrueを返す。それ以外はfalseを返す。
var d=document.createElement('div'), fun=this.isEqual=function(elemA, elemB){
elemA[iB](d.removeNode(true), elemA[FI])
var str=d[iT]=1111111, sw=false
while(!sw){
if(elemB[FI][iT]!=str){ break }
str=d[iT]=1234567
if(elemB[FI][iT]!=str){ break }
sw = true
}
d.removeNode(true)
return sw
}
return fun(elemA, elemB)
}
getChilds=function(){
var tagName2接頭辞={button:'btn', input:'inp', select:'sel', a:'lnk', iframe:'ifr', table:'tbl', textarea:'ta'}
var inp接頭辞={checkbox:'chk', radio:'rad', text:'inp', number:'num'}
var arr階層=[], arr階層_i=0
var get名前=function(){
var F=function(prop, v){return {prop:prop, v:v}}
var E, objNames={value:1, title:1, innerText:1}, forIn用=function(name){ return E[name] ? F(name, E[name]) : 0 }
return function(elem){
E = elem
var obj=forIn(objNames, forIn用), it=E[iT], src=E.src
if(obj){return obj}
if(src){return F('src', src.replace(/[\\\/]([^\\\/]+)$/)[1])}
}
}()
var return用obj, objクリア対象外={btn:1}
var for0L用=function(i, elem){
var name=(elem.tagName || '').toLowerCase()
if(!name){return}
arr階層[arr階層_i] = true
arr階層[++arr階層_i] = false
fun再帰(elem[CN])
arr階層_i--
// 例えば「<button>開始</button>」なら「btn開始」のような名前で参照できるようにする。
var 接頭辞 = ((name=='input' ? inp接頭辞[elem.type] : '') || tagName2接頭辞[name] || name)
var 名前 = get名前(elem)
// 名前があってもそれがiT由来で、かつ子要素を持つエレメントの場合は参照の対象外。
if(!名前 || 名前.prop!=iT || !arr階層[arr階層+1]){return用obj[接頭辞 + (名前 ? 名前.v : '')] = elem}
// 名前.vが「*」から始まる場合は「*」のみ削除する。それ以外の場合は名前.v=''とする。
if(名前 && !objクリア対象外[接頭辞] && (名前.prop!=iT || !arr階層[arr階層_i+1])){ elem[名前.prop] = 名前.v.charAt(0)=='*' ? 名前.v.replace(/^\*/,'') : '' }
arr階層.pop()
}
var fun再帰=function(cn){ for0L(cn,for0L用) }
var getChilds=function(elem){return用obj={elem:elem}; fun再帰(elem[CN]); return return用obj}
getChilds.getClone = function(objModel){
// elemとobjChildsの関係性を丸ごと複製したものを作成して返す。
var elemClone=objModel.elem.cloneNode(true), objRet={elem:elemClone}
forIn(objModel, function(name, elem){
if(name=='elem'){return}
var arr=[], elemC=elemClone
while(elem[PR] || (elem[PA] && elem[PA].tagName) ){
if(elem[PR]){
arr.unshift(NE); elem=elem[PR]
}else{
arr.unshift(FI); elem=elem[PA]
}
if(isEqual(elem, objModel.elem)){break}
}
for0L(arr, function(i, str){ elemC=elemC[str] })
objRet[name] = elemC
})
return objRet
}
return getChilds
}()
var obj=getChilds(document.body[FI]), objモデル=getChilds(obj.tblモデル.removeNode(true))
var 初期化=function(obj){
obj.btn削除.onclick = function(){ obj.elem.removeNode(true) }
obj.ta.value = (new Date()).toLocaleString()
}
with(obj){
btn追加.onclick=function(){
var newObj=getChilds.getClone(objモデル)
divメイン[iB](newObj.elem)
初期化(newObj)
}
btnオールクリア.onclick=function(){ divメイン[iT] = '' }
}
</script>
</html>




動作画面



これでクローン作製用モデルエレメントも名付けに悩まなくてOK!

0 件のコメント:

コメントを投稿