首页  >  文章  >  web前端  >  javascript 一段代码引发的思考_javascript技巧

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

PHP中文网
PHP中文网原创
2016-05-16 18:56:44962浏览

这是一个关于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功能它都有. 
在多的努力用在了错误的方向上,最终的结果都是零! 
唉, 
世间本无事, 庸人自扰之.

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn