拜讀《JavaScript高級程序設計》 --- 站在巨人的肩上
動態JS、動態樣式就是頁面加載時不存在,但將來通過DOM操作動態添加的腳本;包括加載外部文件和添加內部代碼塊兩種;動態加載的外部文件能夠立即運行,而動態添加的代碼塊卻不能如願的立即執行;下面將主要以JS為例,小小的探索下動態腳本的加載、執行以及jQuery的做法、Angularjs的相關做法;
1、動態加載外部文件
因為動態加載的外部JS會立即執行,也沒什麼浏覽器兼容性問題,這個好處理;這裡的重點是怎麼知道腳本已加載完成呢?呵呵,那麼浏覽器的兼容性問題就來咯!bsie!
//動態加載外部js文件
//params:url,loaded,error
var AsyncScripts=function() {
function load() {
var args=arguments;
var head = document.getElementsByTagName('head')[0];
var js = document.createElement('script');
js.type='text/javascript';
js.src=args[0];
head.appendChild(js);
if (document.all) {
js.onreadystatechange = function () {
if (js.readyState == 'loaded' || js.readyState == 'complete') {
args[1]&&args[1]();
}
}
} else {
js.onload = function () {
args[1]&&args[1]();
}
}
js.onerror=function(){
args[2]&&args[2]();
}
}
function execJSblock() {
}
return {
load:load,
exec:execJSblock
}
}();
AsyncScripts.load('http://cdn.famanoder.com/lib/jquery0.js',function() {
alert($)
});
2、立即執行動態加載的代碼塊
理論上講,script.appendChild(createTextNode(codeStr));能夠正常運行,在此不得不繼續bsie了,因為在IE中,<script>被視為一個特殊元素(<style>與其類似),不允許DOM訪問其子節點;不過,可以使用<script>的text屬性來指定代碼塊,讓其正常運行;
//params:scriptsStr
var AsyncScripts=function() {
function execJSblock(scriptsStr) {
var script=document.createElement('script');
script.type='text/javascript';
if (document.all) {
script.text=scriptsStr;
}else {
script.appendChild(document.createTextNode(scriptsStr));
}
document.body.appendChild(script);
}
return {
exec:execJSblock
}
}();
AsyncScripts.exec('var a=1000;alert(document.body.clientWidth+";"+a)');
//實際上該方法與eval(scriptsStr)是一樣的;
3、動態加載style
動態加載JS與動態加載style二者基本上是一樣的做法,存在基本一樣的問題;不一樣的是一個創建script,一個創建style/link;在IE上是script.text,而正常的做法是script.appendChild(document.createTextNode(scriptsStr));在IE上是style.stylesheet.cssText,正常的做法還是style.appendChild(document.createTextNode(stylesStr));還要注意link只在head裡有效,這個不能像script/style標簽可以在body裡;
動態加載JS、成功錯誤的回調、代碼塊立即執行,稍作整理,大概這樣:
var AsyncScripts=function() {
function load() {
var args=arguments;
var head = document.getElementsByTagName('head')[0];
var js = document.createElement('script');
js.type='text/javascript';
js.src=args[0];
head.appendChild(js);
if (document.all) {
js.onreadystatechange = function () {
if (js.readyState == 'loaded' || js.readyState == 'complete') {
args[1]&&args[1]();
}
}
} else {
js.onload = function () {
args[1]&&args[1]();
}
}
js.onerror=function(){
args[2]&&args[2]();
}
}
function execJSblock(scriptsStr) {
var script=document.createElement('script');
script.type='text/javascript';
if (document.all) {
script.text=scriptsStr;
}else {
script.appendChild(document.createTextNode(scriptsStr));
}
document.body.appendChild(script);
}
return {
load:load,
exec:execJSblock
}
}();
4、jQuery怎麼做的
在jQuery1.4.1及之前動態DOM操作添加的腳本是不會執行的,這個問題在1.4.2及以後的版本做了修復,跪拜我偉大的jQuery吧!繼續bsie!那麼就是說下面的代碼裡的script和style都會立即看到效果:
$(selector).append('<script>alert(1)</script>');
$(selector).html('<script>alert(1)</script>');
$(selector).append('<style>.a{color:red;}</style>');
5、Angularjs對動態添加的html及指令的處理
在Angular裡如果ng-bind='html string',Angular會把html string當做text對待,而不像上面的jQuery會幫你執行裡面的腳本;即使ng-bind-html='html string',也不會如你所願;是的,angular很強大的,這個問題它早想到了,$sce服務即是處理這個的,$sce翻譯過來就是“嚴格的上下文模式”;那麼可以先注入$sce服務,然後調用trustAsHtml(_html),通過ng-bind-html達到我們想要的結果;
['$scope','$sce',function($scope,$sce) {
$scope.trustHtml=function(_html) {
return $sce.trustAsHtml(_html);
};
}];
為了方便其他地方調用,我們可以把它封裝成一個過濾器:
app.filter('trustHTML', ['$sce', function ($sce) {
return function (_html) {
return $sce.trustAsHtml(_html);
};
}]);
剛學Angularjs還碰到過個問題:動態添加的指令無法運行;由於頁面加載後angular會從ng-app開始搜集指令然後編譯,所以動態添加的指令已經不在范圍內了,這時需要再次編譯指令,由$compile服務在link裡做這件事;
link:function($scope,$element,$attrs) {
angular.element(DOMobj).html(_html);
$compile(angular.element(DOMobj).contents())($scope);
};
6、叽叽歪歪完了
初學Angularjs,正在改造博客的後台程序,呵呵,繼續挖坑,慢慢填坑;
“高三”是本不錯的書,閒來多看看總有收獲;
有人說習慣了jQuery,要換個思維方式才能學好Angular;有人說寫Angular最好不要用jQuery的搞法,比如DOM操作;可也有人Angular與jQuery一起混用;
我感覺jQuery與Angular是兩種完全不同的思維方式下的產物,因為避免不了操作DOM,所以有人二者混用是可以理解的,但我看著總感覺別扭;我想以當一個項目適合Angular的思維來做,就應該摒棄腦子裡大量的DOM操作,試著以Angular的搞法來做,即便有DOM操作,angular也有對應的做法,這時jQuery可以歇歇了;看看Angular的服務都帶著$符,自帶土豪氣啊,同學們一起加油!!!