http://hi.baidu.com/ui/scripts/global.js
地址在上面,这里就不贴了,随后讲到哪里的话会贴下片段。可能以后baidu会变更,但也应该很容易找到。
这篇主要是我一时兴起写下的,达人级就不必看了。主要为了加深下印象,另外给公司的前端部门那几位瞅瞅。
—
/* declare namespace */
(function(){
var namespaces = [ "System","BdElement", "BdBrowser", "BdEvent", "BdUtil", "BdAjax","BdString"];
for(var i = 0, j = namespaces.length; i < j; i ++){
if (window[ namespaces[ i ] ] != 'object')
window[ namespaces[ i ] ] = {};
}
})();
开头是baidu申明的命名空间,分为:System、Element、Browser、Event、Util、Ajax、BdString几个部分。其中Util我没细看,因为这玩意儿和网站耦合的比较紧,就懒得读了,其中还有个hi_tracker,猜想和百度hi相关。其它的东西倒是值得读一读的。
接下来的/* attach methods */很长,还是分块儿来吧~
/* BdBrowser scope */
var ua = navigator.userAgent.toLowerCase();
var isStrict = document.compatMode == "CSS1Compat",
isOpera = ua.indexOf("opera") > -1,
isSafari = (/webkit|khtml/).test(ua),
isSafari3 = isSafari && ua.indexOf('webkit/5') != -1,
isIE = !isOpera && ua.indexOf("msie") > -1,
isIE7 = !isOpera && ua.indexOf("msie 7") > -1,
isGecko = !isSafari && ua.indexOf("gecko") > -1,
isBorderBox = isIE && !isStrict,
isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
isAir = (ua.indexOf("adobeair") != -1),
isLinux = (ua.indexOf("linux") != -1),
isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
// remove css image flicker
if(isIE && !isIE7){
try{
document.execCommand("BackgroundImageCache", false, true);
}catch(e){}
}
var browsers = {
isOpera : isOpera,
isSafari : isSafari,
isSafari3 : isSafari3,
isSafari2 : isSafari && !isSafari3,
isIE : isIE,
isIE6 : isIE && !isIE7,
isIE7 : isIE7,
isGecko : isGecko,
isBorderBox : isBorderBox,
isLinux : isLinux,
isWindows : isWindows,
isMac : isMac,
isAir : isAir
};
for(var p in browsers){
BdBrowser[p] = browsers[p];
}
这里主要是处理针对不同浏览器的操作的。可能有人眼熟了:这不是Ext嘛!没错,对于js这种宿主环境很多的语言来说,相互学习借鉴本身就是一种提倡的方法。其实不止于此,其它的不也都是么?
上面这段主要讲判断浏览器以及https等放到BdBrowser下面。值得注意的是其中有个针对ie6的bug的修复——即ie6特殊情况下的图片闪烁bug,就是用document.execCommand(“BackgroundImageCache”, false, true);搞定的。
/* BdElement scope */
window.Bd$ = BdElement.check = function(id){
return typeof id == 'string' ? document.getElementById(id) : id;
}
BdElement.removeNode = isIE ? function(){
var d;
return function(node){
if(node && node.tagName != 'BODY'){
d = d || document.createElement('DIV');
d.appendChild(node);
d.innerHTML = '';
}
}
}() : function(node){
if(node && node.parentNode && node.tagName != 'BODY'){
node.parentNode.removeChild(node);
}
}
百度自己的getElementById和removeChild。前者实现为全局的Bd$,用jquery的用户可能很熟悉$(“#id”),就是这种形式;后者添加在BdElement上,方法为removeNode,和IE特有的方法同名。
由于IE的removeChild存在内存泄漏,所以现在基本上用的都是这种方法:将要移出的node放在一个div中,然后讲div的innerHTML清空。也有人提出outHTML等多种改进方案,但通用性稳定性等尚无法确保,所以主流方案还是这样。
/* BdEvent scope */
BdEvent.addEvent = function(el, fn, handler){
if(isIE){
el.attachEvent("on" + fn, handler);
}else{
el.addEventListener(fn, handler, false);
}
}
BdEvent.removeEvent = function(el, fn, handler){
if(isIE){
el.detachEvent("on" + fn, handler);
}else{
el.removeEventListener(fn, handler, false);
}
}
两个添加事件侦听方法,仅需区别是否IE即可,无需过多解释。
BdEvent.addDOMLoadEvent = (function(){
// create event function stack
var load_events = [],
load_timer,
script,
done,
exec,
old_onload,
init = function () {
done = true;
// kill the timer
clearInterval(load_timer);
// execute each function in the stack in the order they were added
while (exec = load_events.shift())
setTimeout(exec, 10);
if (script) script.onreadystatechange = '';
};
return function (func) {
// if the init function was already ran, just run this function now and stop
if (done) return func();
if (!load_events[0]) {
// for Mozilla/Opera9
if (document.addEventListener)
document.addEventListener("DOMContentLoaded", init, false);
// for Internet Explorer
/*@cc_on @*/
/*@if (@_win32)
document.write("<script id=__ie_onload defer src=//0><\/scr"+"ipt>");
script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
if (this.readyState == "complete")
init(); // call the onload handler
};
/*@end @*/
// for Safari
if (/WebKit/i.test(navigator.userAgent)) { // sniff
load_timer = setInterval(function() {
if (/loaded|complete/.test(document.readyState))
init(); // call the onload handler
}, 10);
}
// for other browsers set the window.onload, but also execute the old window.onload
old_onload = window.onload;
window.onload = function() {
init();
if (old_onload) old_onload();
};
}
load_events.push(func);
}
})();
addDOMLoadEvent是个值得细看的方法,其中主要的是“DOMContentLoaded”侦听,这个是大部分情况下替代window.onload的方案。由于window.onload是等候页面所有内容加载完成才执行,所以图片等大资源会造成严重的延迟问题,而DOMContentLoaded则是在DOM树建成之后出发,这才是我们想要的。可惜的是,ff、opera等浏览器对此标准都有了良好的支持,但是即使是IE8,也没看到丝毫“悔改”的念头,最后注意代码中注释掉的部分,这是以前曾经流行过的IE方法,原理是创建空的有defer属性的script标签,根据其onreadystatechange来判断是否DomReady。但这主要有2个缺点:https需要特殊判断;页面中有iframe时须得iframe加载完。
所以代码中用的方法是:dom标准浏览器用DOMContentLoaded,safari不断侦听document.readyState,其它浏览器继续用window.onload()。这虽然解决了不少问题,但并不完美。ie还是需要特殊待遇——doScroll。这里并没有doScroll,其主要原理是IE下只有当DOM树构建完成后才能doScroll,所以便可建立一个侦听,不断地地document.body.doScroll(‘left’);,成功的话说明DOM加载完毕。注意这句需要放在try里面,这样出错的话便可继续触发侦听,直到成功(页面DOM加载完)为止。
/* BdAjax scope */
BdAjax.getXHR = function(){
var xhr = null;
try{
return (xhr = new XMLHttpRequest());
}catch(e){}
for(var i = 0, a = ['MSXML3','MSXML2','Microsoft']; i < a.length; i ++){
try{
xhr = new ActiveXObject(a[i]+'.XMLHTTP');
break;
}catch(e){}
}
return xhr;
}
BdAjax创建xhr的工具,倒是省写了些代码,可以借来用用。不过似乎xhr的设计意图没让开发人员直接使用,而是下面的request。
BdAjax.request = function(url, json){
var xhr = this.getXHR();
if(!xhr){
throw new Error("cant't initialize xhr instance.");
}
var options={};
options.method = (json.method || 'get').toLowerCase();
options.asyn = true;
options.onSuccess = json.onSuccess || function(){};
options.onFailure = json.onFailure || function(){ new Image().src = "/sys/statlog/1.gif?m=ajax-request&v=" + encodeURIComponent(url) + "&t=" + new Date().getTime();};
options.postData = json.postData || null;
xhr.open(options.method, url, options.asyn);
if("post" == options.method.toLowerCase()){
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
}
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 0 || xhr.status == 200){
options.onSuccess(xhr);
}else{
options.onFailure(xhr);
}
}
}
xhr.send(options.postData);
}
request首先创建一个xhr,第一个参数url无须解释,第二个json倒是有点小复杂,这个名字容易使人误解,以为是传回的数据。其实是要ajax的一些参数,叫params倒是更好。不过从格式来看baidu的ajax请求默认就是json,所以有这个名字倒也不为怪。整体方法理解起来并不难,咱不多说了,这种风格倒是值得借鉴的。
BdAjax.loadJS=(function()
{
var head ;
return function(jsUrl){
head = head || document.getElementsByTagName("head")[0];
var s=document.createElement("script");
s.type="text/javascript";
s.src=jsUrl;
head.appendChild(s);
}
})();
loadJS,就是把script标签append到head上。我想不通多判断一下head有何意义……
BdString.trim=function(str)
{
return str.replace(/(^[\s\t\xa0\u3000]+)|([\u3000\xa0\s\t]+$)/g, "");
}
BdString.byteLength = function(str){
return str.replace(/[^\x00-\xFF]/g, "ly").length;
}
BdString.subByte = function(s, n){
if(this.byteLength(s)<=n)
return s;
for(var i=Math.floor((n=n-2)/2),l=s.length; i<l; i++)
if(this.byteLength(s.substr(0,i))>=n)
return s.substr(0,i) +"\u2026";
return s;
}
三个关于String的方法:trim去除两边空白;byteLength将双字节字符替换成两个英文字母再计算长度;subByte有点特殊,截取几个字节的字符串。奇特的是,它首先将最后增加的\u2026——也就是半个省略号算上,所以你要截取2个字节的字符串,只会返回\u2026,只有当截取的字符串大于2个时,它才会真正起作用。
另外今天遇到一个ie6的flash的bug,查了半天终于在别人的空间里找到了,真郁闷!
“FLASH在IE6下Error 2032错的处理
被这个问题折腾了2天。
Flash在IE6中POST数据时会出现ioerror Error #2032
这个错误是Gzip和no-cache同时出现造成的。
解决方法是在返回的页面里增加HTTP头,
Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate, max-age=-1");
总结:为什么昨天突然爆发呢?服务器没做任何修改,难道是MS搞的鬼???”

回复自“百度global.js读后感 & flash在ie6下的bug”
发表回复