Heim  >  Artikel  >  Web-Frontend  >  javascript 一段代码引发的思考_javascript技巧

javascript 一段代码引发的思考_javascript技巧

PHP中文网
PHP中文网Original
2016-05-16 18:56:44991Durchsuche

这是一个关于Ext, Prototype, JavaScript方面的问题,其实下面遇到的问题本不是问题,都是因为错误的理解造成的,本文的宗旨是希望读者朋友避免我犯的同类错误,遇事三思而后行,同时也体会下发现问题,解决问题,反思问题这种精神活动所带来的快乐!

测试代码

<script type="text/javascript"> 
function init(){ 
var tpl = new Ext.Template(&#39;<p id="p{id}">this is p{id}</p>&#39;); 
tpl.append(&#39;p1&#39;,{id:&#39;2&#39;}); 
tpl.insertAfter(&#39;p2&#39;,{id:&#39;3&#39;}); 
tpl.overwrite(&#39;p1&#39;,{id:&#39;88&#39;}); 
} 
Ext.onReady(init); 
</script> 
</head> 
<body> 
<p id="p1">this is p1</p> 
</body>

以下是在IE下的测试.我所期望的结果是(旁白:我已经开始犯错了): 
6dc214457e614d597f9d4d529debc81fthis is div8816b28748ea4df4d9c2150843fecfba68 
295e81a0ea4e7eed4f43b0669aad50c2this is div216b28748ea4df4d9c2150843fecfba68 
83ac4715e7087b135b455d34056492d6this is div316b28748ea4df4d9c2150843fecfba68 
实际结果: 
6dc214457e614d597f9d4d529debc81fthis is div8816b28748ea4df4d9c2150843fecfba68 
问题: 
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('d84829bf1326c239e3bf8396276f45e2this is div{id}16b28748ea4df4d9c2150843fecfba68'); 
tpl.append('div1',{id:'2'}); 
tpl.insertAfter('div2',{id:'3'}); 
$("result-area").innerText = Ext.getDom("div1").innerHTML; 
//......... 
结果如下: 
this is div1 
3054d5539162fa827ace69aa18d2ebcbthis is div2c997cec9f4b83324a7145373fde865ba 
f9596f12c25af0459e75dee8a46aa161this is div3c997cec9f4b83324a7145373fde865ba 
?????? 为什么会这样? “this is div1”两边的dc6dce4a544fdca2df29d5ac0ea9906b标签呢? 
在测试: 
var tpl = new Ext.Template('d84829bf1326c239e3bf8396276f45e2this is div{id}16b28748ea4df4d9c2150843fecfba68'); 
tpl.append('div1',{id:'2'}); 
tpl.insertAfter('div2',{id:'3'}); 
$("result-area").innerText = Ext.getDom("div1").outerHTML; 
//......... 
结果如下: 
b6301f87509127fbacc7431228a6286c 
this is div1 
3054d5539162fa827ace69aa18d2ebcbthis is div2c997cec9f4b83324a7145373fde865ba 
f9596f12c25af0459e75dee8a46aa161this is div3c997cec9f4b83324a7145373fde865ba 
c997cec9f4b83324a7145373fde865ba 
(旁白:本来到这就已经能发现问题所在了,但执迷不悟,继续犯错) 
原来它把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('d84829bf1326c239e3bf8396276f45e2this is div{id}16b28748ea4df4d9c2150843fecfba68'); 
//tpl.append('div1',{id:'2'}); 
//tpl.insertAfter('div2',{id:'3'}); 
Insertion.After($('div1'),tpl.applyTemplate({id:'2'})); 
Insertion.After($('div1'),tpl.applyTemplate({id:'3'})); 
结果: 
6dc214457e614d597f9d4d529debc81fthis is div8816b28748ea4df4d9c2150843fecfba68 
295e81a0ea4e7eed4f43b0669aad50c2this is div216b28748ea4df4d9c2150843fecfba68 
83ac4715e7087b135b455d34056492d6this is div316b28748ea4df4d9c2150843fecfba68 
问题解决了...................................... 
反思:为什么呢? 
看代码 (旁白:问题越引越深) 
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}); 
}, 
Bottom: function(element, content) { 
return Element.insert(element, {bottom:content}); 
}, 
After: function(element, content) { 
return Element.insert(element, {after:content}); 

}; 
接着看:line:1616 
insert: function(element, insertions) { 
element = $(element); 
............................... 
for (var position in insertions) { 
content = insertions[position]; 
position = position.toLowerCase(); 
insert = Element._insertionTranslations[position]; 
.............................. 
return element; 

在接着看: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 
Prototype: 
El.parentNode.insertBefore 
Protoype用El.parentNode.insertBefore是考虑兼容性问题, Mozilla不支持El. insertAdjacentHTML,难到Ext没考虑?( 旁白:思路已经完全乱了,逐渐走向迷途的深渊) 
在回顾下代码:DomHelper.js line: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 'Illegal insertion point -> "' + where + '"'; 

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; 
return el.firstChild; 

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 insertion point -> "' + where + '"'; 

从第二部分(第二个switch块)看的出来,Ext也考虑了,只是如果是ie的话,代码走不到第二部分. 
现在列出case分支与前面方法名的对应关系: 
insertFirst:' afterBegin' 
insertBefore:' beforeBegin' 
insertAfter:' afterEnd' 
append:' beforeEnd' 
对照上面的代码,现在看来,归根到底问题就是我混淆了append,insertAfter.以为append是指在当前节点后面直接追加一个节点, insertAfter是指把节点插到当前节点后面.实际上如果是这样理解的话append,insertAfter不就功能一样了,那Ext作者写两个方法干嘛?.,唉,自己脑残了,没仔细分析代码就乱用,结果引出这大长串事. 
摘录:Ext.Template中关于append,insertAfter方法的说明 
Applies the supplied values to the template and appends the new node(s) to el 
Applies the supplied values to the template and inserts the new node(s) after el 
提示:对于没看懂的朋友请把第一句的 to 理解成 in 是不是就清晰多了呢.另外,如果对页面元素操做的话请用Element,上面的insert,append功能它都有. 
在多的努力用在了错误的方向上,最终的结果都是零! 
唉, 
世间本无事, 庸人自扰之.

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn