这是一个关于Ext, Prototype, JavaScript方面的问题,其实下面遇到的问题本不是问题,都是因为错误的理解造成的,本文的宗旨是希望读者朋友避免我犯的同类错误,遇事三思而后行,同时也体会下发现问题,解决问题,反思问题这种精神活动所带来的快乐!
测试代码
<script type="text/javascript">
function init(){
var tpl = new Ext.Template('<p id="p{id}">this is p{id}</p>');
tpl.append('p1',{id:'2'});
tpl.insertAfter('p2',{id:'3'});
tpl.overwrite('p1',{id:'88'});
}
Ext.onReady(init);
</script>
</head>
<body>
<p id="p1">this is p1</p>
</body>
로그인 후 복사
以下是在IE下的测试.我所期望的结果是(旁白:我已经开始犯错了):
this is div88
this is div2
this is div3
实际结果:
this is div88
问题:
Div2,div3 丢了?
发现问题怎么办?看代码.
Template.js line:197 (Extjs ver 2.2)
append : function(el, values, returnElement){
return this.doInsert('beforeEnd', el, values, returnElement);
}
在看 line201:
doInsert : function(where, el, values, returnEl){
el = Ext.getDom(el);
var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
return returnEl ? Ext.get(newNode, true) : newNode;
}
在在看:DomHelper.js line:267
insertHtml : function(where, el, html){
where = where.toLowerCase();
if(el.insertAdjacentHTML){
if(tableRe.test(el.tagName)){
var rs;
if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
return rs;
}
}
switch(where){
case "beforebegin":
el.insertAdjacentHTML('BeforeBegin', html);
return el.previousSibling;
case "afterbegin":
el.insertAdjacentHTML('AfterBegin', html);
return el.firstChild;
case "beforeend":
el.insertAdjacentHTML('BeforeEnd', html);
return el.lastChild;
case "afterend":
el.insertAdjacentHTML('AfterEnd', html);
return el.nextSibling;
}
throw 'Illegal insertion point -> "' + where + '"';
}
//////后面省略
}
原来还是用的insertAdjacentHTML方法,为什么会有问题呢?
输出中间代码:
var tpl = new Ext.Template('
this is div{id}
');
tpl.append('div1',{id:'2'});
tpl.insertAfter('div2',{id:'3'});
$("result-area").innerText = Ext.getDom("div1").innerHTML;
//.........
结果如下:
this is div1
this is div2
this is div3
?????? 为什么会这样? “this is div1”两边的
标签呢?
在测试:
var tpl = new Ext.Template('
this is div{id}
');
tpl.append('div1',{id:'2'});
tpl.insertAfter('div2',{id:'3'});
$("result-area").innerText = Ext.getDom("div1").outerHTML;
//.........
结果如下:
this is div1
this is div2
this is div3
(旁白:本来到这就已经能发现问题所在了,但执迷不悟,继续犯错)
原来它把div2,div3插到div1的value/text 后边了.所以运行tpl.overwrite('div1',{id:'88'});后div2,div3没了.
在此附上tpl.overwrite源码(innerHTML直接赋值):
overwrite : function(el, values, returnElement){
el = Ext.getDom(el);
el.innerHTML = this.applyTemplate(values);
return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
}
问题知道了,可怎么办呢,改Ext源码?
从做项目的角度来看,最好别改,因为你不清楚原作者的想法,容易把问题扩大化.
改源码不是不行,但那是没有办法的办法,如果能找到其它好的解决方案, 最好别改
(实际上Ext这块没有问题,只是我在参照API使用时,混淆了一些概念)
不改源码,只能找到其它好的解决方案了,用prototype.js看看:
var tpl = new Ext.Template('
this is div{id}
');
//tpl.append('div1',{id:'2'});
//tpl.insertAfter('div2',{id:'3'});
Insertion.After($('div1'),tpl.applyTemplate({id:'2'}));
Insertion.After($('div1'),tpl.applyTemplate({id:'3'}));
结果:
this is div88
this is div2
this is div3
문제 해결................................................
반영: 왜 ?
코드를 보세요 (내레이션: 문제가 점점 깊어집니다)
Prototype.js line:4042 (ver 1.6.0.2)
var Insertion = {
Before: function(element, content ) {
return Element.insert(element, {before:content});
},
Top: function(element, content) {
return Element.insert(element, {top:content) }) ;
},
하단: function(element, content) {
return Element.insert(element, {bottom:content})
},
이후: function(element) , content ) {
return Element.insert(element, {after:content})
}
}
다음을 살펴보세요: line:1616
insert: function(element, inserts ) {
요소 = $(요소)
................................. ..........
for(삽입의 var 위치) {
content = inserts[position];
position = position.toLowerCase()
insert = Element._insertionTranslations [위치]
..........................
요소 반환
}
다음 참조: line:2490
Element._insertionTranslations = {
before: function(element, node) {
element.parentNode.insertBefore(node, element)
},
top : function(element, node) {
element.insertBefore(node, element.firstChild)
},
bottom: function(element, node) {
element.appendChild(node)
},
after: function(element, node) {
element.parentNode.insertBefore(node, element.nextSibling)
},
.......... ...
}
차이점 보기:
Ext:
El.insertAdjacentHTML
프로토타입:
El.parentNode.insertBefore
Protoype는 El.parentNode.insertBefore를 사용합니다. 호환성 문제를 고려해보세요. Mozilla는 El.insertAdjacentHTML을 지원하지 않아서 Ext를 사용하기 어렵다고 생각하지 않으셨나요? (해설: 아이디어가 완전히 엉망이 되어 점점 손실의 나락으로 향하고 있습니다.)
코드 검토 : DomHelper.js 라인: 267
insertHtml: function(where, el, html){
. .....
switch(where){
case "beforebegin":
el .insertAdjacentHTML('BeforeBegin', html);
return el.previousSibling;
case "afterbegin":
el.insertAdjacentHTML('AfterBegin', html)
return el.firstChild; >case "beforeend":
el.insertAdjacentHTML('BeforeEnd', html);
return el .lastChild;
case "afterend":
el.insertAdjacentHTML('AfterEnd', html);
return el.nextSibling;
throw '잘못된 삽입 지점 -> '''
}
var range = el.ownerDocument.createRange(); >var frag;
switch(where){
case "beforebegin":
range.setStartBefore(el)
frag = range.createContextualFragment(html)
el.parentNode.insertBefore (frag, el);
return el.previousSibling;
case "afterbegin":
if(el.firstChild){
range.setStartBefore(el.firstChild)
frag = range .createContextualFragment(html);
el.insertBefore(frag, el.firstChild);
return el .firstChild;
}else{
el.innerHTML = html; ;
}
case "beforeend":
if(el.lastChild){
range.setStartAfter(el.lastChild)
frag = range.createContextualFragment(html); el.appendChild(frag);
return el.lastChild;
}else{
el .innerHTML = html;
return el.lastChild
}
case "afterend"
range.setStartAfter(el);
frag = range.createContextualFragment(html);
el.parentNode.insertBefore(frag, el.nextSibling);
return el.nextSibling;
throw 'Illegal insert point -> "' where '"'
}
두 번째 부분(두 번째 스위치 블록)을 보면 Ext도 고려되는 것을 알 수 있는데, IE라면 , 코드는 두 번째 부분으로 이동할 수 없습니다.
이제 케이스 분기와 이전 메서드 이름 간의 대응 관계를 나열합니다.
insertFirst:' afterBegin'
insertBefore:' beforeBegin'
insertAfter:' afterEnd '
append:' beforeEnd'
위 코드를 비교해 보면 문제는 추가, insertAfter를 혼동한 것 같습니다. 추가는 현재 노드 바로 뒤에 노드를 추가하는 것을 의미하고 insertAfter는 삽입을 의미하는 것으로 생각했습니다. 사실 이것을 이해하면, add와 insertAfter는 동일한 기능을 가지고 있습니다. 그러면 Ext의 저자는 What is this method?라는 두 가지를 썼습니다. 아아, 제가 멍청해서 분석하지 않고 무작위로 사용했습니다.
발췌: Ext.Template의 add 및 insertAfter 메소드에 대한 설명
제공된 값을 템플릿에 적용하고 새 노드를 el에 추가합니다
제공된 값을 템플릿에 적용하고 el
뒤에 새 노드를 삽입합니다. 팁: 그렇지 않은 경우 이해가 안 되시면 넣어주세요. 첫 번째 문장의 "to"를 "in"으로 이해하시면 더 명확하지 않을까요? 또한, 페이지 요소에 대한 작업을 할 경우에는 위의 삽입과 추가 기능이 있는 Element를 사용해주세요. .
너무 많은 노력이 잘못된 방향으로 사용되어 최종 결과는 0입니다!
아아,
세상에 아무것도 없고, 평범한 사람들이 방해할 뿐입니다.