拜讀《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的服務都帶著$符,自帶土豪氣啊,同學們一起加油!!!