2017年3月24日金曜日

getChildsを修正

「<button>開始</button>」を「obj.btn開始」のように参照できるようにするための、getChildsを先日公開しましたが、以下の問題がありました。

1.「obj.btn開始.removeNode(true)」のようにするとbtn開始のエレメントはリムーブされますが、エレメントへの参照用プロパティ「obj.btn開始」は残ってしまい、参照が残っているのでエレメントもメモリ上に居座り続けてしまう。
2.「tableメイン」のようなエレメントをリムーブした場合、そのエレメントに含まれる子孫エレメントへの参照も残ってしまう。

上記の問題を解消するためのメソッドを追加しました。

以下ソース
<html>
<title>getChilds</title>
<body>
<div id=div>
<div>
<div title=123>
<div title=456>
<span>789</span>
</div>
</div>
</div>
</div>
<div id=div0 style="border:solid 1px #000;"></div>
</body>
<script>
resizeTo(400,400)
onload = function(){
var obj=getChilds(div[FI]), d456=obj.removeNode('div456')
// getChildsを利用したエレメントやその子要素に対してremoveNodeしたい場合は
// getChildsが返したオブジェクトに紐づいているremoveNodeメソッドを利用する。
// エレメントに対して直接removeNodeを実行してしまうと、getChildsが返したオブジェクトについているプロパティからの参照が残ってしまう。
// removeNodeしたエレメントを使いまわすにしても参照先が不正という問題もあるので、removeNodeする場合はオブジェクトのメソッドを利用すること。
var objClone=obj.getClone()
div[iB](objClone.elem)
div[iB](d456.elem)
d456.span789[iT] = 'obj.div456:'+obj.div456+'\nobjClone.div456:'+objClone.div456
div0.innerText = div.innerHTML
}
iT='innerText', iH='innerHTML', PA='parentNode', FI='firstChild', CN='childNodes', NE='nextSibling', PR='previousSibling', sI='selectedIndex', iB='insertBefore', OP='options', LEN='length'
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}}}
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用)
if(obj){return obj}
if(E.src){return F('src', E.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 getKey=function(elem, topElem){
// topElemからelemに辿りつくためのFI, NEの配列を調べて返す。
var arr=[]
while(elem!=topElem && (elem[PR] || (elem[PA] && elem[PA].tagName))){
if(elem[PR]){
arr.unshift(NE); elem=elem[PR]
}else{
arr.unshift(FI); elem=elem[PA]
}
}
return arr
}
var getClone = function(){
// elemとobjChildsの関係性を丸ごと複製したものを作成して返す。
var objModel=this, elemClone=objModel.elem.cloneNode(true), objRet={elem:elemClone}
forIn(objModel, function(name, elem){
// 列挙されるプロパティのうち「elem」は無視する。functionは参照コピーする。
if(name=='elem'){return}
if(typeof(elem)=='function'){objRet[name]=objModel[name]; return}
var elemC=elemClone
for0L(getKey(elem, objModel.elem), function(i, str){ elemC=elemC[str] })
objRet[name] = elemC
})
return objRet
}
var removeNode = function(targetName){
// 指定されたnameに対応するエレメントをremoveNodeする。
// そのエレメントとそのエレメントに含まれている要素への参照用プロパティをまとめてdeleteする。
var obj=this, objRet={elem:obj[targetName].removeNode(true)}, arr不正=[]
forIn(obj, function(name,elem){
if(name==targetName){delete obj[name]; return}
if(typeof(elem)=='function'){objRet[name]=obj[name]; return}
while(elem[PR] || (elem[PA] && elem[PA].tagName)){
if(elem==obj.elem){return}
elem = elem[elem[PR] ? PR : PA]
}
if(elem!=objRet.elem){arr不正.push(name); return alert(elem.tagName)}
// ここまで継続したnameは参照解除の対象(removeNodeするエレメントの子要素)
objRet[name] = obj[name]
delete obj[name]
})
if(arr不正[LEN]){alert('以下が不正な参照状態になっています。\nエレメントの削除はgetChildsが返すobjectのremoveNodeメソッドを使用してください。\n\n'+arr不正.join('\n'))}
return objRet
}
return function(elem){
return用obj={elem:elem, getClone:getClone, removeNode:removeNode}
fun再帰(elem[CN])
return return用obj
}
}()
</script>
</html>
view raw getChilds.hta hosted with ❤ by GitHub



今回の修正のついでに「getClone」は「obj.getClone()」で「obj」のクローンを返してくれるように変更しました。

0 件のコメント:

コメントを投稿